{"version":3,"file":"index.cjs","names":[],"sources":["../package/common/accordion/accordion.types.ts","../package/common/accordion/accordion.module.pcss","../package/common/configProvider/validLocale.ts","../package/common/configProvider/configProvider.tsx","../package/common/configProvider/useComponentDefaults.ts","../package/common/configProvider/useLabels.ts","../package/common/configProvider/usePresentationDefault.ts","../package/common/configProvider/useLocale.ts","../package/common/configProvider/presentation.types.ts","../package/common/utils/activationKey.ts","../package/common/themeProvider/color.types.ts","../package/common/utils/isDev.ts","../package/common/styles/tone.module.pcss","../package/common/styles/tone.ts","../package/common/accordion/accordion.tsx","../package/common/actionButtons/actionButtons.module.pcss","../package/common/button/button.module.pcss","../package/common/loader/loader.types.ts","../package/common/loader/loader.module.pcss","../package/common/loader/loader.tsx","../package/common/button/button.types.ts","../package/common/ripple/ripple.module.pcss","../package/common/utils/interactiveTarget.ts","../package/common/ripple/ripple.tsx","../package/common/utils/renderIcon.tsx","../package/common/hooks/useHotkeys.ts","../package/common/utils/navigation.ts","../package/common/button/button.tsx","../package/common/actionButtons/actionButtons.tsx","../package/common/actionIcon/actionIcon.module.pcss","../package/common/actionIcon/actionIcon.tsx","../package/common/scrollbarProvider/scrollbarProvider.module.pcss","../package/common/scrollbarProvider/scrollbarProvider.tsx","../package/common/scrollbarProvider/index.ts","../package/common/themeProvider/defaultTheme.ts","../package/common/themeProvider/themeVariablesContext.ts","../package/common/themeProvider/themeReveal.ts","../package/common/themeProvider/theme.module.pcss","../package/common/themeProvider/themeProvider.tsx","../package/common/themeProvider/lightTheme.ts","../package/common/bodyEnd/portalHostContext.ts","../package/common/bodyEnd/bodyEnd.tsx","../package/common/toast/toast.module.pcss","../package/common/hooks/useMediaQuery.ts","../package/common/hooks/useCoarsePointer.ts","../package/common/hooks/useSwipeDismiss.ts","../package/common/toast/toast.tsx","../package/common/toast/toastService.ts","../package/common/toast/toastHost.tsx","../package/common/ahroweProvider/ahroweProvider.tsx","../package/common/alert/alert.module.pcss","../package/common/alert/alert.tsx","../package/common/animatedLogo/animatedLogo.module.pcss","../package/common/animatedLogo/animatedLogo.tsx","../package/common/animatedText/animatedText.types.ts","../package/common/animatedText/animatedText.module.pcss","../package/common/animatedText/animatedText.tsx","../package/common/badge/badge.types.ts","../package/common/badge/badge.module.pcss","../package/common/badge/badge.tsx","../package/common/avatar/avatar.module.pcss","../package/common/avatar/avatar.tsx","../package/common/avatar/avatarGroup.tsx","../package/common/interactableDiv/interactableDiv.module.pcss","../package/common/interactableDiv/interactableDiv.tsx","../package/common/interactableDiv/index.ts","../package/common/popover/popover.module.pcss","../package/common/styles/sheet.module.pcss","../package/common/sheetBackdrop/sheetBackdrop.tsx","../package/common/utils/scrollAncestors.ts","../package/common/utils/visualViewport.ts","../package/common/hooks/useAnchorTracking.ts","../package/common/popover/popover.types.ts","../package/common/popover/usePopoverPosition.ts","../package/common/hooks/useFocusBoundary.ts","../package/common/hooks/useVisualViewportSize.ts","../package/common/hooks/useSheet.ts","../package/common/hooks/useOverlay.ts","../package/common/utils/dismissClick.ts","../package/common/popover/popover.tsx","../package/common/breadcrumb/breadcrumb.module.pcss","../package/common/breadcrumb/breadcrumb.tsx","../package/common/buttonGroup/buttonGroup.types.ts","../package/common/buttonGroup/buttonGroup.module.pcss","../package/common/buttonGroup/buttonGroup.tsx","../package/common/utils/mergeRefs.ts","../package/common/utils/day.ts","../package/common/calendar/calendar.types.ts","../package/common/calendar/dates.ts","../package/common/calendar/format.ts","../package/common/utils/weekStart.ts","../package/common/calendar/useNow.ts","../package/common/emptyState/emptyState.module.pcss","../package/common/emptyState/emptyState.tsx","../package/common/calendar/layout.ts","../package/common/calendar/components/agendaList/agendaList.module.pcss","../package/common/calendar/components/agendaList/agendaList.tsx","../package/common/optionPicker/optionPicker.module.pcss","../package/common/optionPicker/optionPicker.tsx","../package/common/calendar/components/calendarHeader/calendarHeader.module.pcss","../package/common/calendar/components/calendarHeader/calendarHeader.tsx","../package/common/calendar/gesture.ts","../package/common/calendar/useGestures.ts","../package/common/calendar/usePager.ts","../package/common/calendar/pager.module.pcss","../package/common/calendar/components/compactMonth/compactMonth.module.pcss","../package/common/calendar/components/compactMonth/compactMonth.tsx","../package/common/calendar/components/monthView/monthView.module.pcss","../package/common/calendar/components/monthView/monthView.tsx","../package/common/calendar/components/timeGrid/timeGrid.module.pcss","../package/common/calendar/components/timeGrid/timeGrid.tsx","../package/common/calendar/calendar.module.pcss","../package/common/calendar/calendar.tsx","../package/common/card/card.types.ts","../package/common/card/card.module.pcss","../package/common/card/Card.tsx","../package/common/card/CardHeader.tsx","../package/common/skeleton/skeleton.types.ts","../package/common/skeleton/skeleton.module.pcss","../package/common/skeleton/skeleton.tsx","../package/common/image/image.module.pcss","../package/common/image/image.tsx","../package/common/card/CardMedia.tsx","../package/common/card/CardContent.tsx","../package/common/card/CardActions.tsx","../package/common/carousel/carouselSlideContext.ts","../package/common/carousel/carousel.module.pcss","../package/common/styles/scrollFade.module.pcss","../package/common/carousel/carousel.tsx","../package/common/hooks/useAutoGrow.ts","../package/common/chatComposer/chatComposer.module.pcss","../package/common/chatComposer/chatComposer.tsx","../package/common/chatMessage/chatMessage.types.ts","../package/common/chatMessage/chatMessage.module.pcss","../package/common/chatMessage/chatMessage.tsx","../package/common/infiniteBlock/useInfiniteScroll.ts","../package/common/chatMessageList/chatMessageList.module.pcss","../package/common/chatMessageList/chatMessageList.tsx","../package/common/checkbox/checkbox.module.pcss","../package/common/checkbox/checkbox.tsx","../package/common/chip/chip.types.ts","../package/common/chip/chip.module.pcss","../package/common/chip/chip.tsx","../package/common/chip/chipContainer.tsx","../package/common/tooltip/tooltip.module.pcss","../package/common/tooltip/tooltip.tsx","../package/common/animatedIcon/animatedIcon.module.pcss","../package/common/animatedIcon/animatedIcon.tsx","../package/common/input/input.module.pcss","../package/common/input/input.types.ts","../package/common/input/input.tsx","../package/common/colorPicker/toHex.ts","../package/common/colorPicker/colorPicker.module.pcss","../package/common/colorPicker/colorPicker.tsx","../package/common/modal/modal.module.pcss","../package/common/hooks/useScrollEdges.ts","../package/common/modal/modal.tsx","../package/common/utils/listNavigation.ts","../package/common/menu/menu.types.ts","../package/common/menu/menuItem.tsx","../package/common/commandPalette/filterCommands.ts","../package/common/commandPalette/commandPalette.module.pcss","../package/common/commandPalette/commandPalette.tsx","../package/common/confirmModal/confirmModal.tsx","../package/common/hooks/useClipboard.ts","../package/common/copyButton/copyButton.module.pcss","../package/common/copyButton/copyButton.tsx","../package/common/datePicker/components/datePickerMonth/datePickerMonth.module.pcss","../package/common/datePicker/dateFunctions.ts","../package/common/datePicker/components/datePickerMonth/datePickerMonth.tsx","../package/common/dropdown/dropdown.module.pcss","../package/common/dropdown/dropdown.tsx","../package/common/maskedInput/mask.ts","../package/common/maskedInput/maskedInput.tsx","../package/common/datePicker/datePicker.module.pcss","../package/common/datePicker/datePicker.tsx","../package/common/divider/divider.types.ts","../package/common/divider/divider.module.pcss","../package/common/divider/divider.tsx","../package/common/drawer/drawer.module.pcss","../package/common/drawer/drawer.tsx","../package/common/dropZone/dropZone.types.ts","../package/common/dropZone/dropZone.module.pcss","../package/common/dropZone/dropZone.tsx","../package/common/errorBoundary/errorBoundary.module.pcss","../package/common/errorBoundary/errorBoundary.tsx","../package/common/fab/components/fabBase/fabBase.module.pcss","../package/common/fab/components/fabBase/fabBase.tsx","../package/common/fab/fab.module.pcss","../package/common/fab/fab.tsx","../package/common/flip/flip.module.pcss","../package/common/flip/flip.tsx","../package/common/iconPicker/iconPicker.module.pcss","../package/common/iconPicker/iconPicker.tsx","../package/common/idleManager/idleManager.tsx","../package/common/inputDropdown/inputDropdown.module.pcss","../package/common/inputDropdown/inputDropdown.tsx","../package/common/kanbanBoard/kanbanBoard.module.pcss","../package/common/kanbanBoard/KanbanItem.tsx","../package/common/kanbanBoard/KanbanColumn.tsx","../package/common/kanbanBoard/kanbanBoard.utils.ts","../package/common/kanbanBoard/KanbanBoard.tsx","../package/services/klipy.ts","../package/common/searchInput/searchInput.module.pcss","../package/common/searchInput/searchInput.tsx","../package/common/klipyPicker/components/gifPreview/gifPreview.module.pcss","../package/common/klipyPicker/components/gifPreview/gifPreview.tsx","../package/common/klipyPicker/components/gifView/gifView.module.pcss","../package/common/klipyPicker/components/gifView/gifView.tsx","../package/common/klipyPicker/klipyPicker.module.pcss","../package/common/klipyPicker/klipyPicker.tsx","../package/common/panZoom/panZoom.geometry.ts","../package/common/panZoom/panZoom.module.pcss","../package/common/panZoom/panZoom.tsx","../package/common/lightbox/lightbox.flight.ts","../package/common/lightbox/lightbox.module.pcss","../package/common/lightbox/lightbox.tsx","../package/common/listItem/listItem.module.pcss","../package/common/listItem/listItem.tsx","../package/common/loadingOverlay/loadingOverlay.module.pcss","../package/common/loadingOverlay/loadingOverlay.tsx","../package/common/menu/menu.module.pcss","../package/common/menu/menu.tsx","../package/common/menu/menuHotkeys.ts","../package/common/multiSelect/multiSelect.module.pcss","../package/common/multiSelect/multiSelect.tsx","../package/common/numberInput/numberInput.module.pcss","../package/common/numberInput/useStepRepeat.ts","../package/common/numberInput/numberInput.tsx","../package/common/numberInput/index.ts","../package/common/otpInput/otpInput.types.ts","../package/common/otpInput/otpInput.module.pcss","../package/common/otpInput/otpInput.tsx","../package/hooks/useWindowSize.ts","../package/common/overscroll/overscroll.module.pcss","../package/common/overscroll/overscroll.tsx","../package/common/pagination/pagination.module.pcss","../package/common/pagination/pagination.tsx","../package/common/progressBar/progressBar.module.pcss","../package/common/progressBar/progressBar.tsx","../package/common/radioGroup/radioGroup.types.ts","../package/common/radioGroup/radioGroup.module.pcss","../package/common/radioGroup/radioGroup.tsx","../package/common/rating/rating.module.pcss","../package/common/rating/rating.tsx","../package/common/revealLens/revealLens.module.pcss","../package/common/revealLens/revealLens.tsx","../package/common/planCanvas/planCanvas.module.pcss","../package/common/planCanvas/planCanvas.tsx","../package/common/roomDrawer/usePlanHistory.ts","../package/common/roomDrawer/roomDrawer.module.pcss","../package/common/roomDrawer/roomDrawer.tsx","../package/common/roomViewer/roomViewer.module.pcss","../package/common/roomViewer/roomViewer.tsx","../package/common/scrollFade/scrollFade.types.ts","../package/common/scrollFade/scrollFade.module.pcss","../package/common/scrollFade/scrollFade.tsx","../package/common/sectionHeader/sectionHeader.types.ts","../package/common/sectionHeader/sectionHeader.module.pcss","../package/common/sectionHeader/sectionHeader.tsx","../package/common/slider/slider.module.pcss","../package/common/slider/slider.tsx","../package/common/splitButton/splitButton.module.pcss","../package/common/splitButton/splitButton.tsx","../package/common/stepper/stepper.module.pcss","../package/common/stepper/stepper.types.ts","../package/common/stepper/stepper.tsx","../package/common/sticky/stickyStack.ts","../package/common/sticky/sticky.module.pcss","../package/common/sticky/sticky.tsx","../package/common/switch/switch.module.pcss","../package/common/switch/switch.tsx","../package/common/tabHeader/tabHeader.module.pcss","../package/common/tabHeader/tabHeader.tsx","../package/common/templateEditor/templateEditor.module.pcss","../package/common/templateEditor/templateEditor.utils.ts","../package/common/templateEditor/templateEditorSuggestions.tsx","../package/common/templateEditor/templateEditor.dom.ts","../package/common/templateEditor/templateEditable.tsx","../package/common/templateEditor/templateEditor.types.ts","../package/common/templateEditor/templateEditorPalette.tsx","../package/common/templateEditor/templateEditor.tsx","../package/common/textarea/textarea.tsx","../package/common/themeProvider/useTheme.ts","../package/common/tiltGroup/tiltGroup.tsx","../package/common/tilt/tiltMath.ts","../package/common/tilt/tilt.module.pcss","../package/common/tilt/tilt.tsx","../package/common/timeInput/timeFunctions.ts","../package/common/timeInput/components/clockDial/clockDial.module.pcss","../package/common/timeInput/components/clockDial/clockDial.tsx","../package/common/timeInput/timeInput.module.pcss","../package/common/timeInput/timeInput.tsx","../package/common/timeline/timeline.module.pcss","../package/common/timeline/timeline.types.ts","../package/common/timeline/timeline.tsx","../package/common/timer/timer.chime.ts","../package/common/timer/timer.module.pcss","../package/common/timer/timer.tsx","../package/common/virtualList/virtualList.module.pcss","../package/common/virtualList/virtualList.utils.ts","../package/common/virtualList/virtualRow.tsx","../package/common/virtualList/useHeaderNavigation.ts","../package/common/virtualList/virtualListHeader.tsx","../package/common/virtualList/scroller.ts","../package/common/virtualList/useVirtualWindow.ts","../package/common/virtualList/useSelection.ts","../package/common/virtualList/useColumns.ts","../package/common/virtualList/useRowDrag.ts","../package/common/virtualList/useSorting.ts","../package/common/virtualList/useColumnResize.ts","../package/common/virtualList/useRowNavigation.ts","../package/common/virtualList/virtualList.tsx","../package/common/tree/flattenTree.ts","../package/common/tree/tree.module.pcss","../package/common/tree/tree.tsx","../package/common/weatherIcon/weatherIcon.types.ts","../package/common/weatherIcon/weatherIcon.module.pcss","../package/common/weatherIcon/parts.tsx","../package/common/weatherIcon/weatherIcon.tsx","../package/common/wizard/wizard.module.pcss","../package/common/wizard/wizard.tsx","../package/services/formValidation/FormValidatorGroupWithForm.tsx","../package/services/formValidation/validatableComponent.tsx","../package/services/formValidation/useFormValidatorGroup.ts"],"sourcesContent":["import type { CSSProperties, ReactNode } from 'react';\nimport type { SlotClassNames, SlotStyles, HtmlProps } from '../types/slots.types';\nimport type { ColorValue } from '../themeProvider/color.types';\n\n/**\n * How much the header stands out, as on Button, Badge and Chip. No `Filled`: a section of content\n * should not compete with the page's main action.\n */\nexport enum AccordionStyleType {\n  /** The header in a light tint of the colour, its text in the colour. */\n  Light = 'light',\n  /** A border in the colour, the header's text in the colour. The default. */\n  Outlined = 'outlined',\n  /** The header's text in the colour, no chrome and no padding, for a disclosure inside other content. */\n  Plain = 'plain',\n}\n\nexport type AccordionSlots = 'root' | 'header' | 'title' | 'icon' | 'body' | 'bodyInner';\n\nexport interface AccordionProps extends HtmlProps {\n  title?: string;\n  children?: ReactNode;\n  expanded?: boolean | null;\n  onHeaderClicked?: (id: string, isExpanded: boolean) => void;\n  styleType?: AccordionStyleType;\n  /** A `ThemeColor` (default `Neutral`) or any CSS colour. */\n  color?: ColorValue;\n  /**\n   * Joins this accordion to the joined ones beside it into one block; otherwise neighbours keep a\n   * small gap. Default `false`.\n   */\n  joined?: boolean;\n  startOpen?: boolean;\n  hideHeader?: boolean;\n  className?: string;\n  style?: CSSProperties;\n  classNames?: SlotClassNames<AccordionSlots>;\n  styles?: SlotStyles<AccordionSlots>;\n}\n",".accordion {\n  color: var(--text-color);\n\n  /* Clips the header's fill and hover layer to the corners; the body clips anyway, for its height\n     transition. */\n  overflow: hidden;\n\n  /* Positioned, so a joined section paints over the one above, whose positioned header would\n     otherwise cover the seam where WebKit draws it thinner than the 1px overlap. */\n  position: relative;\n\n  &-header {\n    position: relative;\n    display: flex;\n    align-items: center;\n    gap: 0.75em;\n    padding: 0.75em 1em;\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n\n    /* The rest of the accordion measures from the header, so the base is set here. */\n    font-size: var(--default-font-size);\n\n    /* A layer in currentcolor, so it shows on a fill as on nothing: a Light header already uses\n       the background. */\n    &::before {\n      content: '';\n      position: absolute;\n      inset: 0;\n      background: currentcolor;\n      opacity: 0;\n      transition: opacity 0.15s;\n      pointer-events: none;\n    }\n\n    /* (hover: hover) because on touch a tap latches :hover until the next tap elsewhere. */\n    @media (hover: hover) {\n      &:hover::before {\n        opacity: 0.06;\n      }\n    }\n\n    &:focus-visible {\n      outline: 2px solid var(--focus-border-color, var(--primary-color));\n      outline-offset: -2px;\n    }\n  }\n\n  /* Positioned, so they paint above the hover layer, which is positioned too and comes first. */\n  &-title {\n    position: relative;\n    flex: 1;\n    min-width: 0;\n  }\n\n  &-icon {\n    /* FontAwesome's own rule is unlayered, so it outranks `width` from inside the library's layer and\n       reads this instead. `width` still sizes a custom icon. */\n    --fa-width: 1em;\n\n    position: relative;\n    flex-shrink: 0;\n    width: 1em;\n    height: 1em;\n    transition: transform 0.2s;\n\n    &-active {\n      transform: rotate(180deg);\n    }\n  }\n\n  &-body {\n    overflow: hidden;\n    position: relative;\n    transition: height 0.2s ease;\n\n    &-inner {\n      padding: 1em;\n    }\n  }\n\n  /* --accordion-seam is the line a joined section draws where it meets the one above. */\n  &-style {\n    &-light {\n      /* The page colour: the border is the tint itself, so a seam in it would merge two tinted\n         headers into one block. */\n      --accordion-seam: var(--background);\n\n      border: 1px solid var(--tone-light);\n      border-radius: var(--default-border-radius);\n    }\n\n    &-outlined {\n      --accordion-seam: var(--tone-outline);\n\n      border: 1px solid var(--tone-outline);\n      border-radius: var(--default-border-radius);\n    }\n  }\n\n  &-style-light &-header {\n    background: var(--tone-light);\n    color: var(--tone-text);\n  }\n\n  &-style-outlined &-header,\n  &-style-plain &-header {\n    color: var(--tone-text);\n  }\n\n  /* Plain sits inside other content: no padding, so no hover layer, and the focus ring goes\n     outside the text rather than over it. */\n  &-style-plain &-header {\n    padding: 0;\n\n    &::before {\n      display: none;\n    }\n\n    &:focus-visible {\n      outline-offset: 2px;\n    }\n  }\n\n  &-style-plain &-body-inner {\n    padding: 0.375em 0 0;\n  }\n\n  & + & {\n    margin-top: 0.5em;\n  }\n\n  /* Each joined section overlaps the one above by its 1px border, and that one hides (not removes)\n     its bottom border: a 1px gap or two borders snap to different device pixels at fractional zoom.\n     Ties `& + &` on specificity, so it stays after it. Firefox before 121 lacks :has(). */\n  &-joined + &-joined {\n    margin-top: -1px;\n    border-top-color: var(--accordion-seam);\n    border-top-left-radius: 0;\n    border-top-right-radius: 0;\n  }\n\n  &-style-plain&-joined + &-style-plain&-joined {\n    margin-top: 0;\n  }\n\n  &-joined:has(+ &-joined) {\n    border-bottom-color: transparent;\n    border-bottom-left-radius: 0;\n    border-bottom-right-radius: 0;\n  }\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .accordion-body,\n  .accordion-icon {\n    transition: none;\n  }\n}\n","const reported = new Set<string>();\n\n/**\n * `tag` as Intl writes it, or `undefined` (the browser's locale) for a tag Intl throws on, such as\n * `'de_AT'` or `''`: every formatter and collator below it would throw during render.\n */\nexport function validLocale(tag: string | undefined, source: string): string | undefined {\n  if (tag === undefined) return undefined;\n  try {\n    return Intl.getCanonicalLocales(tag)[0];\n  } catch {\n    if (!reported.has(tag)) {\n      reported.add(tag);\n      console.warn(\n        `${source}: \\`locale\\` ${JSON.stringify(tag)} is not a language tag, so the browser's locale is used. ` +\n          `Write it like 'de-AT'.`,\n      );\n    }\n    return undefined;\n  }\n}\n","import React, { createContext, useContext, useMemo } from 'react';\nimport type { ComponentDefaults, ConfigProviderProps } from './configProvider.types';\nimport type { Presentation } from '../configProvider/presentation.types';\nimport { validLocale } from './validLocale';\n\nexport const ConfigContext = createContext<ComponentDefaults | null>(null);\n\n/**\n * One setting for every component that can present as a bottom sheet, kept separate from\n * `defaultProps` so that turning sheets on (or off) app-wide is a single switch rather than one\n * entry per component key — the components that can do it are unrelated to each other, and a\n * consumer who missed one would get sheets in half their app.\n */\nexport const PresentationContext = createContext<Presentation | undefined>(undefined);\n\nexport const NavigateContext = createContext<ConfigProviderProps['navigate']>(undefined);\n\nexport const LocaleContext = createContext<string | undefined>(undefined);\n\n/**\n * Shallow-merge two default maps per component key, so nested ConfigProviders\n * compose: an inner provider's `Input` defaults extend (and override on\n * conflict) the outer provider's `Input` defaults rather than replacing them.\n */\nfunction mergeDefaults(parent: ComponentDefaults | null, own: ComponentDefaults | undefined): ComponentDefaults {\n  if (!parent) return own ?? {};\n  if (!own) return parent;\n\n  const merged: Record<string, object> = { ...parent };\n  for (const key in own) {\n    merged[key] = {\n      ...(parent as Record<string, object>)[key],\n      ...(own as Record<string, object>)[key],\n    };\n  }\n  return merged as ComponentDefaults;\n}\n\nfunction ConfigProvider({ children, defaultProps, presentation, navigate, locale }: ConfigProviderProps) {\n  const parent = useContext(ConfigContext);\n  const value = useMemo(() => mergeDefaults(parent, defaultProps), [parent, defaultProps]);\n\n  // A nested provider that doesn't set `presentation` keeps the outer one's, the same way\n  // `defaultProps` compose.\n  const inheritedPresentation = useContext(PresentationContext);\n  const presentationValue = presentation ?? inheritedPresentation;\n  const inheritedNavigate = useContext(NavigateContext);\n  const inheritedLocale = useContext(LocaleContext);\n\n  return (\n    <ConfigContext.Provider value={value}>\n      <PresentationContext.Provider value={presentationValue}>\n        <NavigateContext.Provider value={navigate ?? inheritedNavigate}>\n          <LocaleContext.Provider value={validLocale(locale, 'ConfigProvider') ?? inheritedLocale}>\n            {children}\n          </LocaleContext.Provider>\n        </NavigateContext.Provider>\n      </PresentationContext.Provider>\n    </ConfigContext.Provider>\n  );\n}\n\nexport default ConfigProvider;\n","import { useContext } from 'react';\nimport { ConfigContext } from './configProvider';\nimport type { ComponentDefaults } from './configProvider.types';\n\n/**\n * Merge global defaults from the nearest `ConfigProvider` **under** the props\n * the caller actually passed: an explicit prop always wins, a global default\n * fills in only props the caller left `undefined`, and the component's own\n * built-in default (its destructuring default) applies when neither is set.\n *\n * Call it at the very top of a component and destructure from the result\n * instead of from the raw props:\n *\n * ```tsx\n * function Input(props: InputProps) {\n *   const { alwaysFloatLabel = false, ... } = useComponentDefaults('Input', props);\n * }\n * ```\n */\nexport function useComponentDefaults<P extends object>(component: keyof ComponentDefaults, props: P): P {\n  const ctx = useContext(ConfigContext);\n  const defaults = ctx?.[component];\n  if (!defaults) return props;\n\n  const merged: Record<string, unknown> = { ...defaults };\n  for (const key in props) {\n    const value = (props as Record<string, unknown>)[key];\n    if (value !== undefined) merged[key] = value;\n  }\n  return merged as P;\n}\n","import { useContext } from 'react';\n\nimport { ConfigContext } from './configProvider';\nimport type { ComponentDefaults } from './configProvider.types';\n\n/**\n * Resolves the text a component renders of its own, per key, from three places:\n * the component's built-in English, the nearest `ConfigProvider`, and the\n * instance's `labels` prop, in that order of increasing precedence.\n *\n * Per key is the point, and the reason this exists next to `useComponentDefaults`\n * rather than relying on it. That one fills in whole props: a `labels` object on\n * an instance would replace the provider's entirely, so translating an app once\n * and overriding a single string on one instance would silently drop every other\n * translation for that component.\n *\n * ```tsx\n * const DEFAULT_LABELS: Required<ModalLabels> = { close: 'Close' };\n *\n * function Modal({ labels, ...rest }: ModalProps) {\n *   const text = useLabels('Modal', labels, DEFAULT_LABELS);\n *   // text.close\n * }\n * ```\n */\nexport function useLabels<L extends object>(\n  component: keyof ComponentDefaults,\n  labels: Partial<L> | undefined,\n  defaults: L,\n): L {\n  const ctx = useContext(ConfigContext);\n  const global = (ctx?.[component] as { labels?: Partial<L> } | undefined)?.labels;\n  // Not memoised on purpose: `defaults` and `labels` are object literals at every\n  // call site, so a dependency list would never hit and the bookkeeping would\n  // cost more than three spreads of a handful of strings.\n  return { ...defaults, ...global, ...labels };\n}\n","import { useContext } from 'react';\nimport { PresentationContext } from './configProvider';\nimport type { Presentation } from '../configProvider/presentation.types';\n\n/**\n * The nearest `ConfigProvider`'s app-wide `presentation`, or `undefined` when none is set.\n *\n * Sits between a component-keyed global default and the component's own built-in default, so the\n * full order is: explicit prop → `defaultProps.<Component>.presentation` → this → built-in.\n */\nexport function usePresentationDefault(): Presentation | undefined {\n  return useContext(PresentationContext);\n}\n","import { useContext } from 'react';\nimport { LocaleContext } from './configProvider';\n\n/**\n * The nearest `ConfigProvider`'s `locale`, or `undefined` when none is set, which `Intl` reads as\n * the browser's locale. Pass it straight to `Intl` so the app's own dates match the components'.\n */\nexport function useLocale(): string | undefined {\n  return useContext(LocaleContext);\n}\n","/**\n * How a surface is presented on screen. Lives here rather than with any one of\n * the components that read it: `Modal`, `Popover`, `Dropdown` and `MultiSelect`\n * all take it, and `ConfigProvider` owns the app-wide switch that sets it for\n * every one of them at once.\n */\nexport enum Presentation {\n  /** Wherever the component normally puts it: anchored to its trigger, or centred for a Modal. */\n  Default = 'default',\n  /** Pinned to the bottom of the screen, full width up to a maximum. */\n  Sheet = 'sheet',\n  /** A sheet on touch devices with a small screen, anchored everywhere else. */\n  Auto = 'auto',\n}\n","import type React from 'react';\n\n/**\n * Whether a keydown is a click-equivalent activation of the element the handler sits on.\n *\n * The target check is the important half: keydown bubbles, so without it a Space typed into a\n * nested input activates the container and, once the container calls `preventDefault()`, never\n * reaches the input at all.\n */\nexport function isSelfActivation(event: React.KeyboardEvent): boolean {\n  return event.target === event.currentTarget && (event.key === 'Enter' || event.key === ' ');\n}\n","/** The theme's named colours, shared by every component that takes a `color`. */\nexport enum ThemeColor {\n  /** `--primary-color`. */\n  Primary = 'primary',\n  /** The surrounding text colour, with `--border-color` for an outline. */\n  Neutral = 'neutral',\n  /** `--success-color`. */\n  Success = 'success',\n  /** `--info-color`. */\n  Info = 'info',\n  /** `--warn-color`. */\n  Warn = 'warn',\n  /** `--error-color`. */\n  Error = 'error',\n}\n\n/**\n * A `ThemeColor`, or any CSS colour such as `'var(--brand)'` or `'#7c3aed'`.\n * `string & {}` rather than `string`, which would swallow the enum into one plain string type.\n */\nexport type ColorValue = ThemeColor | (string & {});\n","// No `typeof process` guard: a browser has no `process` even where a bundler replaced\n// `process.env.NODE_ENV`, so the guard turned every warning off. Unbundled, the ReferenceError\n// means production. Declared here rather than globally, to stay out of the .d.ts.\ndeclare const process: { env: { NODE_ENV?: string } };\n\nexport function isDev(): boolean {\n  try {\n    return process.env.NODE_ENV !== 'production';\n  } catch {\n    return false;\n  }\n}\n","/* --tone comes from a colour class below, or inline (tone.ts) for any other colour. */\n.tone {\n  /* White wherever it reaches 3:1 (relative luminance below 0.3, as Mantine), else black at 7:1 or\n     more. Not WCAG 2's own crossover at 0.179, which puts black on mid-tone blues where white reads\n     far better. */\n  --tone-is-dark: clamp(0, (0.3 - (0.2126 * r + 0.7152 * g + 0.0722 * b)) * infinity, 1);\n  --tone-auto-contrast: color(from var(--tone) srgb-linear var(--tone-is-dark) var(--tone-is-dark) var(--tone-is-dark));\n  --tone-contrast: var(--tone-auto-contrast);\n\n  /* A third of the way to the text colour, so it reads (dark red on a dark theme), with the lost\n     saturation put back (x1.5) so a light theme is not muddy. Borders, dots and icons keep the\n     pure colour. */\n  --tone-text: oklch(from color-mix(in oklab, var(--tone) 67%, var(--text-color, currentcolor)) l calc(c * 1.5) h);\n  --tone-light: color-mix(in srgb, var(--tone) 22%, transparent);\n  --tone-fill: var(--tone);\n  --tone-outline: var(--tone);\n  --tone-gradient: none;\n}\n\n/* After .tone, whose defaults these override. Not the theme's solid -lighter pastels for\n   --tone-light: Light looked like two styles depending on the colour. */\n.primary {\n  --tone: var(--primary-color);\n  --tone-contrast: var(--text-on-primary, var(--tone-auto-contrast));\n  --tone-gradient: var(--primary-gradient, none);\n}\n\n/* currentcolor, so it takes the colour of what it sits on, such as a close button on an Alert. Not\n   --background-accent for the fill: in a light theme it stood out less than the tint. */\n.neutral {\n  --tone: currentcolor;\n  --tone-contrast: currentcolor;\n  --tone-text: currentcolor;\n  --tone-fill: color-mix(in srgb, currentcolor 20%, transparent);\n  --tone-light: color-mix(in srgb, currentcolor 8%, transparent);\n  --tone-outline: var(--border-color);\n}\n\n.success {\n  --tone: var(--success-color);\n}\n\n.info {\n  --tone: var(--info-color);\n}\n\n.warn {\n  --tone: var(--warn-color);\n}\n\n.error {\n  --tone: var(--error-color);\n  --tone-contrast: var(--text-on-delete, var(--tone-auto-contrast));\n}\n\n/* Browsers in the support floor without relative colour syntax (Chrome 119, Firefox 128). Tests\n   the formula itself, so a browser that parses `from` but not the rest falls back too. */\n@supports not\n((color: color(from red srgb-linear clamp(0, (0.3 - (0.2126 * r + 0.7152 * g + 0.0722 * b)) * infinity, 1) 0 0)) and\n  (color: oklch(from red l calc(c * 1.5) h))) {\n  .tone {\n    --tone-auto-contrast: var(--text-on-primary);\n    --tone-text: color-mix(in oklab, var(--tone) 67%, var(--text-color, currentcolor));\n  }\n}\n","import { useEffect, type CSSProperties } from 'react';\nimport cx from 'classnames';\n\nimport { ThemeColor, type ColorValue } from '../themeProvider/color.types';\nimport { isDev } from '../utils/isDev';\nimport styles from './tone.module.pcss';\n\n// A Map, so a CSS colour can never hit an Object.prototype key.\nconst CLASS_BY_COLOR = new Map<string, string>(\n  Object.entries({\n    primary: styles.primary,\n    neutral: styles.neutral,\n    success: styles.success,\n    info: styles.info,\n    warn: styles.warn,\n    error: styles.error,\n  } satisfies Record<ThemeColor, string>),\n);\n\n/** The class, and for a colour of the caller's own the inline style, that set the --tone properties. */\nexport function toneProps(color: ColorValue): { className: string; style?: CSSProperties } {\n  const named = CLASS_BY_COLOR.get(color);\n  if (named) return { className: cx(styles.tone, named) };\n  return { className: styles.tone, style: { '--tone': color } as CSSProperties };\n}\n\n// A Map, so a CSS colour can never hit an Object.prototype key.\nconst VAR_BY_COLOR = new Map<string, string>(\n  Object.entries({\n    primary: 'var(--primary-color)',\n    neutral: 'currentcolor',\n    success: 'var(--success-color)',\n    info: 'var(--info-color)',\n    warn: 'var(--warn-color)',\n    error: 'var(--error-color)',\n  } satisfies Record<ThemeColor, string>),\n);\n\n/** A colour as a CSS value, for a component that paints it itself rather than through --tone. */\nexport function cssColor(color: ColorValue): string {\n  return VAR_BY_COLOR.get(color) ?? color;\n}\n\n/** Warns in development about a value that can never be a colour. A var() passes: it only resolves on screen. */\nexport function useToneCheck(component: string, colors: (ColorValue | undefined)[]): void {\n  const key = colors.join('\\n');\n  useEffect(() => {\n    // jsdom before 30 has no CSS.supports, and a development check must not break a consumer's tests.\n    if (!isDev() || typeof globalThis.CSS?.supports !== 'function') return;\n    for (const color of key.split('\\n')) {\n      if (color && !CLASS_BY_COLOR.has(color) && !CSS.supports('color', color)) {\n        console.warn(`${component}: \\`color\\` takes a ThemeColor or a CSS colour, and got '${color}'.`);\n      }\n    }\n  }, [component, key]);\n}\n","import React, { useState, useEffect, useId, useRef, type ReactElement } from 'react';\nimport cx from 'classnames';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faChevronDown as arrowIcon } from '@fortawesome/free-solid-svg-icons';\n\nimport { AccordionStyleType, type AccordionProps } from './accordion.types';\n\nimport s from './accordion.module.pcss';\nimport { useComponentDefaults } from '../configProvider';\nimport { isSelfActivation } from '../utils/activationKey';\nimport { ThemeColor } from '../themeProvider/color.types';\nimport { toneProps, useToneCheck } from '../styles/tone';\n\nconst CLASS_BY_STYLE: Record<AccordionStyleType, string> = {\n  [AccordionStyleType.Light]: s.accordionStyleLight,\n  [AccordionStyleType.Outlined]: s.accordionStyleOutlined,\n  [AccordionStyleType.Plain]: s.accordionStylePlain,\n};\n\nconst NAVIGATION_KEYS = new Set(['ArrowDown', 'ArrowUp', 'Home', 'End']);\n\n/** Same parent only, so a list nested in a body is a set of its own. */\nfunction siblingHeaders(root: HTMLElement): HTMLElement[] {\n  return [...(root.parentElement?.children ?? [])].flatMap((sibling) => {\n    if (!sibling.classList.contains(s.accordion)) return [];\n    const header = sibling.querySelector<HTMLElement>(':scope > [role=\"button\"][aria-expanded]');\n    return header ? [header] : [];\n  });\n}\n\nfunction focusSiblingHeader(header: HTMLElement, key: string) {\n  const headers = header.parentElement ? siblingHeaders(header.parentElement) : [];\n  const index = headers.indexOf(header);\n  if (index < 0) return;\n  const last = headers.length - 1;\n  const next =\n    key === 'Home'\n      ? 0\n      : key === 'End'\n        ? last\n        : key === 'ArrowDown'\n          ? (index + 1) % headers.length\n          : (index - 1 + headers.length) % headers.length;\n  headers[next].focus();\n}\n\nfunction Accordion(props: AccordionProps): ReactElement {\n  const {\n    id = '',\n    title = '',\n    children = null,\n    expanded = null,\n    onHeaderClicked = () => {},\n    styleType = AccordionStyleType.Outlined,\n    color = ThemeColor.Neutral,\n    joined = false,\n    startOpen = false,\n    hideHeader = false,\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    ...rest\n  } = useComponentDefaults('Accordion', props);\n  // The header and the body name each other through aria-controls/aria-labelledby, so both need\n  // an id that is stable across renders and unique even with several accordions on the page.\n  const generatedId = useId();\n  const headerId = `${generatedId}-header`;\n  const bodyId = `${generatedId}-body`;\n  const bodyRef = useRef<HTMLDivElement>(null);\n  const bodyInnerRef = useRef<HTMLDivElement>(null);\n\n  const [isExpanded, setIsExpanded] = useState(expanded ?? startOpen);\n  const [bodyHeight, setBodyHeight] = useState<number | null>(null);\n\n  useEffect(() => {\n    // eslint-disable-next-line react-hooks/set-state-in-effect\n    if (expanded !== null) setIsExpanded(expanded);\n  }, [expanded]);\n\n  useEffect(() => {\n    const el = bodyRef.current;\n    if (el) el.inert = !isExpanded;\n  }, [isExpanded]);\n\n  useEffect(() => {\n    const el = bodyInnerRef.current;\n    if (!el) return;\n    const observer = new ResizeObserver(() => setBodyHeight(el.offsetHeight));\n    observer.observe(el);\n    return () => observer.unobserve(el);\n  }, []);\n\n  function toggleExpanded() {\n    const next = !isExpanded;\n    onHeaderClicked(id, next);\n    if (expanded === null) setIsExpanded(next);\n  }\n\n  const bodyStyle = { height: isExpanded ? (bodyHeight ?? 'auto') : '0px' };\n  const tone = toneProps(color);\n  useToneCheck('Accordion', [color]);\n\n  return (\n    <div\n      {...rest}\n      id={id || undefined}\n      className={cx(\n        s.accordion,\n        tone.className,\n        CLASS_BY_STYLE[styleType],\n        joined && s.accordionJoined,\n        className,\n        classNames?.root,\n      )}\n      style={{ ...tone.style, ...style, ...slotStyles?.root }}\n    >\n      {!hideHeader && (\n        <div\n          className={cx(s.accordionHeader, classNames?.header)}\n          style={slotStyles?.header}\n          onClick={toggleExpanded}\n          role='button'\n          tabIndex={0}\n          id={headerId}\n          aria-expanded={isExpanded}\n          aria-controls={bodyId}\n          onKeyDown={(event) => {\n            if (NAVIGATION_KEYS.has(event.key) && event.target === event.currentTarget) {\n              event.preventDefault();\n              focusSiblingHeader(event.currentTarget, event.key);\n              return;\n            }\n            if (!isSelfActivation(event)) return;\n            // Space scrolls the page otherwise, which is what a real <button> suppresses too.\n            event.preventDefault();\n            toggleExpanded();\n          }}\n        >\n          <div className={cx(s.accordionTitle, classNames?.title)} style={slotStyles?.title}>\n            {title}\n          </div>\n          <FontAwesomeIcon\n            icon={arrowIcon}\n            className={cx(s.accordionIcon, isExpanded && s.accordionIconActive, classNames?.icon)}\n            style={slotStyles?.icon as React.CSSProperties & Record<`--fa-font-${string}`, string>}\n          />\n        </div>\n      )}\n      <div\n        ref={bodyRef}\n        id={bodyId}\n        role='region'\n        aria-labelledby={hideHeader ? undefined : headerId}\n        className={cx(s.accordionBody, classNames?.body)}\n        style={{ ...bodyStyle, ...slotStyles?.body }}\n      >\n        <div\n          className={cx(s.accordionBodyInner, classNames?.bodyInner)}\n          style={slotStyles?.bodyInner}\n          ref={bodyInnerRef}\n        >\n          {children}\n        </div>\n      </div>\n    </div>\n  );\n}\n\nexport default Accordion;\n",".actionButtons {\n  display: flex;\n  gap: var(--spacing-m);\n\n  @media (--mq-mobile) {\n    flex-direction: column;\n  }\n\n  &-margin {\n    margin-top: var(--spacing-l);\n  }\n\n  &-stacked {\n    flex-direction: column;\n\n    & > * {\n      width: 100%;\n    }\n  }\n}\n",".button {\n  appearance: none;\n  -webkit-appearance: none;\n  box-sizing: border-box;\n  font-size: var(--default-font-size);\n  background: transparent;\n  border: 1px solid transparent;\n  padding: 0;\n  font-family: var(--default-font);\n  border-radius: var(--default-border-radius);\n  cursor: pointer;\n  -webkit-tap-highlight-color: transparent;\n  position: relative;\n  overflow: hidden;\n  min-width: min-content;\n\n  /* Hover and press layer in currentcolor, so one rule fits every style. On the root, which a\n     stretched button's inner element would not fill; under the Ripple wrapper, which comes later. */\n  &::before {\n    content: '';\n    position: absolute;\n    inset: 0;\n    background: currentcolor;\n    opacity: 0;\n    transition: opacity 0.15s;\n    pointer-events: none;\n  }\n\n  /* What a <button> gets from the browser and an <a> does not. Before the sizes, so Icon's\n     inline-flex wins the specificity tie. A grid, not inline-block: an inline-block that hides\n     its overflow takes its bottom edge as its baseline and sits higher than a <button> beside it.\n     align-content centres the label in a height set from outside, as a <button> does. */\n  &-link {\n    display: inline-grid;\n    align-content: center;\n    font-style: normal;\n    font-weight: normal;\n    letter-spacing: normal;\n    line-height: normal;\n    text-align: center;\n    text-decoration: none;\n    text-indent: 0;\n    text-shadow: none;\n    text-transform: none;\n    word-spacing: normal;\n  }\n\n  &-notAllowed {\n    cursor: not-allowed;\n  }\n\n  /* Disabled only, not loading: a faded spinner would read as disabled rather than working. */\n  &-disabled {\n    opacity: 0.5;\n  }\n\n  /* Inside the edge: outside, a ButtonGroup neighbour with the same fill swallows the ring. A\n     Filled button draws it in its text colour instead, as Pagination's active item does. */\n  &:focus-visible {\n    outline: 2px solid var(--focus-border-color, var(--primary-color));\n    outline-offset: -4px;\n  }\n\n  /* (hover: hover) because on touch a tap latches :hover until the next tap elsewhere. */\n  @media (hover: hover) {\n    &:hover:not(:disabled, [aria-disabled='true'])::before {\n      opacity: 0.08;\n    }\n  }\n\n  /* After the hover rule, which it ties on specificity: a press is a hover too. */\n  &:active:not(:disabled, [aria-disabled='true'])::before {\n    opacity: 0.16;\n  }\n\n  &-content {\n    position: relative;\n    white-space: break-spaces;\n  }\n\n  &-loading {\n    visibility: hidden;\n\n    &-container {\n      position: absolute;\n      width: 100%;\n      height: 100%;\n      display: flex;\n      align-items: center;\n      justify-content: center;\n    }\n  }\n\n  &-withIcon {\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    gap: 0.5em;\n  }\n\n  /* A pill, because plain text right after the label reads as part of it. On the label's\n     baseline rather than centred, so it follows the label wherever the font puts it. */\n  &-shortcut {\n    /* Not inline: Chrome trims only block containers, see the @supports below. */\n    display: inline-block;\n\n    /* Negative margins cancel the vertical padding in layout, so the button is no taller. */\n    margin: -0.2em 0 -0.2em 0.6em;\n    padding: 0.2em 0.6em;\n    border-radius: 99999px;\n    background: color-mix(in srgb, currentcolor 15%, transparent);\n    font-size: 0.85em;\n\n    /* Smaller text on a shared baseline sits lower. Raised so its capitals centre on the label's:\n       (1 - 0.85) / 2 x ~0.72 cap height / 0.85. */\n    vertical-align: 0.065em;\n    white-space: nowrap;\n\n    /* Even padding around the letters. Untrimmed, engines round the font's ascent and descent\n       differently at each size, and the letters sat up to 2px off-centre. */\n    @supports (text-box: trim-both cap alphabetic) {\n      margin-block: -0.4em;\n      padding-block: 0.4em;\n      text-box: trim-both cap alphabetic;\n    }\n\n    /* A touch screen usually has no keyboard to press it on. */\n    @media (pointer: coarse) {\n      display: none;\n    }\n  }\n\n  &-style {\n    &-filled {\n      background: var(--tone-fill);\n\n      /* Must follow the shorthand above, which resets background-image. */\n      background-image: var(--tone-gradient);\n      color: var(--tone-contrast);\n\n      /* After the base :focus-visible rule, which it ties on specificity. */\n      &:focus-visible {\n        outline-color: currentcolor;\n      }\n    }\n\n    &-light {\n      background: var(--tone-light);\n      color: var(--tone-text);\n    }\n\n    &-outlined {\n      color: var(--tone-text);\n      border-color: var(--tone-outline);\n    }\n\n    &-plain {\n      color: var(--tone-text);\n    }\n  }\n\n  &-background {\n    display: block;\n    padding: 0.5em 0.75em;\n  }\n\n  /* Multiples of the theme's base, not em: an em on font-size resolves against the parent,\n     so these would follow whatever the button sits in rather than the theme. */\n  &-size {\n    &-small {\n      font-size: calc(var(--default-font-size) * 0.75);\n    }\n\n    &-medium {\n      font-size: var(--default-font-size);\n    }\n\n    &-large {\n      font-size: calc(var(--default-font-size) * 1.25);\n    }\n\n    /* Square, padding-free button for a lone icon; the caller sets the box size. */\n    &-icon {\n      display: inline-flex;\n\n      /* Inherits, unlike the other sizes: a hardcoded px value would have to be out-specified\n         from another stylesheet, a specificity tie decided only by bundle order. */\n      font-size: inherit;\n\n      .button-ripple,\n      .button-background,\n      .button-content {\n        display: flex;\n        flex: 1;\n        align-items: center;\n        justify-content: center;\n        padding: 0;\n      }\n    }\n  }\n}\n","import type { CSSProperties } from 'react';\nimport type { HtmlProps } from '../types/slots.types';\nimport type { ColorValue } from '../themeProvider/color.types';\n\nexport enum LoaderType {\n  /** An arc that grows and shrinks on a faint track. The default, and the one that reads at any size. */\n  Spinner = 'spinner',\n  /** A glowing head trailing a fading tail round a ring. For a hero spot: a page, a panel, an overlay. */\n  Comet = 'comet',\n  /** Three dots in a wave. For a line of text or a chat reply being written. */\n  Dots = 'dots',\n  /** Bars rising and falling like an equaliser. For media, uploads and data being processed. */\n  Bars = 'bars',\n  /** Rings rippling out from a core. For waiting on something outside the app: a device, a connection, a search. */\n  Pulse = 'pulse',\n}\n\nexport interface LoaderLabels {\n  /** The loader's accessible name. */\n  loading?: string;\n}\n\nexport interface LoaderProps extends HtmlProps<HTMLSpanElement> {\n  /** The animation. Defaults to `LoaderType.Spinner`. */\n  type?: LoaderType;\n  /** Width and height: any CSS length, or a number of pixels. Defaults to `'2.5em'`, so it follows the font size around it. */\n  size?: number | string;\n  /** A `ThemeColor` or any CSS colour. Defaults to `ThemeColor.Primary`; `ThemeColor.Neutral` takes the colour of the text around it. */\n  color?: ColorValue;\n  /** Text the loader produces itself. See `LoaderLabels`. */\n  labels?: LoaderLabels;\n  className?: string;\n  style?: CSSProperties;\n}\n","/* No prefers-reduced-motion block: a loader that stands still says the app has hung. */\n.loader {\n  display: inline-block;\n  position: relative;\n  flex-shrink: 0;\n  width: var(--loader-size);\n  height: var(--loader-size);\n  vertical-align: middle;\n\n  /* A spinning box reaches past its corners. This keeps that out of a scroll container's overflow,\n     where it toggled a scrollbar every turn, and still paints it: `overflow: hidden` would cut off\n     the comet's glow. */\n  contain: layout;\n\n  &-spinner {\n    display: block;\n    width: 100%;\n    height: 100%;\n    overflow: visible;\n    animation: loaderSpin 1.4s linear infinite;\n\n    &-track,\n    &-arc {\n      fill: none;\n      stroke-width: 4.5;\n    }\n\n    &-track {\n      stroke: var(--tone-light);\n    }\n\n    &-arc {\n      stroke: var(--tone);\n      stroke-linecap: round;\n      stroke-dasharray: 1 150;\n      animation: loaderArc 1.4s ease-in-out infinite;\n    }\n  }\n\n  &-comet {\n    position: absolute;\n\n    /* The spinner's ring reaches 89% of the box; this keeps the two the same size. */\n    inset: 5%;\n    animation: loaderSpin 0.9s linear infinite;\n\n    &-tail {\n      position: absolute;\n      inset: 0;\n      border-radius: 50%;\n\n      /* Brightest just behind the head at 0deg, which covers the seam where it starts again. */\n      background: conic-gradient(transparent 25%, var(--tone));\n\n      /* The hole sits off centre towards the bottom right, so the ring is 12% thick at the head\n         and thins to under 5% along the tail. */\n      -webkit-mask-image: radial-gradient(41% 41% at 53% 53%, transparent calc(100% - 0.5px), #000 100%);\n      mask-image: radial-gradient(41% 41% at 53% 53%, transparent calc(100% - 0.5px), #000 100%);\n    }\n\n    &-head {\n      position: absolute;\n      top: 0;\n      left: 44%;\n      width: 12%;\n      height: 12%;\n      border-radius: 50%;\n      background: var(--tone);\n\n      /* A gradient rather than a box-shadow, whose blur cannot be a percentage of the size. */\n      &::before {\n        content: '';\n        position: absolute;\n        inset: -100%;\n        border-radius: 50%;\n        background: radial-gradient(closest-side, color-mix(in srgb, var(--tone) 60%, transparent), transparent);\n      }\n    }\n  }\n\n  /* In text, Dots rest on the baseline like an ellipsis: 0.32 is the resting dot's bottom edge in\n     loaderBounce's 0% frame. Must follow .loader's own vertical-align, as both are one class. */\n  &-on-baseline {\n    vertical-align: calc(var(--loader-size) * -0.32);\n  }\n\n  &-dots {\n    position: absolute;\n    inset: 0;\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n  }\n\n  &-dot {\n    width: 24%;\n    aspect-ratio: 1;\n    border-radius: 50%;\n    background: var(--tone);\n    animation: loaderBounce 1.2s ease-in-out infinite;\n\n    /* Negative delays, so every dot is already in flight on the first frame. */\n    &:nth-child(1) {\n      animation-delay: -0.3s;\n    }\n\n    &:nth-child(2) {\n      animation-delay: -0.15s;\n    }\n  }\n\n  &-bars {\n    position: absolute;\n    inset: 8% 4%;\n    display: flex;\n    justify-content: space-between;\n  }\n\n  /* A clip-path rather than height, which would lay the bars out again every frame, or scaleY,\n     which would squash the rounded ends flat. */\n  &-bar {\n    width: 12%;\n    background: var(--tone);\n    animation: loaderEqualise 1s ease-in-out infinite;\n\n    &:nth-child(1) {\n      animation-delay: -0.6s;\n    }\n\n    &:nth-child(2) {\n      animation-delay: -0.45s;\n    }\n\n    &:nth-child(3) {\n      animation-delay: -0.3s;\n    }\n\n    &:nth-child(4) {\n      animation-delay: -0.15s;\n    }\n  }\n\n  &-pulse {\n    position: absolute;\n    inset: 0;\n\n    &-ring {\n      position: absolute;\n      inset: 0;\n      border-radius: 50%;\n      background: var(--tone);\n      opacity: 0;\n      animation: loaderRipple 1.8s cubic-bezier(0.25, 0.6, 0.4, 1) infinite;\n\n      &:nth-child(2) {\n        animation-delay: -0.9s;\n      }\n    }\n\n    &-core {\n      position: absolute;\n      inset: 34%;\n      border-radius: 50%;\n      background: var(--tone);\n      animation: loaderBreathe 1.8s ease-in-out infinite;\n    }\n  }\n}\n\n/* No keyframe may share a name with a class's camelCase key (loaderDot for .loader-dot): CSS\n   Modules exports both under that key, and the class gets the keyframe's name. */\n@keyframes loaderSpin {\n  to {\n    transform: rotate(360deg);\n  }\n}\n\n/* r=20, so the circumference is 125.7: the arc grows to 90 and slides off the end. */\n@keyframes loaderArc {\n  0% {\n    stroke-dasharray: 1 150;\n    stroke-dashoffset: 0;\n  }\n\n  50% {\n    stroke-dasharray: 90 150;\n    stroke-dashoffset: -35;\n  }\n\n  100% {\n    stroke-dasharray: 90 150;\n    stroke-dashoffset: -124;\n  }\n}\n\n/* Resting dots stay plainly visible: faded, only the raised ones showed, and the dots read as\n   floating mid-line rather than on the baseline. */\n@keyframes loaderBounce {\n  0%,\n  30%,\n  100% {\n    transform: translateY(25%);\n    opacity: 0.55;\n  }\n\n  15% {\n    transform: translateY(-35%);\n    opacity: 1;\n  }\n}\n\n@keyframes loaderEqualise {\n  0%,\n  100% {\n    clip-path: inset(35% 0 round 999px);\n    opacity: 0.45;\n  }\n\n  50% {\n    clip-path: inset(0 0 round 999px);\n    opacity: 1;\n  }\n}\n\n@keyframes loaderRipple {\n  from {\n    transform: scale(0.3);\n    opacity: 0.7;\n  }\n\n  to {\n    transform: scale(1);\n    opacity: 0;\n  }\n}\n\n@keyframes loaderBreathe {\n  0%,\n  100% {\n    transform: scale(1);\n  }\n\n  50% {\n    transform: scale(0.8);\n  }\n}\n","import { useEffect, type CSSProperties, type ReactElement, type ReactNode } from 'react';\nimport cx from 'classnames';\n\nimport { LoaderType } from './loader.types';\nimport type { LoaderLabels, LoaderProps } from './loader.types';\nimport styles from './loader.module.pcss';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport { ThemeColor } from '../themeProvider/color.types';\nimport { toneProps, useToneCheck } from '../styles/tone';\nimport { isDev } from '../utils/isDev';\n\nconst DEFAULT_LABELS: Required<LoaderLabels> = {\n  loading: 'Loading',\n};\n\nfunction parts(count: number, className: string): ReactNode {\n  return Array.from({ length: count }, (_, index) => <span key={index} className={className} />);\n}\n\nfunction spinner(): ReactNode {\n  return (\n    <svg className={styles.loaderSpinner} viewBox='0 0 50 50'>\n      <circle className={styles.loaderSpinnerTrack} cx='25' cy='25' r='20' />\n      <circle className={styles.loaderSpinnerArc} cx='25' cy='25' r='20' />\n    </svg>\n  );\n}\n\n// A Map, so a type from untyped code or a JSON config can never hit an Object.prototype key.\nconst FIGURE_BY_TYPE = new Map<string, () => ReactNode>(\n  Object.entries({\n    [LoaderType.Spinner]: spinner,\n    [LoaderType.Comet]: () => (\n      <span className={styles.loaderComet}>\n        <span className={styles.loaderCometTail} />\n        <span className={styles.loaderCometHead} />\n      </span>\n    ),\n    [LoaderType.Dots]: () => <span className={styles.loaderDots}>{parts(3, styles.loaderDot)}</span>,\n    [LoaderType.Bars]: () => <span className={styles.loaderBars}>{parts(5, styles.loaderBar)}</span>,\n    [LoaderType.Pulse]: () => (\n      <span className={styles.loaderPulse}>\n        {parts(2, styles.loaderPulseRing)}\n        <span className={styles.loaderPulseCore} />\n      </span>\n    ),\n  } satisfies Record<LoaderType, () => ReactNode>),\n);\n\nfunction useTypeCheck(type: string): void {\n  useEffect(() => {\n    if (isDev() && !FIGURE_BY_TYPE.has(type)) {\n      console.warn(`Loader: \\`type\\` takes a LoaderType, and got '${type}'. Drawing the spinner instead.`);\n    }\n  }, [type]);\n}\n\nfunction Loader(props: LoaderProps): ReactElement {\n  const {\n    type = LoaderType.Spinner,\n    size = '2.5em',\n    color = ThemeColor.Primary,\n    labels,\n    className,\n    style,\n    ...rest\n  } = useComponentDefaults('Loader', props);\n  const text = useLabels('Loader', labels, DEFAULT_LABELS);\n  const tone = toneProps(color);\n  useToneCheck('Loader', [color]);\n  useTypeCheck(type);\n  const figure = FIGURE_BY_TYPE.get(type) ?? spinner;\n\n  return (\n    <span\n      {...rest}\n      role='progressbar'\n      aria-label={text.loading}\n      className={cx(styles.loader, type === LoaderType.Dots && styles.loaderOnBaseline, tone.className, className)}\n      style={\n        { '--loader-size': typeof size === 'number' ? `${size}px` : size, ...tone.style, ...style } as CSSProperties\n      }\n    >\n      {figure()}\n    </span>\n  );\n}\n\nexport default Loader;\n","import type React from 'react';\nimport type { CSSProperties } from 'react';\nimport type { IconContent } from '../types/icon.types';\nimport type { SlotClassNames, SlotStyles, DataProps } from '../types/slots.types';\nimport type { ColorValue } from '../themeProvider/color.types';\n\n/** How much the button stands out. Independent of its colour. */\nexport enum ButtonStyleType {\n  /** Solid fill, for the one main action. */\n  Filled = 'filled',\n  /** A light tint of the colour behind text in the colour. */\n  Light = 'light',\n  /** Border and text in the colour, no fill. The default. */\n  Outlined = 'outlined',\n  /** Text in the colour, no fill and no border. For toolbar, icon and close buttons. */\n  Plain = 'plain',\n}\n\nexport enum ButtonSize {\n  Small = 'small',\n  /** The default. */\n  Medium = 'medium',\n  Large = 'large',\n  /** Square, padding-free button for a lone icon. Give it its own width/height. */\n  Icon = 'icon',\n}\n\nexport enum ButtonType {\n  Button = 'button',\n  Reset = 'reset',\n  Submit = 'submit',\n}\n\nexport type ButtonSlots = 'background' | 'content' | 'loadingContainer' | 'label' | 'shortcut';\n\ninterface ButtonOwnProps extends DataProps {\n  className?: string;\n  style?: CSSProperties;\n  /** Emphasis: `Filled`, `Light`, `Outlined` (default) or `Plain`. */\n  styleType?: ButtonStyleType;\n  /** A `ThemeColor` (default `Primary`) or any CSS colour, such as `'var(--brand)'`. */\n  color?: ColorValue;\n  size?: ButtonSize;\n  children?: React.ReactNode;\n  icon?: IconContent;\n  /**\n   * A shortcut drawn after the label, in `useHotkeys` spelling (`'mod+s'`): `⌘S` on Apple\n   * platforms, `Ctrl+S` elsewhere. Drawn only; bind it with `useHotkeys`.\n   */\n  shortcut?: string;\n  isLoading?: boolean;\n  disabled?: boolean;\n  title?: string;\n  classNames?: SlotClassNames<ButtonSlots>;\n  styles?: SlotStyles<ButtonSlots>;\n}\n\n/**\n * A Button without `href`, a `<button>`. `DataProps` supplies the `data-*` index signature, which\n * `ButtonHTMLAttributes` doesn't cover. `type` and `onClick` narrow their native counterparts.\n */\nexport interface ButtonElementProps\n  extends\n    ButtonOwnProps,\n    Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, keyof ButtonOwnProps | 'type' | 'onClick'> {\n  href?: undefined;\n  type?: ButtonType;\n  onClick?: (event: React.MouseEvent<HTMLButtonElement>) => Promise<void> | void;\n  ref?: React.Ref<HTMLButtonElement>;\n}\n\n/** A Button with `href`, an `<a>`: `target`, `rel` and `download` are the native ones. */\nexport interface ButtonLinkProps\n  extends\n    ButtonOwnProps,\n    Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, keyof ButtonOwnProps | 'type' | 'onClick' | 'href'> {\n  /**\n   * Renders the button as a link, so a long press, Ctrl/⌘-click and a middle click work as on any\n   * other: `tel:`, `mailto:`, a maps URL, a page of the app. A plain click on a page of the app goes\n   * through `ConfigProvider`'s `navigate` when it is set.\n   */\n  href: string;\n  type?: undefined;\n  onClick?: (event: React.MouseEvent<HTMLAnchorElement>) => Promise<void> | void;\n  ref?: React.Ref<HTMLAnchorElement>;\n}\n\nexport type ButtonProps = ButtonElementProps | ButtonLinkProps;\n",".ripple {\n  background: rgb(255 255 255 / 20%);\n  border-radius: 1000px;\n  width: 100%;\n  aspect-ratio: 1;\n  position: absolute;\n  left: 0;\n  top: 0;\n  transform: translate(-50%, -50%) scale(0);\n  animation: ripple var(--ripple-animation-length) linear forwards;\n\n  &-container {\n    position: absolute;\n    width: 100%;\n    height: 100%;\n    top: 0;\n    left: 0;\n    border-radius: inherit;\n    user-select: none;\n    pointer-events: none;\n  }\n\n  @keyframes ripple {\n    to {\n      transform: translate(-50%, -50%) scale(4);\n      opacity: 0;\n    }\n  }\n}\n\n/* The ripple is nothing but the animation, and it starts at scale(0), so this leaves no ink at\n   all rather than a dot parked under the cursor. The element is removed on a timer either way. */\n@media (prefers-reduced-motion: reduce) {\n  .ripple {\n    animation: none;\n  }\n}\n","/** Elements that answer a pointer themselves, so a click on one is not a click on its container. */\nconst INTERACTIVE_SELECTOR = [\n  'a[href]',\n  'button',\n  'input',\n  'select',\n  'textarea',\n  'label',\n  '[contenteditable=\"\"]',\n  '[contenteditable=\"true\"]',\n  '[role=\"button\"]',\n  '[role=\"link\"]',\n  '[role=\"checkbox\"]',\n  '[role=\"combobox\"]',\n  '[role=\"menuitem\"]',\n  '[role=\"menuitemcheckbox\"]',\n  '[role=\"menuitemradio\"]',\n  '[role=\"option\"]',\n  '[role=\"radio\"]',\n  '[role=\"searchbox\"]',\n  '[role=\"slider\"]',\n  '[role=\"spinbutton\"]',\n  '[role=\"switch\"]',\n  '[role=\"tab\"]',\n  '[role=\"textbox\"]',\n  '[role=\"treeitem\"]',\n].join(', ');\n\n/**\n * Whether a click landed on a control of its own inside `container`, rather than on the container.\n *\n * `container` itself never counts, or one carrying `role=\"button\"` would exclude every click it is\n * meant to answer. `role=\"group\"` is absent for the same reason: it marks a wrapper, not a control.\n */\nexport function isInteractiveTarget(target: EventTarget | null, container: Element | null): boolean {\n  if (!container || !(target instanceof Element)) return false;\n  const hit = target.closest(INTERACTIVE_SELECTOR);\n  return !!hit && hit !== container && container.contains(hit);\n}\n","import React, { useRef, useEffect, useImperativeHandle } from 'react';\n\nimport styles from './ripple.module.pcss';\nimport { isInteractiveTarget } from '../utils/interactiveTarget';\nimport type { RippleProps, RippleHandle } from './ripple.types';\n\nexport type { RippleProps, RippleHandle };\n\nfunction Ripple({\n  disabled = false,\n  children,\n  className,\n  style,\n  overflowHidden = true,\n  rippleAnimationLength = 0.6,\n  rippleBackground = 'rgba(255, 255, 255, 0.2)',\n  manualOnly = false,\n  ref,\n  ...rest\n}: RippleProps) {\n  const containerRef = useRef<HTMLDivElement>(null);\n  const addRippleRef = useRef<((event: MouseEvent) => void) | null>(null);\n\n  function spawnRipple(x: number, y: number) {\n    if (disabled) return;\n    const ripple = document.createElement('div');\n    ripple.style.left = `${x}px`;\n    ripple.style.top = `${y}px`;\n    ripple.style.background = rippleBackground;\n    ripple.classList.add(styles.ripple);\n    containerRef.current?.appendChild(ripple);\n    setTimeout(\n      () => {\n        containerRef.current?.removeChild(ripple);\n      },\n      rippleAnimationLength * 1000 + 50,\n    );\n  }\n\n  function addRipple(event: MouseEvent | React.MouseEvent) {\n    if (!containerRef.current) return;\n    const pos = containerRef.current.getBoundingClientRect();\n    spawnRipple(event.clientX - pos.x, event.clientY - pos.y);\n  }\n\n  useImperativeHandle(ref, () => ({\n    addRipple() {\n      const el = containerRef.current;\n      if (!el) return;\n      spawnRipple(el.offsetWidth / 2, el.offsetHeight / 2);\n    },\n  }));\n\n  useEffect(() => {\n    addRippleRef.current = addRipple;\n  });\n\n  useEffect(() => {\n    if (children !== undefined || manualOnly) return;\n    const parent = containerRef.current?.parentElement;\n    if (!parent) return;\n    // A click on a control inside the rippling element belongs to that control: the ripple is\n    // feedback for activating this element, and the element it landed on gives its own.\n    const handler = (e: MouseEvent) => {\n      if (isInteractiveTarget(e.target, parent)) return;\n      addRippleRef.current?.(e);\n    };\n    parent.addEventListener('click', handler);\n    return () => parent.removeEventListener('click', handler);\n  }, [children, manualOnly]);\n\n  const containerStyle: React.CSSProperties & { '--ripple-animation-length'?: string } = {\n    '--ripple-animation-length': `${rippleAnimationLength}s`,\n  };\n\n  if (children !== undefined) {\n    return (\n      <div\n        {...rest}\n        className={className}\n        onClick={\n          manualOnly\n            ? undefined\n            : (e) => {\n                if (isInteractiveTarget(e.target, e.currentTarget)) return;\n                addRipple(e);\n              }\n        }\n        style={{\n          position: 'relative',\n          ...style,\n        }}\n      >\n        {children}\n        <div\n          className={styles.rippleContainer}\n          ref={containerRef}\n          style={{\n            overflow: overflowHidden ? 'hidden' : 'visible',\n            ...containerStyle,\n          }}\n        />\n      </div>\n    );\n  }\n\n  return (\n    <div\n      className={styles.rippleContainer}\n      ref={containerRef}\n      style={{\n        overflow: overflowHidden ? 'hidden' : 'visible',\n        ...containerStyle,\n      }}\n    />\n  );\n}\n\nexport default Ripple;\n","import type { CSSProperties, ReactNode } from 'react';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport type { IconDefinition } from '@fortawesome/fontawesome-svg-core';\n\nimport type { IconContent } from '../types/icon.types';\n\n/** FontAwesome's own `style` type — `CSSProperties` plus its `--fa-*` variables. */\ntype FaStyle = CSSProperties & Record<`--fa-font-${string}`, string>;\n\ninterface RenderIconOptions {\n  /** Class applied to the rendered icon (or to the wrapper span for a custom element). */\n  className?: string;\n  /** Inline style applied to the rendered icon (or to the wrapper span for a custom element). */\n  style?: CSSProperties;\n}\n\n/**\n * Narrows an `IconContent` to a FontAwesome `IconDefinition`.\n *\n * Both halves of the union are plain objects (a React element is one too), so\n * they can only be told apart structurally: an icon definition always carries\n * `prefix`, `iconName` and the `icon` tuple.\n */\nexport function isIconDefinition(icon: IconContent | null | undefined): icon is IconDefinition {\n  return typeof icon === 'object' && icon !== null && 'prefix' in icon && 'iconName' in icon && 'icon' in icon;\n}\n\n/**\n * Renders an `icon` prop that may be either a FontAwesome `IconDefinition` or an\n * arbitrary React element.\n *\n * A definition goes through `FontAwesomeIcon`; an element is returned untouched\n * unless a `className`/`style` was given, in which case it is wrapped in a\n * `<span>` carrying them so component and consumer slot styling still lands\n * somewhere. Note that a custom element is not sized by FontAwesome's `1em`\n * geometry — size it yourself if the surrounding layout expects a square icon.\n */\nexport function renderIcon(icon: IconContent | null | undefined, options: RenderIconOptions = {}): ReactNode {\n  const { className, style } = options;\n\n  if (icon === null || icon === undefined) return null;\n\n  if (isIconDefinition(icon)) {\n    return <FontAwesomeIcon icon={icon} className={className} style={style as FaStyle} />;\n  }\n\n  if (className || style) {\n    return (\n      <span className={className} style={style}>\n        {icon}\n      </span>\n    );\n  }\n\n  return icon;\n}\n","import { useEffect, useRef, type RefObject } from 'react';\n\nexport type HotkeyHandler = (event: KeyboardEvent) => void;\n\n/** Combo → handler, e.g. `{ 'mod+k': open, Escape: close }`. */\nexport type HotkeyMap = Record<string, HotkeyHandler>;\n\nexport interface UseHotkeysOptions {\n  /** Attach the listener at all. Default `true`; pass `false` to suspend every combo at once. */\n  enabled?: boolean;\n  /** Scope the shortcuts to one element instead of the whole window. */\n  target?: RefObject<HTMLElement | null>;\n  /**\n   * Let plain-key combos fire while a text field or `contenteditable` has focus. Default `false`,\n   * so `'n'` does not fire for every `n` typed into an input. Combos carrying Ctrl, Cmd or Alt\n   * are never suppressed — they cannot be confused with typing.\n   */\n  enableInFields?: boolean;\n  /**\n   * Call `preventDefault()` on a match, so `mod+s` does not also open the browser's save dialog.\n   * Default `true`.\n   */\n  preventDefault?: boolean;\n  /**\n   * Fire again for each repeat while the combo is held. Default `false`, so holding `mod+s` saves\n   * once. Turn it on for keys that step through something, such as arrows or zoom.\n   */\n  repeat?: boolean;\n}\n\ninterface Combo {\n  key: string;\n  ctrl: boolean;\n  meta: boolean;\n  alt: boolean;\n  shift: boolean;\n  /** Set when the combo named Shift itself — see `matches`. */\n  shiftNamed: boolean;\n}\n\nconst FIELD_SELECTOR = 'input, textarea, select, [contenteditable=\"\"], [contenteditable=\"true\"]';\n\n/** Spellings accepted for keys whose `event.key` is awkward to write inside a `+`-separated combo. */\nconst KEY_ALIASES: Record<string, string> = {\n  esc: 'escape',\n  space: ' ',\n  spacebar: ' ',\n  plus: '+',\n  up: 'arrowup',\n  down: 'arrowdown',\n  left: 'arrowleft',\n  right: 'arrowright',\n};\n\n/** `mod` is Cmd where Cmd is the command modifier, and Ctrl everywhere else. */\nfunction isApplePlatform(): boolean {\n  return typeof navigator !== 'undefined' && /Mac|iPhone|iPad|iPod/i.test(navigator.userAgent);\n}\n\nfunction parseCombo(combo: string): Combo | null {\n  const parts = combo\n    .split('+')\n    .map((part) => part.trim().toLowerCase())\n    .filter(Boolean);\n  // A trailing separator (`'mod+'`) loses its key to the filter above, so the whole combo is\n  // dropped rather than matching on the modifier alone. `'mod+plus'` is how to mean `+` itself.\n  if (parts.length === 0) return null;\n\n  const parsed: Combo = { key: '', ctrl: false, meta: false, alt: false, shift: false, shiftNamed: false };\n  const apple = isApplePlatform();\n\n  for (const part of parts) {\n    switch (part) {\n      case 'mod':\n        if (apple) parsed.meta = true;\n        else parsed.ctrl = true;\n        break;\n      case 'ctrl':\n      case 'control':\n        parsed.ctrl = true;\n        break;\n      case 'meta':\n      case 'cmd':\n      case 'command':\n        parsed.meta = true;\n        break;\n      case 'alt':\n      case 'option':\n        parsed.alt = true;\n        break;\n      case 'shift':\n        parsed.shift = true;\n        parsed.shiftNamed = true;\n        break;\n      default:\n        parsed.key = KEY_ALIASES[part] ?? part;\n    }\n  }\n\n  return parsed.key ? parsed : null;\n}\n\n/**\n * The key the event carries, plus the one its physical position would produce unshifted.\n *\n * Both are needed: Alt+K on a Mac arrives as `key: '˚'` and Shift+1 as `key: '!'`, so a combo\n * written against the letter or digit on the keycap only matches through `code`.\n */\nfunction pressedKeys(event: KeyboardEvent): string[] {\n  const keys = [event.key.toLowerCase()];\n  const physical = /^(?:Key|Digit)(.)$/.exec(event.code);\n  if (physical) keys.push(physical[1].toLowerCase());\n  return keys;\n}\n\nfunction matches(combo: Combo, event: KeyboardEvent): boolean {\n  if (combo.ctrl !== event.ctrlKey || combo.meta !== event.metaKey || combo.alt !== event.altKey) return false;\n  // Shift is only compared when the combo named it. A symbol is typed *with* Shift on most\n  // layouts, so requiring `shiftKey === false` would make `'?'` and `'mod+/'` unreachable.\n  if (combo.shiftNamed && combo.shift !== event.shiftKey) return false;\n  return pressedKeys(event).includes(combo.key);\n}\n\nfunction isInField(target: EventTarget | null): boolean {\n  return target instanceof Element && !!target.closest(FIELD_SELECTOR);\n}\n\n/** Keys a text field uses for itself, beyond the characters it inserts. */\nconst FIELD_KEYS = new Set([\n  'arrowup',\n  'arrowdown',\n  'arrowleft',\n  'arrowright',\n  'home',\n  'end',\n  'pageup',\n  'pagedown',\n  'backspace',\n  'delete',\n  'enter',\n  'tab',\n]);\n\n/**\n * Whether a combo would be taken out of a focused field's mouth.\n *\n * Ctrl, Cmd and Alt take a key out of the field's hands whatever it is. Without one, a single\n * character is typing and the listed keys are editing, but `F2` and `Escape` are neither, and\n * suppressing those made a menu's `F2` do nothing the moment the page had a focused input.\n */\nfunction isFieldKey(combo: Combo): boolean {\n  if (combo.ctrl || combo.meta || combo.alt) return false;\n  return combo.key.length === 1 || FIELD_KEYS.has(combo.key);\n}\n\n/** Keys whose name is not what a keycap says. Anything else is capitalised, so `f2` reads `F2`. */\nconst KEY_LABELS: Record<string, string> = {\n  ' ': 'Space',\n  arrowup: '↑',\n  arrowdown: '↓',\n  arrowleft: '←',\n  arrowright: '→',\n  escape: 'Esc',\n  enter: '↵',\n  backspace: '⌫',\n  delete: 'Del',\n  pageup: 'PgUp',\n  pagedown: 'PgDn',\n};\n\n/**\n * A combo written for the reader: `'mod+shift+k'` reads `⇧⌘K` on an Apple platform and\n * `Ctrl+Shift+K` everywhere else.\n *\n * So a shortcut is written once, in one spelling, and the hint beside it cannot contradict what\n * the binding actually listens for. Hand-written `'⌘K'` hints are wrong on every other platform,\n * silently, because nothing compares the label to the binding.\n *\n * Resolved from the platform at call time, so a server-rendered page renders the non-Apple form.\n */\nexport function formatHotkey(combo: string): string {\n  const parsed = parseCombo(combo);\n  if (!parsed) return combo;\n\n  const key = KEY_LABELS[parsed.key] ?? (parsed.key.length === 1 ? parsed.key.toUpperCase() : capitalise(parsed.key));\n\n  // Apple orders modifier symbols ⌃⌥⇧⌘ and runs them into the key; everywhere else they are\n  // words joined by `+`.\n  if (isApplePlatform()) {\n    const symbols = [parsed.ctrl && '⌃', parsed.alt && '⌥', parsed.shift && '⇧', parsed.meta && '⌘'];\n    return symbols.filter(Boolean).join('') + key;\n  }\n  const words = [parsed.ctrl && 'Ctrl', parsed.meta && 'Win', parsed.alt && 'Alt', parsed.shift && 'Shift'];\n  return [...words.filter(Boolean), key].join('+');\n}\n\nfunction capitalise(value: string): string {\n  return value[0].toUpperCase() + value.slice(1);\n}\n\n/** UI Events key names that are not the lowercase spelling capitalised. */\nconst ARIA_KEY_NAMES: Record<string, string> = {\n  // The attribute's syntax uses both characters as separators, so ARIA names them instead.\n  ' ': 'Space',\n  '+': 'Plus',\n  arrowup: 'ArrowUp',\n  arrowdown: 'ArrowDown',\n  arrowleft: 'ArrowLeft',\n  arrowright: 'ArrowRight',\n  pageup: 'PageUp',\n  pagedown: 'PageDown',\n};\n\n/**\n * A combo as `aria-keyshortcuts` spells it: `'mod+shift+k'` is `Control+Shift+K`, or\n * `Meta+Shift+K` on an Apple platform. Resolved from the platform like `formatHotkey`, so the two\n * always name the same keys.\n */\nexport function ariaKeyshortcuts(combo: string | undefined): string | undefined {\n  const parsed = combo ? parseCombo(combo) : null;\n  if (!parsed) return undefined;\n\n  const key = ARIA_KEY_NAMES[parsed.key] ?? capitalise(parsed.key);\n  const modifiers = [parsed.ctrl && 'Control', parsed.alt && 'Alt', parsed.shift && 'Shift', parsed.meta && 'Meta'];\n  return [...modifiers.filter(Boolean), key].join('+');\n}\n\n/**\n * Keyboard shortcuts, for a whole app or for one element.\n *\n * ```tsx\n * useHotkeys({ 'mod+k': () => setPaletteOpen(true), '?': () => setHelpOpen(true) });\n * ```\n *\n * The map is read through a ref at event time, so an inline object literal is fine and no handler\n * needs memoising. Shortcuts keep firing while a Modal or Drawer is open — pass `enabled` for the\n * ones that should not.\n */\nexport function useHotkeys(hotkeys: HotkeyMap, options: UseHotkeysOptions = {}): void {\n  const { enabled = true, target, enableInFields = false, preventDefault = true, repeat = false } = options;\n\n  const hotkeysRef = useRef(hotkeys);\n  useEffect(() => {\n    hotkeysRef.current = hotkeys;\n  });\n\n  useEffect(() => {\n    if (!enabled) return;\n    const node: HTMLElement | Window | null = target ? target.current : window;\n    if (!node) return;\n\n    function onKeyDown(event: Event) {\n      const keyboardEvent = event as KeyboardEvent;\n      // A key pressed to compose a character in an IME is not a shortcut.\n      if (keyboardEvent.isComposing) return;\n\n      for (const [spelling, handler] of Object.entries(hotkeysRef.current)) {\n        const combo = parseCombo(spelling);\n        if (!combo || !matches(combo, keyboardEvent)) continue;\n        if (isFieldKey(combo) && !enableInFields && isInField(keyboardEvent.target)) continue;\n        // Prevented on a skipped repeat too, or the second keydown of a held mod+s opens the\n        // browser's save dialog.\n        if (preventDefault) keyboardEvent.preventDefault();\n        if (keyboardEvent.repeat && !repeat) return;\n        handler(keyboardEvent);\n        return;\n      }\n    }\n\n    node.addEventListener('keydown', onKeyDown);\n    return () => node.removeEventListener('keydown', onKeyDown);\n  }, [enabled, target, enableInFields, preventDefault, repeat]);\n}\n\nexport default useHotkeys;\n","import { useContext, type MouseEvent } from 'react';\n\nimport { NavigateContext } from '../configProvider/configProvider';\n\ntype Navigate = (path: string) => void;\n\nfunction parseUrl(href: string): URL | null {\n  try {\n    return new URL(href, document.baseURI);\n  } catch {\n    return null;\n  }\n}\n\n/**\n * The path to hand to the router, or null where the browser has to take the URL: another site, or\n * another protocol. Protocol and origin are both checked because a `blob:` URL, which an app opens\n * a generated PDF through, carries the page's own origin. A jump to an anchor on the current page\n * stays with the browser too, which scrolls to it.\n */\nfunction routerPath(url: URL): string | null {\n  if (url.protocol !== 'http:' && url.protocol !== 'https:') return null;\n  if (url.origin !== window.location.origin) return null;\n  const samePage = url.pathname === window.location.pathname && url.search === window.location.search;\n  // Not `url.hash`, which is empty for a bare `#` that still jumps to the top.\n  if (samePage && url.href.includes('#')) return null;\n  return url.pathname + url.search + url.hash;\n}\n\n/** The checks react-router's own `<Link>` makes, plus `download`, which it leaves to the browser. */\nfunction isPlainClick(event: MouseEvent<HTMLAnchorElement>): boolean {\n  const anchor = event.currentTarget;\n  return (\n    !event.defaultPrevented &&\n    event.button === 0 &&\n    !(event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) &&\n    (!anchor.target || anchor.target === '_self') &&\n    !anchor.hasAttribute('download')\n  );\n}\n\n/**\n * The click handler for an `<a>` a component renders: a plain click on a link within the app goes\n * to `ConfigProvider`'s `navigate`, everything else to the browser. Call it after the consumer's own\n * `onClick`, so a `preventDefault()` there still wins.\n */\nexport function useLinkClick(): (event: MouseEvent<HTMLAnchorElement>) => void {\n  const navigate = useContext(NavigateContext);\n  return (event) => {\n    if (!navigate || !isPlainClick(event)) return;\n    // `.href` rather than the attribute: the browser has already resolved a relative link against\n    // the page, as it does for a middle click, which the router would resolve against the route.\n    const url = parseUrl(event.currentTarget.href);\n    const path = url && routerPath(url);\n    if (path === null) return;\n    event.preventDefault();\n    navigate(path);\n  };\n}\n\n/** Follows a link from code, where no anchor was clicked: through the router where it can. */\nexport function followHref(href: string, navigate?: Navigate): void {\n  const url = parseUrl(href);\n  if (!url) return;\n  // React refuses a javascript: URL on an <a>, so the same link must not run one here. Parsed,\n  // not matched, because the parser strips the spaces and control characters that can hide one.\n  if (url.protocol === 'javascript:') return;\n  const path = navigate ? routerPath(url) : null;\n  if (navigate && path !== null) navigate(path);\n  else window.location.assign(url);\n}\n\nexport function useFollowHref(): (href: string) => void {\n  const navigate = useContext(NavigateContext);\n  return (href) => followHref(href, navigate);\n}\n","import cx from 'classnames';\nimport { useRef, type ReactElement } from 'react';\n\nimport styles from './button.module.pcss';\nimport Loader from '../loader';\nimport { ButtonStyleType, ButtonSize, ButtonType } from './button.types';\nimport { ThemeColor } from '../themeProvider/color.types';\nimport type { ButtonProps } from './button.types';\nimport Ripple from '../ripple';\nimport type { RippleHandle } from '../ripple';\nimport { useComponentDefaults } from '../configProvider';\nimport { renderIcon } from '../utils/renderIcon';\nimport { ariaKeyshortcuts, formatHotkey } from '../hooks/useHotkeys';\nimport { toneProps, useToneCheck } from '../styles/tone';\nimport { useLinkClick } from '../utils/navigation';\n\nconst CLASS_BY_STYLE: Record<ButtonStyleType, string> = {\n  filled: styles.buttonStyleFilled,\n  light: styles.buttonStyleLight,\n  outlined: styles.buttonStyleOutlined,\n  plain: styles.buttonStylePlain,\n};\n\nconst CLASS_BY_SIZE: Record<ButtonSize, string> = {\n  small: styles.buttonSizeSmall,\n  medium: styles.buttonSizeMedium,\n  large: styles.buttonSizeLarge,\n  icon: styles.buttonSizeIcon,\n};\n\n// Ripple's own default is white, which vanishes on a transparent button over a light page.\nconst RIPPLE_BACKGROUND = 'color-mix(in srgb, currentcolor 20%, transparent)';\n\nfunction Button(props: ButtonProps): ReactElement {\n  const {\n    className,\n    style,\n    type = ButtonType.Button,\n    styleType = ButtonStyleType.Outlined,\n    color = ThemeColor.Primary,\n    size = ButtonSize.Medium,\n    children,\n    icon,\n    shortcut,\n    // The caller's own wins: it may list a second binding the hint leaves out.\n    'aria-keyshortcuts': keyshortcuts = ariaKeyshortcuts(shortcut),\n    isLoading = false,\n    disabled = false,\n    title = undefined,\n    classNames,\n    styles: slotStyles,\n    ...rest\n  } = useComponentDefaults('Button', props);\n\n  const rippleRef = useRef<RippleHandle>(null);\n  const followLink = useLinkClick();\n  const tone = toneProps(color);\n  useToneCheck('Button', [color]);\n  const isInactive = isLoading || disabled;\n\n  const rootProps = {\n    className: cx(\n      styles.button,\n      CLASS_BY_STYLE[styleType],\n      tone.className,\n      CLASS_BY_SIZE[size],\n      {\n        [styles.buttonLink]: rest.href !== undefined,\n        [styles.buttonNotAllowed]: isInactive,\n        [styles.buttonDisabled]: disabled,\n      },\n      className,\n    ),\n    style: { ...tone.style, ...style },\n    title,\n    'aria-keyshortcuts': keyshortcuts,\n  };\n\n  const content = (\n    <Ripple ref={rippleRef} className={styles.buttonRipple} disabled={isInactive} rippleBackground={RIPPLE_BACKGROUND}>\n      <div className={cx(styles.buttonBackground, classNames?.background)} style={slotStyles?.background}>\n        <div className={cx(styles.buttonContent, classNames?.content)} style={slotStyles?.content}>\n          {isLoading ? (\n            <div\n              className={cx(styles.buttonLoadingContainer, classNames?.loadingContainer)}\n              style={slotStyles?.loadingContainer}\n            >\n              <Loader size='1.2em' color={ThemeColor.Neutral} />\n            </div>\n          ) : null}\n          <div\n            className={cx(classNames?.label, {\n              [styles.buttonLoading]: isLoading,\n              [styles.buttonWithIcon]: !!icon,\n            })}\n            style={slotStyles?.label}\n          >\n            {icon ? renderIcon(icon) : null}\n            {shortcut ? (\n              // One inline run with the label, so the hint sits on its baseline: the icon's flex\n              // row would make the hint its own item and centre it instead.\n              <span>\n                {children}\n                {/* Out of the accessible name, which would read \"Save Ctrl+S\": aria-keyshortcuts says it. */}\n                <span\n                  aria-hidden\n                  className={cx(styles.buttonShortcut, classNames?.shortcut)}\n                  style={slotStyles?.shortcut}\n                >\n                  {formatHotkey(shortcut)}\n                </span>\n              </span>\n            ) : (\n              children\n            )}\n          </div>\n        </div>\n      </div>\n    </Ripple>\n  );\n\n  if (rest.href !== undefined) {\n    const { href, onClick, ref, ...anchorRest } = rest;\n    return (\n      <a\n        {...anchorRest}\n        {...rootProps}\n        // An <a> cannot be disabled. Without its href and tabIndex it leaves the tab order and goes\n        // nowhere, and the role keeps it announced as the link it is, dimmed.\n        {...(disabled ? { role: 'link', tabIndex: undefined } : { href })}\n        aria-disabled={isInactive || anchorRest['aria-disabled'] || undefined}\n        onClick={(event) => {\n          if (isInactive) {\n            event.preventDefault();\n            return;\n          }\n          if (event.detail === 0) rippleRef.current?.addRipple();\n          void onClick?.(event);\n          followLink(event);\n        }}\n        ref={ref}\n      >\n        {content}\n      </a>\n    );\n  }\n\n  const { onClick, ref, ...buttonRest } = rest;\n  return (\n    <button\n      {...buttonRest}\n      {...rootProps}\n      type={type}\n      // Loading is aria-disabled, not disabled, which would drop the keyboard focus mid-submit. The\n      // click is cancelled instead, which also stops the form it submits.\n      aria-disabled={isLoading || buttonRest['aria-disabled'] || undefined}\n      disabled={disabled}\n      onClick={(event) => {\n        if (isLoading) {\n          event.preventDefault();\n          return;\n        }\n        if (event.detail === 0) rippleRef.current?.addRipple();\n        return onClick?.(event);\n      }}\n      ref={ref}\n    >\n      {content}\n    </button>\n  );\n}\n\nButton.StyleType = ButtonStyleType;\nButton.Size = ButtonSize;\nButton.Type = ButtonType;\n\nexport default Button;\n","import cx from 'classnames';\n\nimport styles from './actionButtons.module.pcss';\nimport Button, { ButtonStyleType, ButtonType } from '../button';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport { ActionButtonsProps } from './actionButtons.types';\nimport type { ActionButtonsLabels } from './actionButtons.types';\nimport { ThemeColor } from '../themeProvider/color.types';\n\nconst DEFAULT_LABELS: Required<ActionButtonsLabels> = {\n  submit: 'Save',\n  cancel: 'Cancel',\n};\n\nexport default function ActionButtons(props: ActionButtonsProps) {\n  const {\n    onSubmit = () => null,\n    onClose = () => null,\n    labels,\n    submitButtonStyle = ButtonStyleType.Filled,\n    submitButtonColor = ThemeColor.Primary,\n    isSubmitButtonLoading = false,\n    isSubmitButtonDisabled = false,\n    submitButtonIcon = undefined,\n    cancelButtonStyle = ButtonStyleType.Outlined,\n    cancelButtonColor = ThemeColor.Primary,\n    isCancelButtonLoading = false,\n    isCancelButtonDisabled = false,\n    cancelButtonIcon = undefined,\n    hidePrimaryButton = false,\n    hideSecondaryButton = false,\n    noMargin = false,\n    stackButtons = false,\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    ...rest\n  } = useComponentDefaults('ActionButtons', props);\n  const text = useLabels('ActionButtons', labels, DEFAULT_LABELS);\n  return (\n    <div\n      {...rest}\n      className={cx(\n        styles.actionButtons,\n        !noMargin && styles.actionButtonsMargin,\n        stackButtons && styles.actionButtonsStacked,\n        className,\n        classNames?.root,\n      )}\n      style={{ ...style, ...slotStyles?.root }}\n    >\n      {!hidePrimaryButton && (\n        <Button\n          isLoading={isSubmitButtonLoading}\n          disabled={isSubmitButtonDisabled}\n          styleType={submitButtonStyle}\n          color={submitButtonColor}\n          type={ButtonType.Submit}\n          onClick={onSubmit}\n          className={classNames?.submitButton}\n          style={slotStyles?.submitButton}\n          icon={submitButtonIcon}\n        >\n          {text.submit}\n        </Button>\n      )}\n      {!hideSecondaryButton && (\n        <Button\n          isLoading={isCancelButtonLoading}\n          disabled={isCancelButtonDisabled}\n          onClick={onClose}\n          styleType={cancelButtonStyle}\n          color={cancelButtonColor}\n          className={classNames?.cancelButton}\n          style={slotStyles?.cancelButton}\n          icon={cancelButtonIcon}\n        >\n          {text.cancel}\n        </Button>\n      )}\n    </div>\n  );\n}\n","/* Sized by its icon, where an Icon-size Button leaves the box to the caller. */\n.actionIcon {\n  width: calc(1em + 8px);\n  height: calc(1em + 8px);\n\n  /* A flex box, so the icon centres in it rather than riding on a text baseline. */\n  &-label {\n    display: flex;\n  }\n}\n","import cx from 'classnames';\nimport type { ReactElement } from 'react';\n\nimport Button from '../button';\nimport { ButtonSize, ButtonStyleType } from '../button/button.types';\nimport { ThemeColor } from '../themeProvider/color.types';\nimport type { ActionIconProps } from './actionIcon.types';\nimport styles from './actionIcon.module.pcss';\nimport { renderIcon } from '../utils/renderIcon';\n\nfunction ActionIcon({\n  icon,\n  styleType = ButtonStyleType.Plain,\n  color = ThemeColor.Neutral,\n  className,\n  style,\n  classNames,\n  styles: slotStyles,\n  ...rest\n}: ActionIconProps): ReactElement {\n  return (\n    <Button\n      {...rest}\n      styleType={styleType}\n      color={color}\n      size={ButtonSize.Icon}\n      className={cx(styles.actionIcon, className, classNames?.root)}\n      style={{ ...style, ...slotStyles?.root }}\n      classNames={{ label: styles.actionIconLabel }}\n    >\n      {renderIcon(icon, { className: classNames?.icon, style: slotStyles?.icon })}\n    </Button>\n  );\n}\n\nexport default ActionIcon;\n",".scrollbarProvider {\n  display: contents;\n}\n\n/* Zero specificity, so any scrollbar rule of a component's or the app's own wins. As\n   `.scrollbarProvider *` these tied with a single class and won or lost on stylesheet order. */\n\n/* Firefox */\n:where(.scrollbarProvider *) {\n  scrollbar-width: thin;\n  scrollbar-color: var(--background-accent-light) transparent;\n}\n\n/* Chrome, Edge, and Safari */\n:where(.scrollbarProvider *)::-webkit-scrollbar {\n  width: 12px;\n}\n\n:where(.scrollbarProvider *)::-webkit-scrollbar-track {\n  background: transparent;\n}\n\n:where(.scrollbarProvider *)::-webkit-scrollbar-thumb {\n  background-color: var(--background-accent-light);\n  border-radius: 10px;\n  border: 3px solid transparent;\n  background-clip: padding-box;\n}\n\n/* The page's own scrollbar: <html> is outside every provider, so AhroweProvider sets the thumb\n   colour on it. Without that property the value is invalid, and the browser's own stays. */\n:where(html) {\n  scrollbar-color: var(--ahrowe-page-scrollbar-thumb) transparent;\n}\n","import type { ReactElement } from 'react';\nimport cx from 'classnames';\n\nimport styles from './scrollbarProvider.module.pcss';\n\ninterface Properties {\n  className?: string | undefined;\n  children?: React.ReactNode | undefined;\n}\n\nfunction ScrollbarProvider({ className = undefined, children = undefined }: Properties): ReactElement {\n  return <div className={cx(styles.scrollbarProvider, className)}>{children}</div>;\n}\n\nexport default ScrollbarProvider;\n","import ScrollbarProvider from './scrollbarProvider';\n\nexport default ScrollbarProvider;\n","import type { Theme } from './theme.types';\n\n/**\n * Built-in default theme used by ThemeProvider when no `themes` prop is\n * supplied. Provides a complete set of CSS variables so the component\n * library renders correctly out of the box.\n */\nexport const defaultTheme: Theme = {\n  id: 'default',\n  variables: {\n    '--grey': '#8A8A8A',\n    '--lightgrey': '#e0e0e0',\n    '--lightergrey': '#B1B1B1',\n    '--white': '#FFFFFF',\n    '--red': '#D6002A',\n    '--lighter-red': '#ffeae9',\n    '--green': '#287626',\n    '--lighter-green': '#C4FFC9',\n    '--blue': '#5873d5',\n    '--lighter-blue': '#C6DBFF',\n    '--black': 'black',\n    '--orange': '#ff7f50',\n    '--lighter-orange': '#ffe5d9',\n\n    '--primary-color': '#8184d6',\n    '--primary-accent': '#90CAF9',\n    '--primary-lighter': '#9a9ce4',\n    '--accent-color': 'var(--grey)',\n    '--accent-color-light': 'var(--lightgrey)',\n\n    '--text-color': 'var(--white)',\n    '--text-dark': 'var(--lightergrey)',\n    '--text-light': 'var(--lightergrey)',\n    '--text-on-primary': 'var(--white)',\n    '--text-on-delete': 'var(--white)',\n    '--border-color': '#656574',\n    '--focus-border-color': 'var(--primary-color)',\n\n    '--background': '#393945',\n    '--background-accent': '#33333c',\n    '--background-accent-dark': 'var(--grey)',\n    '--background-accent-light': '#484856',\n    '--background-modal': '#484856',\n\n    '--success-color': 'var(--green)',\n    '--success-lighter': 'var(--lighter-green)',\n    '--error-color': 'var(--red)',\n    '--error-lighter': 'var(--lighter-red)',\n    '--required-mark-color': 'var(--error-color)',\n    '--info-color': 'var(--blue)',\n    '--info-lighter': 'var(--lighter-blue)',\n    '--warn-color': 'var(--orange)',\n    '--warn-lighter': 'var(--lighter-orange)',\n\n    '--default-font-size': '16px',\n    '--text-small-font-size': '14px',\n    '--header-font-size': '24px',\n    '--header-medium-font-size': '18px',\n    '--header-small-font-size': '16px',\n\n    '--default-font-weight': '400',\n    '--header-font-weight': '600',\n\n    '--input-background': 'transparent',\n    '--input-icon-color': 'var(--text-color)',\n    '--error-border-color': 'var(--red)',\n\n    '--default-border-radius': '8px',\n    '--padding-sides': '15px',\n    '--margin-top': '10px',\n\n    '--default-font': \"'Inter', sans-serif\",\n    '--themed-font': \"'Inter', sans-serif\",\n\n    '--card-shadow': '0 3px 1px -2px rgb(0 0 0 / 20%), 0 2px 2px 0 rgb(0 0 0 / 14%), 0 1px 5px 0 rgb(0 0 0 / 12%)',\n\n    '--background-card-elevated': 'var(--background-accent-light)',\n    '--background-card-outlined': 'transparent',\n    // Below the page: on a dark theme the tint fallback is as light as Elevated.\n    '--background-card-filled': 'var(--background-accent)',\n\n    '--spacing-s': '4px',\n    '--spacing-m': '8px',\n    '--spacing-l': '16px',\n  },\n};\n","import { createContext } from 'react';\n\n/**\n * The nearest ThemeProvider's resolved variables, for content that renders outside its DOM\n * subtree. Theme variables reach a component by CSS inheritance, which a portal breaks: a\n * `BodyEnd` child lands wherever the portal target is, so it picks up whatever provider wraps\n * *that* node rather than the one it was written under.\n *\n * Deliberately its own module rather than a member of `themeProvider.tsx`: BodyEnd needs the\n * context and nothing else, and importing the component for it would pull the whole\n * ThemeProvider into every bundle that uses a portal.\n */\nexport const ThemeVariablesContext = createContext<Record<string, string> | null>(null);\n","import { flushSync } from 'react-dom';\n\nlet revealing = false;\n\n/**\n * True while a reveal renders its new theme. ThemeProvider skips its colour fade then: the new\n * page is live inside the blob, so the fade played there too and the blob opened grey.\n */\nexport function isRevealingTheme(): boolean {\n  return revealing;\n}\n\n/**\n * The outline of the new page `progress` (0 to 1) of the way through: a blob round `x`, `y` whose\n * edge ripples as two waves travel round it. The ripple dies down as it grows, so it ends as a\n * circle of exactly `radius`, covering the farthest corner as the animation ends. Ending any\n * larger, so the dents could cover the corner too, it went on growing off screen for a while after\n * it looked finished, and the page took no clicks all that time.\n */\nfunction blob(x: number, y: number, radius: number, progress: number): string {\n  const size = radius * progress;\n  const phase = progress * Math.PI * 3;\n  const wobble = 0.16 * (1 - progress);\n  const vertices = Array.from({ length: 72 }, (_, i) => {\n    const angle = (i / 72) * Math.PI * 2;\n    const r = size * (1 + wobble * (0.6 * Math.sin(3 * angle + phase) + 0.4 * Math.sin(5 * angle - 1.7 * phase)));\n    return `${(x + r * Math.cos(angle)).toFixed(1)}px ${(y + r * Math.sin(angle)).toFixed(1)}px`;\n  });\n  return `polygon(${vertices.join(', ')})`;\n}\n\n/**\n * Applies `change` inside a view transition that clips the new page in as a blob growing out of\n * `origin`. Without view transitions, or when the user asks for reduced motion, the change just\n * happens.\n */\nexport function revealTheme(change: () => void, origin: Element): void {\n  const reduceMotion =\n    typeof window.matchMedia === 'function' && window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n  if (typeof document.startViewTransition !== 'function' || reduceMotion) {\n    change();\n    return;\n  }\n\n  const box = origin.getBoundingClientRect();\n  const x = box.left + box.width / 2;\n  const y = box.top + box.height / 2;\n  const radius = Math.hypot(Math.max(x, window.innerWidth - x), Math.max(y, window.innerHeight - y));\n  const root = document.documentElement;\n\n  // The new theme is captured when this callback returns, so React has to have rendered it by then.\n  const transition = document.startViewTransition(() => {\n    revealing = true;\n    try {\n      flushSync(change);\n    } finally {\n      revealing = false;\n    }\n  });\n  transition.ready\n    .then(() => {\n      const timing = { duration: 900, easing: 'cubic-bezier(0.4, 0, 0.2, 1)' };\n      // Both pictures kept whole and unblended over the browser's own cross-fade, which would show\n      // both themes at half strength inside the blob. Here rather than in the stylesheet, where\n      // an app's unlayered view-transition rule would beat the library's layered one.\n      const whole = { opacity: [1, 1], mixBlendMode: ['normal', 'normal'] };\n      root.animate(whole, { ...timing, pseudoElement: '::view-transition-old(root)' });\n      // Eight steps are enough for the ripple to read as motion rather than as a shape.\n      const clipPath = Array.from({ length: 9 }, (_, step) => blob(x, y, radius, step / 8));\n      root.animate({ ...whole, clipPath }, { ...timing, pseudoElement: '::view-transition-new(root)' });\n    })\n    // Rejects when the transition is skipped, such as by another one starting. The theme has\n    // changed all the same: a skipped transition still runs its update.\n    .catch(() => {});\n}\n",".themeRoot {\n  font-family: var(--default-font);\n  font-size: var(--default-font-size);\n  font-weight: var(--default-font-weight);\n  color: var(--text-color);\n}\n\n/* The class is doubled to beat a component's own `transition` declaration. `.colorFade *` and a\n   plain `.drawer` both count as one class, so which one a descendant gets was decided by whichever\n   stylesheet the bundler happened to emit last, and the theme fade silently stopped applying to\n   whole components when that order moved. */\n.colorFade.colorFade * {\n  transition: background 0.3s ease-in-out;\n}\n","import React, { Component } from 'react';\nimport cx from 'classnames';\n\nimport type { Theme, ThemeProviderProps } from './theme.types';\nimport { defaultTheme } from './defaultTheme';\nimport { ThemeVariablesContext } from './themeVariablesContext';\nimport { isRevealingTheme } from './themeReveal';\nimport styles from './theme.module.pcss';\n\ninterface State {\n  resolvedVariables: Record<string, string>;\n  colorFadeActive: boolean;\n  currentThemeColor: string | null;\n  /** What `resolvedVariables` was resolved from, to tell a theme change from any other render. */\n  resolvedFrom: { themes: Theme[] | undefined; currentThemeId: string | undefined };\n}\n\n/**\n * Resolves a theme ID's variables by walking up its baseThemeId chain, ordered from deepest\n * ancestor to the requested theme so they merge in order (later entries win).\n */\nfunction resolveChain(themes: Theme[], id: string): Record<string, string>[] {\n  const theme = themes.find((t) => t.id === id);\n  if (!theme) return [];\n  const parentVars = theme.baseThemeId ? resolveChain(themes, theme.baseThemeId) : [];\n  return [...parentVars, theme.variables as Record<string, string>];\n}\n\nfunction resolveVariables({ themes = [defaultTheme], currentThemeId }: ThemeProviderProps): Record<string, string> {\n  // The root theme, the first without a baseThemeId, is always the foundation.\n  const rootTheme = themes.find((t) => !t.baseThemeId) ?? themes[0];\n  const rootVars = rootTheme ? (rootTheme.variables as Record<string, string>) : {};\n  const chainVars = currentThemeId ? resolveChain(themes, currentThemeId) : [];\n  return Object.assign({}, rootVars, ...chainVars);\n}\n\nclass ThemeProvider extends Component<ThemeProviderProps, State> {\n  // A nested provider themes a section, not the page, so it leaves the page's `theme-color` alone.\n  static contextType = ThemeVariablesContext;\n  declare context: React.ContextType<typeof ThemeVariablesContext>;\n\n  // Resolved before the first render rather than after mount: resolved in componentDidMount, every\n  // component rendered and measured itself unthemed first, and on each page load some 50\n  // backgrounds then faded in from nothing.\n  state: State = {\n    resolvedVariables: resolveVariables(this.props),\n    colorFadeActive: false,\n    currentThemeColor: null,\n    resolvedFrom: { themes: this.props.themes, currentThemeId: this.props.currentThemeId },\n  };\n\n  private colorFadeTimeout: ReturnType<typeof setTimeout> | null = null;\n  private wrapperRef = React.createRef<HTMLDivElement>();\n  private themeColorMeta: HTMLMetaElement | null = null;\n  private createdThemeColorMeta = false;\n\n  // The fade and the new variables have to land in the same render: added a render later, the\n  // browser may already have painted the new colours without it.\n  static getDerivedStateFromProps(props: ThemeProviderProps, state: State): Partial<State> | null {\n    const { themes, currentThemeId } = state.resolvedFrom;\n    if (props.themes === themes && props.currentThemeId === currentThemeId) return null;\n    return {\n      resolvedVariables: resolveVariables(props),\n      colorFadeActive: !isRevealingTheme(),\n      resolvedFrom: { themes: props.themes, currentThemeId: props.currentThemeId },\n    };\n  }\n\n  componentDidMount() {\n    this.readThemeColor();\n  }\n\n  componentDidUpdate(prevProps: ThemeProviderProps, prevState: State) {\n    if (prevState.resolvedVariables !== this.state.resolvedVariables) {\n      this.readThemeColor();\n      if (this.colorFadeTimeout) clearTimeout(this.colorFadeTimeout);\n      this.colorFadeTimeout = setTimeout(() => {\n        this.setState({ colorFadeActive: false });\n        this.colorFadeTimeout = null;\n      }, 500);\n    }\n    if (prevState.currentThemeColor !== this.state.currentThemeColor) {\n      this.syncThemeColorMeta(this.state.currentThemeColor ?? '');\n    }\n  }\n\n  componentWillUnmount() {\n    if (this.colorFadeTimeout) clearTimeout(this.colorFadeTimeout);\n    // Only remove the meta tag if we created it; leave a host-app tag intact.\n    // Also clear the cached refs — StrictMode's dev-only mount/unmount/remount\n    // cycle reuses this instance, and a stale ref would otherwise point at a\n    // detached element on remount instead of re-inserting into <head>.\n    if (this.createdThemeColorMeta) {\n      this.themeColorMeta?.remove();\n      this.themeColorMeta = null;\n      this.createdThemeColorMeta = false;\n    }\n  }\n\n  /**\n   * Replaces the former react-helmet usage: keep a single\n   * <meta name=\"theme-color\"> in sync with the active theme's primary colour.\n   * Reuses a tag the host app already declares; otherwise creates its own\n   * (and removes it again on unmount).\n   */\n  private syncThemeColorMeta(color: string) {\n    if (typeof document === 'undefined') return;\n\n    if (!this.themeColorMeta) {\n      const existing = document.head.querySelector<HTMLMetaElement>('meta[name=\"theme-color\"]');\n      if (existing) {\n        this.themeColorMeta = existing;\n      } else {\n        const meta = document.createElement('meta');\n        meta.setAttribute('name', 'theme-color');\n        document.head.appendChild(meta);\n        this.themeColorMeta = meta;\n        this.createdThemeColorMeta = true;\n      }\n    }\n\n    this.themeColorMeta.setAttribute('content', color);\n  }\n\n  /** `--primary-color` as the browser resolved it, which may be a `var()` of another variable. */\n  private readThemeColor() {\n    if (this.context || !this.wrapperRef.current) return;\n    const color = getComputedStyle(this.wrapperRef.current).getPropertyValue('--primary-color').trim();\n    if (color !== this.state.currentThemeColor) this.setState({ currentThemeColor: color });\n  }\n\n  render() {\n    const { children } = this.props;\n    const { resolvedVariables, colorFadeActive } = this.state;\n\n    return (\n      <div\n        ref={this.wrapperRef}\n        id='themeProvider'\n        className={cx(styles.themeRoot, colorFadeActive && styles.colorFade)}\n        style={resolvedVariables as React.CSSProperties}\n      >\n        <ThemeVariablesContext.Provider value={resolvedVariables}>{children}</ThemeVariablesContext.Provider>\n      </div>\n    );\n  }\n}\n\nexport default ThemeProvider;\n","import type { Theme } from './theme.types';\n\n/**\n * The light sibling of {@link defaultTheme}, which is dark. Same brand, inverted surfaces:\n * it declares only what has to flip, and inherits the rest through `baseThemeId`, so a change\n * to the palette or the type scale reaches both themes at once.\n *\n * ```tsx\n * <ThemeProvider themes={[defaultTheme, lightTheme]} currentThemeId='light'>\n * ```\n *\n * `--primary-color` is deliberately untouched: it is the brand, and it reads on both.\n */\nexport const lightTheme: Theme = {\n  id: 'light',\n  baseThemeId: 'default',\n  variables: {\n    '--background': '#FFFFFF',\n    '--background-accent': '#F4F4F8',\n    '--background-accent-light': '#FAFAFD',\n    '--background-accent-dark': '#D4D4E0',\n    '--background-modal': '#FFFFFF',\n\n    '--text-color': '#1E1E2E',\n    '--text-dark': '#55556E',\n    '--text-light': '#75758C',\n\n    '--border-color': '#DCDCE8',\n\n    // Dark text on the dark theme's own lighter-* tints is unreadable; on white they are the\n    // right weight as-is, so only the greens and reds that were tuned for a dark ground move.\n    '--lighter-green': '#DFF5E1',\n    '--lighter-red': '#FCE4E6',\n    '--lighter-blue': '#E1EBFF',\n    '--lighter-orange': '#FFEADF',\n\n    // The default theme's green is dark enough to read on white; its grey text is not.\n    '--accent-color': '#6E6E80',\n\n    '--input-background': '#F4F4F8',\n\n    // Three stacked black shadows read as grime on a white surface.\n    '--card-shadow': '0 1px 3px rgb(0 0 0 / 8%), 0 1px 2px rgb(0 0 0 / 6%)',\n  },\n};\n","import { createContext } from 'react';\n\n/**\n * The element `AhroweProvider` portals into. `undefined` means there is no provider, so BodyEnd\n * falls back to `#bodyEnd`; `null` means a provider that cannot portal yet, on the server and\n * while hydrating, and must not be reported missing.\n */\nexport const PortalHostContext = createContext<HTMLElement | null | undefined>(undefined);\n","import { createPortal } from 'react-dom';\nimport { useContext, type CSSProperties, type ReactElement } from 'react';\n\nimport { ThemeVariablesContext } from '../themeProvider/themeVariablesContext';\nimport themeStyles from '../themeProvider/theme.module.pcss';\nimport { PortalHostContext } from './portalHostContext';\nimport type { BodyEndProps } from './bodyEnd.types';\nimport { isDev } from '../utils/isDev';\n\nfunction BodyEnd({ children = null }: BodyEndProps): ReactElement | null {\n  const themeVariables = useContext(ThemeVariablesContext);\n  const host = useContext(PortalHostContext);\n  const target = host === undefined ? document.getElementById('bodyEnd') : host;\n  if (!target) {\n    if (host === undefined && isDev()) {\n      console.error('[BodyEnd] Nowhere to render. Wrap your app in <AhroweProvider>, or add <div id=\"bodyEnd\"></div>.');\n    }\n    return null;\n  }\n  /*\n   * Carry the nearest theme through the portal. `display: contents` is what makes this safe:\n   * the wrapper generates no box, so positioning, stacking and clipping are untouched —\n   * escaping clipping ancestors is the whole point of this component — while custom\n   * properties still inherit, because inheritance follows the element tree, not the box tree.\n   *\n   * Variables this theme leaves unset still fall through to whatever wraps the target, which\n   * is the same cascade the children would have seen rendered in place. `themeRoot` sets the\n   * base typography again, which the target would otherwise pass down from its own theme.\n   */\n  if (!themeVariables) return createPortal(children, target);\n  return createPortal(\n    <div className={themeStyles.themeRoot} style={{ display: 'contents', ...themeVariables } as CSSProperties}>\n      {children}\n    </div>,\n    target,\n  );\n}\n\nexport default BodyEnd;\n",".container {\n  position: fixed;\n  z-index: 9999;\n  display: flex;\n  flex-direction: column;\n  gap: 0.5em;\n  pointer-events: none;\n  padding: 1em;\n  box-sizing: border-box;\n\n  &-top-left {\n    top: 0;\n    left: 0;\n    align-items: flex-start;\n  }\n\n  &-top-center {\n    top: 0;\n    left: 50%;\n    transform: translateX(-50%);\n    align-items: center;\n  }\n\n  &-top-right {\n    top: 0;\n    right: 0;\n    align-items: flex-end;\n  }\n\n  &-bottom-left {\n    bottom: 0;\n    left: 0;\n    align-items: flex-start;\n    flex-direction: column-reverse;\n  }\n\n  &-bottom-center {\n    bottom: 0;\n    left: 50%;\n    transform: translateX(-50%);\n    align-items: center;\n    flex-direction: column-reverse;\n  }\n\n  &-bottom-right {\n    bottom: 0;\n    right: 0;\n    align-items: flex-end;\n    flex-direction: column-reverse;\n  }\n}\n\n.toast {\n  pointer-events: all;\n  display: flex;\n  align-items: flex-start;\n  gap: 0.625em;\n  padding: 0.75em 0.875em;\n  border-radius: var(--default-border-radius);\n  border-left: 0.1875em solid var(--tone);\n  background: var(--background-modal);\n\n  /* Must follow the shorthand above, which resets it. The gradient reaches over border-left only\n     because its origin is the border box; the border is the fallback. */\n  background-image: var(--tone-gradient);\n  background-repeat: no-repeat;\n  background-size: 0.1875em 100%;\n  background-origin: border-box;\n  color: var(--text-color);\n  font-family: var(--default-font);\n  box-shadow: var(--card-shadow);\n\n  /* em, so the line length stays a measure rather than a pixel count. The screen\n     wins over both: below about 270px the minimum alone was wider than the space\n     the container had, and a toast pinned to one edge hung off the other. The 2em\n     is the container's own padding, which is in the same em as these. */\n  min-width: min(15em, calc(100vw - 2em));\n  max-width: min(23.75em, calc(100vw - 2em));\n  box-sizing: border-box;\n  position: relative;\n\n  /* Tells the browser vertical panning is native (so the page can still scroll behind the toast)\n     while horizontal movement is left for useSwipeDismiss's own pointer handling to decide. */\n  touch-action: pan-y;\n  overflow: hidden;\n  animation: toastSlideDown 0.25s ease forwards;\n\n  &-bottom {\n    animation: toastSlideUp 0.25s ease forwards;\n  }\n\n  &-dismissing {\n    animation: toastFadeOut 0.2s ease forwards;\n  }\n\n  &-paused .toastProgressBar {\n    animation-play-state: paused;\n  }\n}\n\n.toastIcon {\n  flex-shrink: 0;\n  color: var(--tone);\n  margin-top: 0.0667em; /* 1px against this element's own 0.9375em */\n  font-size: 0.9375em;\n}\n\n.toastContent {\n  flex: 1;\n  font-size: 0.875em;\n  line-height: 1.4;\n}\n\n/* Two classes, to outrank Button's font-size and ActionIcon's box whichever stylesheet loads\n   first. */\n.toast .toastCloseButton {\n  flex-shrink: 0;\n  width: 1em;\n  height: 1em;\n  font-size: 0.8125em;\n  opacity: 0.6;\n  transition: opacity 0.15s;\n  margin-top: 0.0769em; /* 1px against this element's own 0.8125em */\n\n  &:hover {\n    opacity: 1;\n  }\n}\n\n.toastProgressBar {\n  position: absolute;\n  bottom: 0;\n  left: 0;\n  height: 0.1875em;\n  width: 100%;\n  background: currentcolor;\n  opacity: 0.35;\n  transform-origin: left;\n  animation: toastShrink var(--toast-duration) linear forwards;\n}\n\n@keyframes toastSlideDown {\n  from {\n    opacity: 0;\n    transform: translateY(-0.75em);\n  }\n\n  to {\n    opacity: 1;\n    transform: translateY(0);\n  }\n}\n\n@keyframes toastSlideUp {\n  from {\n    opacity: 0;\n    transform: translateY(0.75em);\n  }\n\n  to {\n    opacity: 1;\n    transform: translateY(0);\n  }\n}\n\n@keyframes toastFadeOut {\n  from {\n    opacity: 1;\n    transform: scale(1);\n  }\n\n  to {\n    opacity: 0;\n    transform: scale(0.92);\n  }\n}\n\n@keyframes toastShrink {\n  from {\n    transform: scaleX(1);\n  }\n\n  to {\n    transform: scaleX(0);\n  }\n}\n\n/* The entrance and exit slide go. `toastShrink` stays: it is the remaining-time indicator, and a\n   progress bar frozen at full width would tell the user the opposite of what is true. Removal is\n   driven by a timer in toastProvider, not by these animations, so dropping them cannot strand a\n   toast on screen. */\n@media (prefers-reduced-motion: reduce) {\n  .toast,\n  .toast-bottom,\n  .toast-dismissing {\n    animation: none;\n  }\n}\n","import { useCallback, useSyncExternalStore } from 'react';\n\nfunction supported(): boolean {\n  return typeof window !== 'undefined' && typeof window.matchMedia === 'function';\n}\n\n/**\n * `true` while `query` matches, kept current so a resize or an input-mode change re-renders.\n *\n * `useSyncExternalStore` rather than `useState` + `useEffect`: callers pick *markup* with this,\n * so hydration has to start from the server's answer. Reading `matchMedia` in a `useState`\n * initialiser makes the client's first render disagree with the server's HTML.\n */\nexport function useMediaQuery(query: string): boolean {\n  const subscribe = useCallback(\n    (onChange: () => void) => {\n      if (!supported()) return () => {};\n      const mql = window.matchMedia(query);\n      mql.addEventListener('change', onChange);\n      return () => mql.removeEventListener('change', onChange);\n    },\n    [query],\n  );\n\n  const getSnapshot = useCallback(() => supported() && window.matchMedia(query).matches, [query]);\n\n  /** No media query exists on the server, so the tree renders as if nothing matched. */\n  const getServerSnapshot = useCallback(() => false, []);\n\n  return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n}\n\nexport default useMediaQuery;\n","import { useMediaQuery } from './useMediaQuery';\n\n/** `true` when the primary pointing device is coarse (touch). */\nexport function useCoarsePointer(): boolean {\n  return useMediaQuery('(pointer: coarse)');\n}\n\nexport default useCoarsePointer;\n","import { useCallback, useEffect, useRef, useState } from 'react';\n\nimport { useCoarsePointer } from './useCoarsePointer';\n\nexport interface UseSwipeDismissOptions {\n  /** Called once the swipe commits (past the threshold) — after the exit transition finishes,\n   * not the instant the pointer is released, unless `animateExit` is `false`. */\n  onDismiss: () => void;\n  /** Which axis the drag tracks (default `'x'`, the natural direction for a toast/notification). */\n  axis?: 'x' | 'y';\n  /** Distance in px past which releasing counts as a commit, regardless of speed (default `80`). */\n  distanceThreshold?: number;\n  /** Speed in px/ms past which releasing counts as a commit, regardless of distance (default `0.5`) —\n   * lets a fast flick dismiss even if the pointer didn't travel very far. */\n  velocityThreshold?: number;\n  /** Disables the gesture entirely — no listeners are attached. */\n  disabled?: boolean;\n  /** The offset in px as it changes: on every move, then `0` as it springs back or the exit offset\n   * once it commits. For something else that follows the drag, such as a fading backdrop. */\n  onDrag?: (offset: number) => void;\n  /** Restricts the gesture to coarse (touch) pointers, auto-detected via `matchMedia`. On a\n   * mouse/trackpad device a drag is more likely to be a text selection than an intentional\n   * dismiss, and other affordances (a close button, auto-dismiss) typically already cover that\n   * case. Default `true`; set `false` to allow the gesture on any pointer type. */\n  mobileOnly?: boolean;\n  /** Fades the element out as it is dragged (default `true`). */\n  fade?: boolean;\n  /** Slides the element off before calling `onDismiss` (default `true`). `false` calls it the moment\n   * the swipe commits and leaves the element where it was let go, for a caller that animates the\n   * exit itself. */\n  animateExit?: boolean;\n}\n\nexport interface UseSwipeDismissResult<T extends HTMLElement> {\n  /** Attach to the element that should become draggable. The hook wires up its own pointer\n   * listeners on this node directly (no handlers to spread), so this is the only thing to attach.\n   *\n   * A callback ref rather than an object one, because the hook has to know when the node changes:\n   * an element that is unmounted and rendered again is a *different* node, and a ref object cannot\n   * say so. To keep a ref of your own on the element, call both from one callback. */\n  ref: React.RefCallback<T>;\n  /** True for the duration of an active drag — e.g. use it to pause an auto-dismiss timer. */\n  isDragging: boolean;\n}\n\nconst EXIT_TRANSITION_MS = 180;\n// Below this many px of movement, direction intent isn't decided yet — lets a vertical scroll\n// gesture that merely starts on the element pass through instead of being hijacked as a swipe.\nconst AXIS_LOCK_THRESHOLD = 10;\n\n/**\n * Drag-to-dismiss on any pointer type (touch, mouse, pen) via the Pointer Events API. Applies the\n * drag offset and a fading opacity directly to the DOM node (bypassing React state) so the visual\n * tracks the pointer at native speed, then either springs back or animates fully off-screen and\n * calls `onDismiss` once that exit transition completes.\n */\nexport function useSwipeDismiss<T extends HTMLElement>({\n  onDismiss,\n  axis = 'x',\n  distanceThreshold = 80,\n  velocityThreshold = 0.5,\n  disabled = false,\n  mobileOnly = true,\n  fade = true,\n  animateExit = true,\n  onDrag,\n}: UseSwipeDismissOptions): UseSwipeDismissResult<T> {\n  // The node lives in state, not in a ref: the listeners have to be re-attached when the element\n  // is replaced, and a ref object's `current` changing never re-runs the effect below. Rendering\n  // the element again after it was unmounted otherwise left the gesture wired to the old node.\n  const [node, setNode] = useState<T | null>(null);\n  const ref = useCallback((element: T | null) => setNode(element), []);\n  const [isDragging, setIsDragging] = useState(false);\n\n  const isCoarsePointer = useCoarsePointer();\n\n  const effectiveDisabled = disabled || (mobileOnly && !isCoarsePointer);\n\n  // Mutable across the life of one drag; doesn't need to trigger re-renders.\n  const dragRef = useRef<{\n    pointerId: number;\n    startX: number;\n    startY: number;\n    startTime: number;\n    offset: number;\n    axisLocked: boolean;\n    active: boolean;\n  } | null>(null);\n\n  const onDismissRef = useRef(onDismiss);\n  const onDragRef = useRef(onDrag);\n  useEffect(() => {\n    onDismissRef.current = onDismiss;\n    onDragRef.current = onDrag;\n  });\n\n  useEffect(() => {\n    const el = node;\n    if (!el || effectiveDisabled) return;\n    let settleTimer: ReturnType<typeof setTimeout> | undefined;\n\n    function applyTransform(offset: number, transition: string) {\n      if (!el) return;\n      if (transition !== 'none') {\n        // Switching straight from the live drag's `transition: none` to an animated one, in the\n        // same tick as the transform change, can get coalesced into a single frame with no\n        // animation in between — the element just jumps to its end state instead of sliding\n        // there. Forcing a layout read first commits the current (pre-transition) position as\n        // its own frame, so the transition below actually has something to animate from.\n        void el.offsetHeight;\n      }\n      const translate = axis === 'x' ? `translateX(${offset}px)` : `translateY(${offset}px)`;\n      el.style.transition = transition;\n      el.style.transform = translate;\n      if (fade) el.style.opacity = String(Math.max(0, 1 - Math.abs(offset) / (distanceThreshold * 2.5)));\n      onDragRef.current?.(offset);\n    }\n\n    // Left behind, the inline styles would override the element's stylesheet for good.\n    function clearInlineStyles() {\n      if (!el || dragRef.current) return;\n      el.style.removeProperty('transform');\n      el.style.removeProperty('opacity');\n      el.style.removeProperty('transition');\n    }\n\n    function onPointerMove(event: PointerEvent) {\n      const drag = dragRef.current;\n      if (!drag || event.pointerId !== drag.pointerId) return;\n      // No button held: its release never reached the page, as when a context menu kept it.\n      if (event.buttons === 0) {\n        finishDrag();\n        return;\n      }\n\n      const deltaX = event.clientX - drag.startX;\n      const deltaY = event.clientY - drag.startY;\n\n      if (!drag.axisLocked) {\n        const primary = axis === 'x' ? deltaX : deltaY;\n        const cross = axis === 'x' ? deltaY : deltaX;\n        if (Math.abs(primary) < AXIS_LOCK_THRESHOLD && Math.abs(cross) < AXIS_LOCK_THRESHOLD) return;\n        if (Math.abs(cross) > Math.abs(primary)) {\n          // The gesture is mostly along the other axis (e.g. a page scroll) — hand it back.\n          drag.active = false;\n          return;\n        }\n        drag.axisLocked = true;\n        setIsDragging(true);\n        // A finished `forwards` animation (a toast's entrance) holds its frame over inline styles.\n        // Cancelled, as `animation: none` would replay it once restored. Not on a mere tap.\n        for (const animation of el?.getAnimations?.() ?? []) animation.cancel();\n      }\n\n      if (!drag.active) return;\n      event.preventDefault();\n      drag.offset = axis === 'x' ? deltaX : deltaY;\n      applyTransform(drag.offset, 'none');\n    }\n\n    function finishDrag() {\n      const drag = dragRef.current;\n      dragRef.current = null;\n      window.removeEventListener('pointermove', onPointerMove);\n      window.removeEventListener('pointerup', finishDrag);\n      window.removeEventListener('pointercancel', finishDrag);\n      if (!drag || !drag.active || !drag.axisLocked) {\n        setIsDragging(false);\n        return;\n      }\n\n      const elapsed = Math.max(1, Date.now() - drag.startTime);\n      const velocity = drag.offset / elapsed;\n      const committed = Math.abs(drag.offset) >= distanceThreshold || Math.abs(velocity) >= velocityThreshold;\n\n      if (committed && !animateExit) {\n        onDismissRef.current();\n      } else if (committed) {\n        const rect = el?.getBoundingClientRect();\n        const size = (axis === 'x' ? rect?.width : rect?.height) ?? 0;\n        const exitOffset = Math.sign(drag.offset || 1) * (size + 40);\n        applyTransform(\n          exitOffset,\n          `transform ${EXIT_TRANSITION_MS}ms ease-out, opacity ${EXIT_TRANSITION_MS}ms ease-out`,\n        );\n        setTimeout(() => onDismissRef.current(), EXIT_TRANSITION_MS);\n      } else {\n        applyTransform(0, `transform ${EXIT_TRANSITION_MS}ms ease-out, opacity ${EXIT_TRANSITION_MS}ms ease-out`);\n        clearTimeout(settleTimer);\n        settleTimer = setTimeout(clearInlineStyles, EXIT_TRANSITION_MS);\n      }\n      setIsDragging(false);\n    }\n\n    function onPointerDown(event: PointerEvent) {\n      // Another mouse button opens a menu, which can keep its release from the page.\n      if (dragRef.current || !el || event.button !== 0) return;\n      dragRef.current = {\n        pointerId: event.pointerId,\n        startX: event.clientX,\n        startY: event.clientY,\n        startTime: Date.now(),\n        offset: 0,\n        axisLocked: false,\n        active: true,\n      };\n      window.addEventListener('pointermove', onPointerMove, { passive: false });\n      window.addEventListener('pointerup', finishDrag);\n      window.addEventListener('pointercancel', finishDrag);\n    }\n\n    el.addEventListener('pointerdown', onPointerDown);\n    return () => {\n      clearTimeout(settleTimer);\n      // A press under way would miss its release, and its drag would block every press after it.\n      dragRef.current = null;\n      setIsDragging(false);\n      el.removeEventListener('pointerdown', onPointerDown);\n      window.removeEventListener('pointermove', onPointerMove);\n      window.removeEventListener('pointerup', finishDrag);\n      window.removeEventListener('pointercancel', finishDrag);\n    };\n  }, [node, axis, distanceThreshold, velocityThreshold, effectiveDisabled, fade, animateExit]);\n\n  return { ref, isDragging };\n}\n","import React, { useEffect, useRef, useState } from 'react';\nimport cx from 'classnames';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport {\n  faCircleInfo,\n  faCircleCheck,\n  faCircleXmark,\n  faTriangleExclamation,\n  faBell,\n  faXmark,\n} from '@fortawesome/free-solid-svg-icons';\n\nimport styles from './toast.module.pcss';\nimport type { ToastItem, ToastLabels } from './toast.types';\nimport ActionIcon from '../actionIcon';\nimport { useSwipeDismiss } from '../hooks/useSwipeDismiss';\nimport { ThemeColor } from '../themeProvider/color.types';\nimport { toneProps, useToneCheck } from '../styles/tone';\n\ninterface ToastProps {\n  item: ToastItem;\n  isBottom: boolean;\n  onDismiss: (id: string) => void;\n  /** Already resolved by the provider: defaults, provider, then this toast's own. */\n  labels: Required<ToastLabels>;\n}\n\n// A Map, so a CSS colour can never hit an Object.prototype key.\nconst ICON_BY_COLOR = new Map<string, typeof faBell>([\n  [ThemeColor.Info, faCircleInfo],\n  [ThemeColor.Success, faCircleCheck],\n  [ThemeColor.Warn, faTriangleExclamation],\n  [ThemeColor.Error, faCircleXmark],\n]);\n\nfunction Toast({ item, isBottom, onDismiss, labels }: ToastProps) {\n  const { id, content, options, dismissing } = item;\n  const { duration, showRemainingTime, showCloseButton, pauseOnHover, color, classNames, styles: slotStyles } = options;\n  const tone = toneProps(color);\n  useToneCheck('Toast', [color]);\n\n  const [hovered, setHovered] = useState(false);\n  const [focused, setFocused] = useState(false);\n\n  const [swiped, setSwiped] = useState(false);\n  const { ref: swipeRef, isDragging } = useSwipeDismiss<HTMLDivElement>({\n    onDismiss: () => {\n      setSwiped(true);\n      onDismiss(id);\n    },\n  });\n\n  const paused = (pauseOnHover && (hovered || focused)) || isDragging;\n\n  // Remaining time is carried across pause/resume cycles so the timer continues\n  // where it left off instead of restarting on every hover.\n  const remainingRef = useRef(duration);\n\n  useEffect(() => {\n    if (duration === 0 || paused) return;\n    const start = Date.now();\n    const timer = setTimeout(() => onDismiss(id), remainingRef.current);\n    return () => {\n      clearTimeout(timer);\n      remainingRef.current -= Date.now() - start;\n    };\n  }, [id, duration, onDismiss, paused]);\n\n  return (\n    <div\n      ref={swipeRef}\n      className={cx(\n        styles.toast,\n        isBottom ? styles.toastBottom : null,\n        tone.className,\n        dismissing && !swiped && styles.toastDismissing,\n        paused && styles.toastPaused,\n        classNames?.root,\n      )}\n      style={{ ...tone.style, ...slotStyles?.root }}\n      onMouseEnter={pauseOnHover ? () => setHovered(true) : undefined}\n      onMouseLeave={pauseOnHover ? () => setHovered(false) : undefined}\n      onFocus={pauseOnHover ? () => setFocused(true) : undefined}\n      onBlur={pauseOnHover ? () => setFocused(false) : undefined}\n    >\n      <FontAwesomeIcon\n        icon={ICON_BY_COLOR.get(color) ?? faBell}\n        className={cx(styles.toastIcon, classNames?.icon)}\n        style={slotStyles?.icon as React.CSSProperties & Record<`--fa-font-${string}`, string>}\n      />\n\n      <div className={cx(styles.toastContent, classNames?.content)} style={slotStyles?.content}>\n        {content}\n      </div>\n\n      {showCloseButton && (\n        <ActionIcon\n          icon={faXmark}\n          color='var(--text-dark)'\n          onClick={() => onDismiss(id)}\n          className={cx(styles.toastCloseButton, classNames?.closeButton)}\n          style={slotStyles?.closeButton}\n          aria-label={labels.close}\n        />\n      )}\n\n      {showRemainingTime && duration > 0 && (\n        <div\n          className={cx(styles.toastProgressBar, classNames?.progressBar)}\n          style={{ '--toast-duration': `${duration}ms`, ...slotStyles?.progressBar } as React.CSSProperties}\n        />\n      )}\n    </div>\n  );\n}\n\nexport default Toast;\n","import type { ReactNode } from 'react';\nimport type { ToastOptions } from './toast.types';\n\ntype ShowToastFn = (content: ReactNode, options?: ToastOptions) => void;\n\n// A list, so that when the host registered last unmounts, the one before it takes over again.\nconst handlers: ShowToastFn[] = [];\n\nexport function _registerToastHandler(fn: ShowToastFn): void {\n  if (handlers.length > 0) {\n    console.warn(\n      '[Toast] More than one outermost AhroweProvider is mounted, and toasts show in only one of them. Keep one at the root and nest any other inside it.',\n    );\n  }\n  handlers.push(fn);\n}\n\nexport function _unregisterToastHandler(fn: ShowToastFn): void {\n  const index = handlers.indexOf(fn);\n  if (index !== -1) handlers.splice(index, 1);\n}\n\nexport function showToast(content: ReactNode, options?: ToastOptions): void {\n  const handler = handlers[handlers.length - 1];\n  if (!handler) {\n    console.error('[Toast] No AhroweProvider found. Wrap your app in <AhroweProvider>.');\n    return;\n  }\n  handler(content, options);\n}\n","import React, { useCallback, useInsertionEffect, useRef, useState } from 'react';\nimport cx from 'classnames';\nimport BodyEnd from 'components/common/bodyEnd';\n\nimport styles from './toast.module.pcss';\nimport Toast from './toast';\nimport { _registerToastHandler, _unregisterToastHandler } from './toastService';\nimport type {\n  ToastContextConfig,\n  ToastItem,\n  ToastOptions,\n  ResolvedToastOptions,\n  ToastPosition,\n  ToastLabels,\n  ToastHostProps,\n} from './toast.types';\nimport type { ReactNode } from 'react';\nimport { ThemeColor } from '../themeProvider/color.types';\n\nconst DEFAULTS: Required<ToastContextConfig> = {\n  position: 'top-right',\n  duration: 4000,\n  showRemainingTime: true,\n  showCloseButton: true,\n  allowStacking: true,\n  pauseOnHover: true,\n  labels: { close: 'Close' },\n};\n\nconst DISMISS_ANIMATION_MS = 220;\n\nfunction positionToClass(position: ToastPosition): string {\n  return styles[`container-${position}`];\n}\n\nfunction ToastHost({\n  children,\n  position = DEFAULTS.position,\n  duration = DEFAULTS.duration,\n  showRemainingTime = DEFAULTS.showRemainingTime,\n  showCloseButton = DEFAULTS.showCloseButton,\n  allowStacking = DEFAULTS.allowStacking,\n  pauseOnHover = DEFAULTS.pauseOnHover,\n  labels,\n}: ToastHostProps) {\n  const [toasts, setToasts] = useState<ToastItem[]>([]);\n  const counterRef = useRef(0);\n\n  const dismiss = useCallback((id: string) => {\n    setToasts((prev) => prev.map((t) => (t.id === id ? { ...t, dismissing: true } : t)));\n    setTimeout(() => {\n      setToasts((prev) => prev.filter((t) => t.id !== id));\n    }, DISMISS_ANIMATION_MS);\n  }, []);\n\n  const addToast = useCallback(\n    (content: ReactNode, options?: ToastOptions) => {\n      const resolved: ResolvedToastOptions = {\n        position: options?.position ?? position,\n        duration: options?.duration ?? duration,\n        showRemainingTime: options?.showRemainingTime ?? showRemainingTime,\n        showCloseButton: options?.showCloseButton ?? showCloseButton,\n        pauseOnHover: options?.pauseOnHover ?? pauseOnHover,\n        color: options?.color ?? ThemeColor.Primary,\n        classNames: options?.classNames,\n        styles: options?.styles,\n      };\n\n      counterRef.current += 1;\n      const id = `toast-${counterRef.current}`;\n      const newToast: ToastItem = { id, content, options: resolved, dismissing: false, labels: options?.labels };\n\n      setToasts((prev) => {\n        if (!allowStacking && prev.length > 0) {\n          // Mark all existing as dismissing, then replace\n          setTimeout(() => {\n            setToasts([newToast]);\n          }, DISMISS_ANIMATION_MS);\n          return prev.map((t) => ({ ...t, dismissing: true }));\n        }\n        return [...prev, newToast];\n      });\n    },\n    [position, duration, showRemainingTime, showCloseButton, allowStacking, pauseOnHover],\n  );\n\n  // Insertion effects run before every layout and passive effect, including the app's, which as\n  // this host's descendants would otherwise run first. Registered once, so that a settings change\n  // does not move this host to the end of the list.\n  const latestAddToast = useRef(addToast);\n  useInsertionEffect(() => {\n    latestAddToast.current = addToast;\n  }, [addToast]);\n\n  useInsertionEffect(() => {\n    const show: typeof addToast = (content, options) => latestAddToast.current(content, options);\n    _registerToastHandler(show);\n    return () => _unregisterToastHandler(show);\n  }, []);\n\n  // Group toasts by position so per-toast position overrides work correctly\n  const byPosition = toasts.reduce<Record<string, ToastItem[]>>((acc, t) => {\n    const pos = t.options.position;\n    if (!acc[pos]) acc[pos] = [];\n    acc[pos].push(t);\n    return acc;\n  }, {});\n\n  return (\n    <>\n      {children}\n      <BodyEnd>\n        {Object.entries(byPosition).map(([pos, items]) => (\n          <div key={pos} className={cx(styles.container, positionToClass(pos as typeof position))}>\n            {items.map((item) => (\n              <Toast\n                key={item.id}\n                item={item}\n                isBottom={pos.startsWith('bottom')}\n                onDismiss={dismiss}\n                labels={{ ...DEFAULTS.labels, ...labels, ...item.labels } as Required<ToastLabels>}\n              />\n            ))}\n          </div>\n        ))}\n      </BodyEnd>\n    </>\n  );\n}\n\nexport default ToastHost;\n","import {\n  useCallback,\n  useContext,\n  useEffect,\n  useLayoutEffect,\n  useState,\n  useSyncExternalStore,\n  type ReactElement,\n} from 'react';\n\nimport ConfigProvider from 'components/common/configProvider';\nimport ScrollbarProvider from 'components/common/scrollbarProvider';\nimport ThemeProvider from 'components/common/themeProvider';\nimport ToastHost from '../toast/toastHost';\nimport { PortalHostContext } from '../bodyEnd/portalHostContext';\nimport type { AhroweProviderProps } from './ahroweProvider.types';\n\nconst subscribeToNothing = () => () => {};\n\nfunction AhroweProvider({\n  children,\n  themes,\n  currentThemeId,\n  defaultProps,\n  presentation,\n  navigate,\n  locale,\n  toast,\n  scrollbars,\n}: AhroweProviderProps): ReactElement {\n  // A nested provider themes and configures its section only. A second toast list would stack\n  // over the first in the same corner, and a second host would split the portals in two.\n  const isNested = useContext(PortalHostContext) !== undefined;\n\n  // The server renders no portal, so hydration must not either: `false` there, and `true` from\n  // the first render of an app that is not hydrating.\n  const canPortal = useSyncExternalStore(\n    subscribeToNothing,\n    () => true,\n    () => false,\n  );\n\n  // Created up front instead of rendered: a rendered host exists only after the first commit, so\n  // a modal open on the first render mounted a commit late and its initial focus found nothing.\n  const [host] = useState(() => (typeof document === 'undefined' ? null : document.createElement('div')));\n\n  // Attached from a marker before the app, because refs attach in tree order: this way the host is\n  // in the page before any layout effect runs. The slot stays last, so panels paint above the app.\n  const attachHost = useCallback(\n    (marker: HTMLTemplateElement | null) => {\n      const slot = marker?.parentElement?.lastElementChild;\n      if (!slot || !host) return;\n      // No cleanup, and no move when already in place: the host leaves with its slot anyway, and\n      // StrictMode's extra detach and reattach otherwise took a focused element in it along.\n      if (host.parentNode !== slot) slot.appendChild(host);\n    },\n    [host],\n  );\n\n  // Read from the host rather than `themes`, for the resolved value, `var()` references included.\n  const stylesPage = !isNested && scrollbars !== false;\n  useLayoutEffect(() => {\n    if (!stylesPage || !host) return;\n    const thumb = getComputedStyle(host).getPropertyValue('--background-accent-light').trim();\n    const page = document.documentElement.style;\n    page.setProperty('--ahrowe-page-scrollbar-thumb', thumb);\n    return () => {\n      page.removeProperty('--ahrowe-page-scrollbar-thumb');\n    };\n  }, [stylesPage, host, themes, currentThemeId]);\n\n  const ignoresRootProps = isNested && (toast !== undefined || scrollbars !== undefined);\n  useEffect(() => {\n    if (ignoresRootProps) {\n      console.warn(\n        '[AhroweProvider] `toast` and `scrollbars` are read by the outermost AhroweProvider only. Set them there.',\n      );\n    }\n  }, [ignoresRootProps]);\n\n  const root = (\n    <PortalHostContext.Provider value={canPortal ? host : null}>\n      <template ref={attachHost} />\n      <ToastHost {...toast}>{children}</ToastHost>\n      <div />\n    </PortalHostContext.Provider>\n  );\n\n  return (\n    <ConfigProvider defaultProps={defaultProps} presentation={presentation} navigate={navigate} locale={locale}>\n      <ThemeProvider themes={themes} currentThemeId={currentThemeId}>\n        {/* Around the portal slot too, so the scrollbars of the panels in it are styled as well. */}\n        {isNested ? children : scrollbars === false ? root : <ScrollbarProvider>{root}</ScrollbarProvider>}\n      </ThemeProvider>\n    </ConfigProvider>\n  );\n}\n\nexport default AhroweProvider;\n",".alert {\n  display: flex;\n  align-items: flex-start;\n  gap: 0.625em;\n  padding: 0.75em 0.875em;\n  border-radius: var(--default-border-radius);\n  border-left: 0.1875em solid var(--tone);\n  background: var(--background-accent);\n\n  /* Must follow the shorthand above, which resets it. The gradient reaches over border-left only\n     because its origin is the border box; the border is the fallback. */\n  background-image: var(--tone-gradient);\n  background-repeat: no-repeat;\n  background-size: 0.1875em 100%;\n  background-origin: border-box;\n  color: var(--text-color);\n  font-family: var(--default-font);\n  box-sizing: border-box;\n\n  &-icon {\n    flex-shrink: 0;\n    color: var(--tone);\n    margin-top: 0.1333em; /* 2px against this element's own 0.9375em */\n    font-size: 0.9375em;\n  }\n\n  &-content {\n    flex: 1;\n    font-size: 0.875em;\n    line-height: 1.4;\n  }\n\n  &-title {\n    font-weight: var(--header-font-weight, 600);\n\n    &:not(:only-child) {\n      margin-bottom: 0.143em;\n    }\n  }\n\n  &-description {\n    color: var(--text-dark);\n  }\n\n  /* Two classes, to outrank Button's font-size and ActionIcon's box whichever stylesheet loads\n     first. */\n  & &-closeButton {\n    flex-shrink: 0;\n    width: 1em;\n    height: 1em;\n    font-size: 0.8125em;\n    opacity: 0.6;\n    transition: opacity 0.15s;\n    margin-top: 0.1538em; /* 2px against this element's own 0.8125em */\n\n    &:hover {\n      opacity: 1;\n    }\n  }\n}\n","import type { ReactElement } from 'react';\nimport cx from 'classnames';\nimport {\n  faCircleInfo,\n  faCircleCheck,\n  faCircleXmark,\n  faTriangleExclamation,\n  faBell,\n  faXmark,\n} from '@fortawesome/free-solid-svg-icons';\n\nimport type { AlertLabels, AlertProps } from './alert.types';\nimport styles from './alert.module.pcss';\nimport ActionIcon from '../actionIcon';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport { renderIcon } from '../utils/renderIcon';\nimport { ThemeColor } from '../themeProvider/color.types';\nimport { toneProps, useToneCheck } from '../styles/tone';\n\n// A Map, so a CSS colour can never hit an Object.prototype key.\nconst ICON_BY_COLOR = new Map<string, typeof faBell>([\n  [ThemeColor.Info, faCircleInfo],\n  [ThemeColor.Success, faCircleCheck],\n  [ThemeColor.Warn, faTriangleExclamation],\n  [ThemeColor.Error, faCircleXmark],\n]);\n\nconst DEFAULT_LABELS: Required<AlertLabels> = {\n  dismiss: 'Dismiss',\n};\n\nfunction Alert(props: AlertProps): ReactElement {\n  const {\n    title,\n    children = null,\n    color = ThemeColor.Primary,\n    icon,\n    hideIcon = false,\n    onClose,\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    labels,\n    ...rest\n  } = useComponentDefaults('Alert', props);\n  const text = useLabels('Alert', labels, DEFAULT_LABELS);\n  const tone = toneProps(color);\n  useToneCheck('Alert', [color]);\n\n  return (\n    <div\n      {...rest}\n      role='alert'\n      className={cx(styles.alert, tone.className, classNames?.root, className)}\n      style={{ ...tone.style, ...style, ...slotStyles?.root }}\n    >\n      {!hideIcon &&\n        renderIcon(icon ?? ICON_BY_COLOR.get(color) ?? faBell, {\n          className: cx(styles.alertIcon, classNames?.icon),\n          style: slotStyles?.icon,\n        })}\n\n      <div className={cx(styles.alertContent, classNames?.content)} style={slotStyles?.content}>\n        {title && (\n          <div className={cx(styles.alertTitle, classNames?.title)} style={slotStyles?.title}>\n            {title}\n          </div>\n        )}\n        {children && (\n          <div className={cx(styles.alertDescription, classNames?.description)} style={slotStyles?.description}>\n            {children}\n          </div>\n        )}\n      </div>\n\n      {onClose && (\n        <ActionIcon\n          icon={faXmark}\n          color='var(--text-dark)'\n          onClick={onClose}\n          className={cx(styles.alertCloseButton, classNames?.closeButton)}\n          style={slotStyles?.closeButton}\n          aria-label={text.dismiss}\n        />\n      )}\n    </div>\n  );\n}\n\nexport default Alert;\n",".animatedLogo {\n  display: inline-block;\n  overflow: visible;\n\n  &-path {\n    stroke: var(--primary-color);\n    fill: var(--primary-color);\n    fill-opacity: 0;\n  }\n\n  &-pathLoop {\n    animation-name: animatedLogoRevealLoop;\n    animation-timing-function: cubic-bezier(0.65, 0, 0.35, 1);\n    animation-iteration-count: infinite;\n  }\n\n  &-pathOnce {\n    animation-name: animatedLogoRevealOnce;\n    animation-timing-function: cubic-bezier(0.65, 0, 0.35, 1);\n    animation-iteration-count: 1;\n    animation-fill-mode: forwards;\n  }\n\n  &-pathLoopNoFill {\n    animation-name: animatedLogoRevealLoopNoFill;\n    animation-timing-function: cubic-bezier(0.65, 0, 0.35, 1);\n    animation-iteration-count: infinite;\n  }\n\n  &-pathOnceNoFill {\n    animation-name: animatedLogoRevealOnceNoFill;\n    animation-timing-function: cubic-bezier(0.65, 0, 0.35, 1);\n    animation-iteration-count: 1;\n    animation-fill-mode: forwards;\n  }\n\n  &-spinLoop,\n  &-spinOnce,\n  &-scaleLoop,\n  &-scaleOnce {\n    display: inline-block;\n    transform-box: fill-box;\n    transform-origin: center;\n    animation-timing-function: cubic-bezier(0.65, 0, 0.35, 1);\n  }\n\n  &-spinLoop {\n    animation-name: animatedLogoRotateLoop;\n    animation-iteration-count: infinite;\n  }\n\n  &-spinOnce {\n    animation-name: animatedLogoRotateOnce;\n    animation-iteration-count: 1;\n    animation-fill-mode: forwards;\n  }\n\n  &-scaleLoop {\n    animation-name: animatedLogoGrowLoop;\n    animation-iteration-count: infinite;\n  }\n\n  &-scaleOnce {\n    animation-name: animatedLogoGrowOnce;\n    animation-iteration-count: 1;\n    animation-fill-mode: forwards;\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    &-pathLoop,\n    &-pathOnce {\n      animation: none;\n      fill-opacity: 1;\n      stroke-dashoffset: 0;\n    }\n\n    &-pathLoopNoFill,\n    &-pathOnceNoFill {\n      animation: none;\n      stroke-dashoffset: 0;\n    }\n\n    &-spinLoop,\n    &-spinOnce,\n    &-scaleLoop,\n    &-scaleOnce {\n      animation: none;\n    }\n  }\n}\n\n@keyframes animatedLogoRevealLoop {\n  0% {\n    stroke-dashoffset: var(--animated-logo-length);\n    fill-opacity: 0;\n  }\n\n  38% {\n    stroke-dashoffset: 0;\n    fill-opacity: 0;\n  }\n\n  62% {\n    fill-opacity: 1;\n  }\n\n  86% {\n    fill-opacity: 1;\n  }\n\n  96% {\n    stroke-dashoffset: 0;\n    fill-opacity: 0;\n  }\n\n  100% {\n    stroke-dashoffset: var(--animated-logo-length);\n    fill-opacity: 0;\n  }\n}\n\n@keyframes animatedLogoRevealOnce {\n  0% {\n    stroke-dashoffset: var(--animated-logo-length);\n    fill-opacity: 0;\n  }\n\n  45% {\n    stroke-dashoffset: 0;\n    fill-opacity: 0;\n  }\n\n  100% {\n    stroke-dashoffset: 0;\n    fill-opacity: 1;\n  }\n}\n\n@keyframes animatedLogoRevealLoopNoFill {\n  0% {\n    stroke-dashoffset: var(--animated-logo-length);\n  }\n\n  38% {\n    stroke-dashoffset: 0;\n  }\n\n  86% {\n    stroke-dashoffset: 0;\n  }\n\n  100% {\n    stroke-dashoffset: var(--animated-logo-length);\n  }\n}\n\n@keyframes animatedLogoRevealOnceNoFill {\n  0% {\n    stroke-dashoffset: var(--animated-logo-length);\n  }\n\n  45% {\n    stroke-dashoffset: 0;\n  }\n\n  100% {\n    stroke-dashoffset: 0;\n  }\n}\n\n@keyframes animatedLogoRotateLoop {\n  0% {\n    transform: rotate(180deg);\n  }\n\n  38% {\n    transform: rotate(0deg);\n  }\n\n  100% {\n    transform: rotate(0deg);\n  }\n}\n\n@keyframes animatedLogoRotateOnce {\n  0% {\n    transform: rotate(180deg);\n  }\n\n  45% {\n    transform: rotate(0deg);\n  }\n\n  100% {\n    transform: rotate(0deg);\n  }\n}\n\n@keyframes animatedLogoGrowLoop {\n  0% {\n    transform: scale(0);\n  }\n\n  38% {\n    transform: scale(1);\n  }\n\n  96% {\n    transform: scale(1);\n  }\n\n  100% {\n    transform: scale(0);\n  }\n}\n\n@keyframes animatedLogoGrowOnce {\n  0% {\n    transform: scale(0);\n  }\n\n  45% {\n    transform: scale(1);\n  }\n\n  100% {\n    transform: scale(1);\n  }\n}\n","import { useEffect, useRef, useState } from 'react';\nimport type { CSSProperties, ReactElement } from 'react';\nimport cx from 'classnames';\n\nimport type { AnimatedLogoProps } from './animatedLogo.types';\nimport styles from './animatedLogo.module.pcss';\nimport { cssColor, useToneCheck } from '../styles/tone';\n\nconst DEFAULT_PATH =\n  'M 1.292 0.278 C -0.41 4.855 3.185 6.433 0.344 8.916 Q 3.923 10.712 5.361 14.001 Q 6.128 14.398 6.876 14.003 Q 8.439 10.632 11.86 8.912 C 8.893 6.387 12.578 5.159 10.86 0.255 Q 8.655 1.166 7.391 3.377 Q 6.092 2.93 4.843 3.38 Q 3.246 0.883 1.292 0.278 Z M 1.663 1.28 Q 2.413 1.635 3.195 2.718 Q 2.083 2.778 1.595 3.884 Q 1.309 2.7 1.663 1.28 M 3.802 7.908 Q 4.426 8.069 3.284 9.003 Q 3.824 9.32 4.557 10.141 Q 4.812 10.869 4.849 11.671 Q 4.371 10.09 2.642 9.144 Q 2.438 9.033 2.609 8.922 Q 3.229 8.537 3.802 7.908 M 8.469 7.927 Q 8.955 8.469 9.646 8.926 Q 9.791 9.032 9.647 9.136 Q 7.821 10.157 7.389 11.702 Q 7.464 10.869 7.709 10.119 Q 8.219 9.53 8.956 9.025 Q 7.81 7.927 8.469 7.927';\n\nconst DEFAULT_VIEW_BOX = '0.244 0.155 11.72 14.25';\n\nfunction AnimatedLogo(props: AnimatedLogoProps): ReactElement {\n  const {\n    path = DEFAULT_PATH,\n    viewBox = DEFAULT_VIEW_BOX,\n    size = 96,\n    color,\n    duration = 4.2,\n    loop = true,\n    fill = true,\n    spin = false,\n    scale = false,\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    ...rest\n  } = props;\n  useToneCheck('AnimatedLogo', [color]);\n\n  const pathRef = useRef<SVGPathElement>(null);\n  const [ready, setReady] = useState(false);\n\n  useEffect(() => {\n    const pathEl = pathRef.current;\n    if (!pathEl) return;\n\n    setReady(false);\n    const length = pathEl.getTotalLength();\n    pathEl.style.setProperty('--animated-logo-length', String(length));\n    pathEl.style.strokeDasharray = String(length);\n    pathEl.style.strokeDashoffset = String(length);\n\n    const frame = requestAnimationFrame(() => setReady(true));\n    return () => cancelAnimationFrame(frame);\n  }, [path]);\n\n  const pathStyle: CSSProperties = {\n    animationDuration: `${duration}s`,\n    ...(color ? { stroke: cssColor(color), fill: cssColor(color) } : undefined),\n    ...slotStyles?.path,\n  };\n\n  return (\n    <svg\n      {...rest}\n      className={cx(styles.animatedLogo, className)}\n      style={{ width: size, height: size, ...style }}\n      viewBox={viewBox}\n      xmlns='http://www.w3.org/2000/svg'\n    >\n      <g\n        className={cx(ready && scale && (loop ? styles.animatedLogoScaleLoop : styles.animatedLogoScaleOnce))}\n        style={{ animationDuration: `${duration}s` }}\n      >\n        <g\n          className={cx(ready && spin && (loop ? styles.animatedLogoSpinLoop : styles.animatedLogoSpinOnce))}\n          style={{ animationDuration: `${duration}s` }}\n        >\n          <path\n            ref={pathRef}\n            className={cx(\n              styles.animatedLogoPath,\n              ready &&\n                (loop\n                  ? fill\n                    ? styles.animatedLogoPathLoop\n                    : styles.animatedLogoPathLoopNoFill\n                  : fill\n                    ? styles.animatedLogoPathOnce\n                    : styles.animatedLogoPathOnceNoFill),\n              classNames?.path,\n            )}\n            style={pathStyle}\n            strokeWidth={0.1}\n            strokeLinejoin='round'\n            strokeLinecap='round'\n            d={path}\n          />\n        </g>\n      </g>\n    </svg>\n  );\n}\n\nexport default AnimatedLogo;\n","import type { CSSProperties } from 'react';\nimport type { SlotClassNames, SlotStyles, HtmlProps } from '../types/slots.types';\n\n/** Which animation plays across the text. */\nexport enum AnimatedTextVariant {\n  /** Each unit rises up from below its baseline, staggered left to right. */\n  RiseUp = 'rise-up',\n  /** Each unit fades in from a blur, staggered left to right. */\n  BlurIn = 'blur-in',\n  /** Each unit drops in from above with a spring overshoot, staggered left to right. */\n  Bounce = 'bounce',\n  /** Each unit pops in in order, like it's being typed, with a blinking cursor at the end. */\n  Typewriter = 'typewriter',\n  /** Each unit resolves from random characters to its real value, staggered left to right. */\n  Scramble = 'scramble',\n  /** Each unit briefly RGB-splits and jitters before settling, staggered left to right. */\n  Glitch = 'glitch',\n  /** Every unit continuously bobs up and down in a travelling wave, on a loop. */\n  Wave = 'wave',\n  /** A highlight sweeps across the text, left to right, on a loop. */\n  Shine = 'shine',\n  /** A marker-style highlight block wipes in behind the whole string, once, left to right. */\n  Highlight = 'highlight',\n}\n\n/**\n * What gets staggered and animated as one entrance unit. Applies to every variant except\n * `Shine` and `Highlight`, which always animate the whole string as a single run.\n */\nexport enum AnimatedTextUnit {\n  /** Every letter animates on its own. */\n  Letter = 'letter',\n  /** Every word animates as a whole block. */\n  Word = 'word',\n}\n\nexport type AnimatedTextSlots = 'letter' | 'cursor';\n\nexport interface AnimatedTextProps extends HtmlProps {\n  /** The text to animate. Split and animated one unit at a time for every variant but `Shine`/`Highlight`. */\n  text: string;\n  /** Which animation plays (default `RiseUp`). */\n  variant?: AnimatedTextVariant;\n  /** Whether entrance/loop variants stagger by letter or by word (default `Letter`). Ignored by `Shine`/`Highlight`. */\n  unit?: AnimatedTextUnit;\n  /**\n   * Delay in ms between each unit's animation start. Applies to every variant except `Shine`\n   * and `Highlight`, which animate as a single run (default `40`).\n   */\n  staggerDelay?: number;\n  /**\n   * Duration in ms of one animation cycle: a single unit's entrance for `RiseUp`/`BlurIn`/\n   * `Bounce`/`Glitch` (default `500`), one bob cycle for `Wave` (default `1200`), one sweep of\n   * the highlight for `Shine` (default `2000`), or the one-shot wipe-in for `Highlight` (default\n   * `600`). Ignored by `Typewriter` and `Scramble`, since each unit resolves instantly; use\n   * `staggerDelay` to control their pacing.\n   */\n  duration?: number;\n  /**\n   * When `true` (default), the animation doesn't start until the component first scrolls into\n   * the viewport. Set to `false` to play immediately on mount, e.g. for text already in view\n   * above the fold.\n   */\n  triggerOnView?: boolean;\n\n  className?: string;\n  style?: CSSProperties;\n  classNames?: SlotClassNames<AnimatedTextSlots>;\n  styles?: SlotStyles<AnimatedTextSlots>;\n}\n",".animatedText {\n  display: inline-block;\n  position: relative;\n\n  &-visually-hidden {\n    position: absolute;\n    width: 1px;\n    height: 1px;\n    margin: -1px;\n    padding: 0;\n    overflow: hidden;\n    clip: rect(0, 0, 0, 0);\n    white-space: nowrap;\n    border: 0;\n  }\n\n  &-letter {\n    display: inline-block;\n    white-space: pre;\n    opacity: 0;\n    animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1);\n    animation-fill-mode: forwards;\n  }\n\n  &-variant {\n    &-rise-up {\n      animation-name: animatedTextRiseUp;\n    }\n\n    &-blur-in {\n      animation-name: animatedTextBlurIn;\n    }\n\n    &-bounce {\n      animation-name: animatedTextBounceIn;\n      animation-timing-function: ease-out;\n    }\n\n    &-glitch {\n      animation-name: animatedTextGlitch;\n      animation-timing-function: steps(5, jump-none);\n    }\n\n    &-wave {\n      /* Ambient/looping, not an entrance, so the letter stays visible throughout, overriding\n         `.animatedText-letter`'s opacity:0 base (meant for one-shot entrance variants). */\n      opacity: 1;\n      animation-name: animatedTextWave;\n      animation-iteration-count: infinite;\n      animation-timing-function: ease-in-out;\n    }\n  }\n\n  &-typewriter-letter {\n    display: inline-block;\n    white-space: pre;\n  }\n\n  &-scramble-letter {\n    display: inline-block;\n    white-space: pre;\n  }\n\n  &-cursor {\n    display: inline-block;\n    width: 0.08em;\n    min-width: 1px;\n    height: 0.85em;\n    margin-left: 0.05em;\n    background: var(--text-color);\n    vertical-align: baseline;\n\n    &-blinking {\n      animation: animatedTextCursorBlink 1s step-end infinite;\n    }\n  }\n\n  &-shine {\n    display: inline-block;\n    white-space: pre;\n\n    /* currentColor can't be used here: it would resolve against this same rule's\n       `color: transparent` below, making the whole gradient transparent. */\n    background-image: linear-gradient(90deg, var(--text-color) 25%, var(--primary-lighter) 50%, var(--text-color) 75%);\n    background-size: 200% 100%;\n    background-clip: text;\n    -webkit-background-clip: text;\n    color: transparent;\n    -webkit-text-fill-color: transparent;\n    animation-name: animatedTextShineSweep;\n    animation-timing-function: linear;\n    animation-iteration-count: infinite;\n  }\n\n  &-highlight {\n    display: inline-block;\n    white-space: pre;\n    background-image: linear-gradient(var(--primary-lighter), var(--primary-lighter));\n    background-repeat: no-repeat;\n    background-position: left center;\n    background-size: 0% 100%;\n    animation-name: animatedTextHighlightSweep;\n    animation-timing-function: ease-out;\n    animation-fill-mode: forwards;\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    &-letter {\n      animation: none;\n      opacity: 1;\n    }\n\n    &-shine {\n      animation: none;\n      background-image: none;\n      color: currentcolor;\n      -webkit-text-fill-color: currentcolor;\n    }\n\n    &-highlight {\n      animation: none;\n      background-size: 100% 100%;\n    }\n\n    &-cursor-blinking {\n      animation: none;\n      opacity: 1;\n    }\n  }\n}\n\n@keyframes animatedTextRiseUp {\n  from {\n    opacity: 0;\n    transform: translateY(0.6em);\n  }\n\n  to {\n    opacity: 1;\n    transform: translateY(0);\n  }\n}\n\n@keyframes animatedTextBlurIn {\n  from {\n    opacity: 0;\n    filter: blur(8px);\n  }\n\n  to {\n    opacity: 1;\n    filter: blur(0);\n  }\n}\n\n@keyframes animatedTextBounceIn {\n  0% {\n    opacity: 0;\n    transform: translateY(-0.8em);\n  }\n\n  60% {\n    opacity: 1;\n    transform: translateY(0.15em);\n  }\n\n  80% {\n    transform: translateY(-0.05em);\n  }\n\n  100% {\n    opacity: 1;\n    transform: translateY(0);\n  }\n}\n\n@keyframes animatedTextGlitch {\n  0% {\n    opacity: 0;\n    transform: translate(0, 0);\n    text-shadow: none;\n  }\n\n  15% {\n    opacity: 1;\n    transform: translate(-0.05em, 0.03em);\n    text-shadow:\n      0.06em 0 var(--error-color),\n      -0.06em 0 var(--info-color);\n  }\n\n  30% {\n    transform: translate(0.05em, -0.03em);\n    text-shadow:\n      -0.06em 0 var(--error-color),\n      0.06em 0 var(--info-color);\n  }\n\n  45% {\n    transform: translate(-0.03em, 0);\n    text-shadow:\n      0.03em 0 var(--error-color),\n      -0.03em 0 var(--info-color);\n  }\n\n  60%,\n  100% {\n    opacity: 1;\n    transform: translate(0, 0);\n    text-shadow: none;\n  }\n}\n\n@keyframes animatedTextWave {\n  0%,\n  100% {\n    transform: translateY(0);\n  }\n\n  50% {\n    transform: translateY(-0.25em);\n  }\n}\n\n@keyframes animatedTextShineSweep {\n  0% {\n    background-position: 200% 0;\n  }\n\n  100% {\n    background-position: -200% 0;\n  }\n}\n\n@keyframes animatedTextHighlightSweep {\n  from {\n    background-size: 0% 100%;\n  }\n\n  to {\n    background-size: 100% 100%;\n  }\n}\n\n@keyframes animatedTextCursorBlink {\n  0%,\n  50% {\n    opacity: 1;\n  }\n\n  50.01%,\n  100% {\n    opacity: 0;\n  }\n}\n","import { Fragment, useEffect, useMemo, useRef, useState, type ReactElement } from 'react';\nimport cx from 'classnames';\n\nimport { AnimatedTextVariant, AnimatedTextUnit } from './animatedText.types';\nimport type { AnimatedTextProps } from './animatedText.types';\nimport styles from './animatedText.module.pcss';\n\nfunction prefersReducedMotion(): boolean {\n  return typeof window !== 'undefined' && typeof window.matchMedia === 'function'\n    ? window.matchMedia('(prefers-reduced-motion: reduce)').matches\n    : false;\n}\n\n// CSS-driven one-shot entrance variants: identical rendering, only the keyframe (via this class)\n// differs. RiseUp/BlurIn/Bounce/Glitch all animate opacity 0 -> 1 with `animation-fill-mode:\n// forwards`, staggered per unit via `animation-delay`.\nconst ENTRANCE_VARIANT_CLASS: Partial<Record<AnimatedTextVariant, string>> = {\n  [AnimatedTextVariant.RiseUp]: styles.animatedTextVariantRiseUp,\n  [AnimatedTextVariant.BlurIn]: styles.animatedTextVariantBlurIn,\n  [AnimatedTextVariant.Bounce]: styles.animatedTextVariantBounce,\n  [AnimatedTextVariant.Glitch]: styles.animatedTextVariantGlitch,\n};\n\nfunction splitUnits(text: string, unit: AnimatedTextUnit): string[] {\n  return unit === AnimatedTextUnit.Word ? text.split(' ').filter(Boolean) : Array.from(text);\n}\n\ninterface TypewriterProps {\n  units: string[];\n  unit: AnimatedTextUnit;\n  staggerDelay: number;\n  classNames: AnimatedTextProps['classNames'];\n  slotStyles: AnimatedTextProps['styles'];\n}\n\n// Split out so its wrapper (keyed by `replayKey`, see below) can be remounted on prop change to\n// reset this component's reveal state, the idiomatic React way to reset state on prop change.\n// It also keeps the reveal-count updates that happen over time (inside the interval callback) as\n// the only setState calls, none of them synchronous-in-effect.\nfunction Typewriter({ units, unit, staggerDelay, classNames, slotStyles }: TypewriterProps) {\n  const [revealCount, setRevealCount] = useState(() => {\n    if (units.length === 0) return 0;\n    return prefersReducedMotion() ? units.length : 1;\n  });\n\n  useEffect(() => {\n    if (units.length <= 1 || prefersReducedMotion()) return;\n    let revealed = 1;\n    const id = setInterval(() => {\n      revealed += 1;\n      setRevealCount(revealed);\n      if (revealed >= units.length) clearInterval(id);\n    }, staggerDelay);\n    return () => clearInterval(id);\n  }, [units, staggerDelay]);\n\n  const isDone = revealCount >= units.length;\n\n  return (\n    <>\n      {units.slice(0, revealCount).map((unitText, index) => (\n        <Fragment key={index}>\n          <span className={cx(styles.animatedTextTypewriterLetter, classNames?.letter)} style={slotStyles?.letter}>\n            {unitText}\n          </span>\n          {unit === AnimatedTextUnit.Word && index < revealCount - 1 ? ' ' : null}\n        </Fragment>\n      ))}\n      <span\n        className={cx(styles.animatedTextCursor, isDone && styles.animatedTextCursorBlinking, classNames?.cursor)}\n        style={slotStyles?.cursor}\n      />\n    </>\n  );\n}\n\nconst SCRAMBLE_CHARSET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%&*';\nconst SCRAMBLE_TICK_MS = 35;\n\nfunction randomScrambleText(length: number): string {\n  return Array.from({ length }, () => SCRAMBLE_CHARSET[Math.floor(Math.random() * SCRAMBLE_CHARSET.length)]).join('');\n}\n\ninterface ScrambleProps {\n  units: string[];\n  unit: AnimatedTextUnit;\n  staggerDelay: number;\n  classNames: AnimatedTextProps['classNames'];\n  slotStyles: AnimatedTextProps['styles'];\n}\n\n// Same keyed-remount-on-prop-change approach as Typewriter. Every unit starts scrambled and\n// \"locks in\" left to right; the scramble refresh (SCRAMBLE_TICK_MS) is a separate, faster cadence\n// than the per-unit settle pacing (staggerDelay) so still-scrambling units keep visibly flickering.\nfunction Scramble({ units, unit, staggerDelay, classNames, slotStyles }: ScrambleProps) {\n  const [reduceMotion] = useState(prefersReducedMotion);\n  const [tick, setTick] = useState(0);\n\n  useEffect(() => {\n    if (units.length === 0 || reduceMotion) return;\n    let t = 0;\n    const totalTicks = Math.ceil((units.length * staggerDelay) / SCRAMBLE_TICK_MS) + 1;\n    const id = setInterval(() => {\n      t += 1;\n      setTick(t);\n      if (t >= totalTicks) clearInterval(id);\n    }, SCRAMBLE_TICK_MS);\n    return () => clearInterval(id);\n  }, [units, staggerDelay, reduceMotion]);\n\n  const settledCount = Math.min(units.length, Math.floor((tick * SCRAMBLE_TICK_MS) / staggerDelay));\n\n  return (\n    <>\n      {units.map((unitText, index) => {\n        const isSettled = reduceMotion || index < settledCount;\n        return (\n          <Fragment key={index}>\n            <span className={cx(styles.animatedTextScrambleLetter, classNames?.letter)} style={slotStyles?.letter}>\n              {unitText === ' ' ? ' ' : isSettled ? unitText : randomScrambleText(unitText.length)}\n            </span>\n            {unit === AnimatedTextUnit.Word && index < units.length - 1 ? ' ' : null}\n          </Fragment>\n        );\n      })}\n    </>\n  );\n}\n\nfunction AnimatedText({\n  text,\n  variant = AnimatedTextVariant.RiseUp,\n  unit = AnimatedTextUnit.Letter,\n  staggerDelay = 40,\n  duration,\n  triggerOnView = true,\n  className,\n  style,\n  classNames,\n  styles: slotStyles,\n  ...rest\n}: AnimatedTextProps): ReactElement {\n  const units = useMemo(() => splitUnits(text, unit), [text, unit]);\n  const rootRef = useRef<HTMLSpanElement>(null);\n\n  // When `triggerOnView` is on, the animated subtree stays unmounted (see `isVisible` below)\n  // until the root first scrolls into the viewport, so the entrance/loop only plays once someone\n  // can actually see it instead of burning its one-shot animation off-screen on mount. Falls back\n  // to immediately visible when IntersectionObserver isn't available (SSR, old browsers, jsdom).\n  const [isVisible, setIsVisible] = useState(() => !triggerOnView || typeof IntersectionObserver === 'undefined');\n\n  useEffect(() => {\n    if (isVisible) return;\n    const node = rootRef.current;\n    if (!node) return;\n    const observer = new IntersectionObserver(([entry]) => {\n      if (entry?.isIntersecting) {\n        setIsVisible(true);\n        observer.disconnect();\n      }\n    });\n    observer.observe(node);\n    return () => observer.disconnect();\n  }, [isVisible]);\n\n  // Forces the animated subtree (not the whole component) to remount whenever anything that\n  // affects the animation changes, most commonly `text`. Without this, CSS entrance variants\n  // rely on `animation-fill-mode: forwards`, which just holds the prior end state instead of\n  // replaying when the letters underneath it change; keying on these values makes every variant\n  // (CSS-driven and JS-driven alike) replay consistently, matching what a component named\n  // AnimatedText should do when the text it's animating actually changes.\n  const replayKey = `${variant} ${text} ${unit} ${staggerDelay} ${duration ?? ''}`;\n\n  let content: ReactElement;\n\n  if (!isVisible) {\n    // Reserves the same layout space as the real content without playing anything yet.\n    content = (\n      <span aria-hidden='true' style={{ opacity: 0 }}>\n        {text}\n      </span>\n    );\n  } else if (variant === AnimatedTextVariant.Shine) {\n    content = (\n      <span\n        key={replayKey}\n        aria-hidden='true'\n        className={cx(styles.animatedTextShine, classNames?.letter)}\n        style={{ animationDuration: `${duration ?? 2000}ms`, ...slotStyles?.letter }}\n      >\n        {text}\n      </span>\n    );\n  } else if (variant === AnimatedTextVariant.Highlight) {\n    content = (\n      <span\n        key={replayKey}\n        aria-hidden='true'\n        className={cx(styles.animatedTextHighlight, classNames?.letter)}\n        style={{ animationDuration: `${duration ?? 600}ms`, ...slotStyles?.letter }}\n      >\n        {text}\n      </span>\n    );\n  } else if (variant === AnimatedTextVariant.Typewriter) {\n    content = (\n      <span key={replayKey} aria-hidden='true'>\n        <Typewriter\n          units={units}\n          unit={unit}\n          staggerDelay={staggerDelay}\n          classNames={classNames}\n          slotStyles={slotStyles}\n        />\n      </span>\n    );\n  } else if (variant === AnimatedTextVariant.Scramble) {\n    content = (\n      <span key={replayKey} aria-hidden='true'>\n        <Scramble\n          units={units}\n          unit={unit}\n          staggerDelay={staggerDelay}\n          classNames={classNames}\n          slotStyles={slotStyles}\n        />\n      </span>\n    );\n  } else if (variant === AnimatedTextVariant.Wave) {\n    content = (\n      <span key={replayKey} aria-hidden='true'>\n        {units.map((unitText, index) => (\n          <Fragment key={index}>\n            <span\n              className={cx(styles.animatedTextLetter, styles.animatedTextVariantWave, classNames?.letter)}\n              style={{\n                // Negative delay starts the loop already mid-cycle, phase-shifted per unit.\n                // That's what makes it read as a travelling wave from the very first frame,\n                // instead of every unit bobbing in lockstep or staggering in once and then syncing.\n                animationDelay: `-${index * staggerDelay}ms`,\n                animationDuration: `${duration ?? 1200}ms`,\n                ...slotStyles?.letter,\n              }}\n            >\n              {unitText === ' ' ? ' ' : unitText}\n            </span>\n            {unit === AnimatedTextUnit.Word && index < units.length - 1 ? ' ' : null}\n          </Fragment>\n        ))}\n      </span>\n    );\n  } else {\n    const entranceClass = ENTRANCE_VARIANT_CLASS[variant] ?? styles.animatedTextVariantRiseUp;\n    content = (\n      <span key={replayKey} aria-hidden='true'>\n        {units.map((unitText, index) => (\n          <Fragment key={index}>\n            <span\n              className={cx(styles.animatedTextLetter, entranceClass, classNames?.letter)}\n              style={{\n                animationDelay: `${index * staggerDelay}ms`,\n                animationDuration: `${duration ?? 500}ms`,\n                ...slotStyles?.letter,\n              }}\n            >\n              {unitText === ' ' ? ' ' : unitText}\n            </span>\n            {unit === AnimatedTextUnit.Word && index < units.length - 1 ? ' ' : null}\n          </Fragment>\n        ))}\n      </span>\n    );\n  }\n\n  return (\n    <span {...rest} ref={rootRef} className={cx(styles.animatedText, className)} style={style}>\n      <span className={styles.animatedTextVisuallyHidden}>{text}</span>\n      {content}\n    </span>\n  );\n}\n\nexport default AnimatedText;\n","import type { CSSProperties, ReactNode } from 'react';\nimport type { SlotClassNames, SlotStyles, HtmlProps } from '../types/slots.types';\nimport type { ColorValue } from '../themeProvider/color.types';\n\n/** How much the badge stands out. Independent of its colour; a `dot` is always solid. */\nexport enum BadgeStyleType {\n  /** Solid fill. The default. */\n  Filled = 'filled',\n  /** A light tint of the colour behind text in the colour. */\n  Light = 'light',\n  /** Border and text in the colour, no fill. */\n  Outlined = 'outlined',\n}\n\nexport enum BadgePosition {\n  TopRight = 'top-right',\n  TopLeft = 'top-left',\n  BottomRight = 'bottom-right',\n  BottomLeft = 'bottom-left',\n}\n\nexport type BadgeSlots = 'badge';\n\nexport interface BadgeProps extends Omit<HtmlProps, 'content'> {\n  count?: number;\n  max?: number;\n  content?: ReactNode;\n  dot?: boolean;\n  showZero?: boolean;\n  styleType?: BadgeStyleType;\n  /** A `ThemeColor` (default `Primary`) or any CSS colour, such as `'var(--brand)'`. */\n  color?: ColorValue;\n  position?: BadgePosition;\n  children?: ReactNode;\n  className?: string;\n  style?: CSSProperties;\n  classNames?: SlotClassNames<BadgeSlots>;\n  styles?: SlotStyles<BadgeSlots>;\n}\n",".wrapper {\n  position: relative;\n  display: inline-flex;\n  align-items: center;\n  isolation: isolate;\n}\n\n.badge {\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  box-sizing: border-box;\n  min-width: 1.5em;\n  height: 1.5em;\n  padding: 0 0.375em;\n  border-radius: var(--default-border-radius);\n  font-size: 0.75em;\n  font-weight: 600;\n  line-height: 1;\n  white-space: nowrap;\n\n  /* Always solid: too small for a tint, an outline or a gradient. */\n  &-dot {\n    min-width: 0.8em;\n    width: 0.8em;\n    height: 0.8em;\n    padding: 0;\n    border-radius: 100%;\n    background: var(--tone);\n\n    &.badge-position-top-right {\n      transform: translate(20%, -20%);\n    }\n\n    &.badge-position-top-left {\n      transform: translate(-20%, -20%);\n    }\n\n    &.badge-position-bottom-right {\n      transform: translate(20%, 20%);\n    }\n\n    &.badge-position-bottom-left {\n      transform: translate(-20%, 20%);\n    }\n  }\n\n  /* An opaque page-coloured base under the style, or the content shows through Light, Outlined\n     and Neutral's fill. */\n  &-overlay {\n    position: absolute;\n    z-index: 1;\n\n    &.badge-style-filled {\n      background: var(--tone-gradient), linear-gradient(var(--tone-fill), var(--tone-fill)), var(--background);\n    }\n\n    &.badge-style-light {\n      background: linear-gradient(var(--tone-light), var(--tone-light)), var(--background);\n    }\n\n    &.badge-style-outlined {\n      background: var(--background);\n    }\n  }\n\n  &-position {\n    &-top-right {\n      top: 0;\n      right: 0;\n      transform: translate(50%, -50%);\n    }\n\n    &-top-left {\n      top: 0;\n      left: 0;\n      transform: translate(-50%, -50%);\n    }\n\n    &-bottom-right {\n      bottom: 0;\n      right: 0;\n      transform: translate(50%, 50%);\n    }\n\n    &-bottom-left {\n      bottom: 0;\n      left: 0;\n      transform: translate(-50%, 50%);\n    }\n  }\n\n  &-style {\n    &-filled {\n      background: var(--tone-fill);\n\n      /* Must follow the shorthand above, which resets background-image. */\n      background-image: var(--tone-gradient);\n      color: var(--tone-contrast);\n    }\n\n    &-light {\n      background: var(--tone-light);\n      color: var(--tone-text);\n    }\n\n    /* An inset shadow rather than a border, so an outlined badge is exactly as big as the rest. */\n    &-outlined {\n      box-shadow: inset 0 0 0 1px var(--tone-outline);\n      color: var(--tone-text);\n    }\n  }\n}\n","import type { ReactElement } from 'react';\nimport cx from 'classnames';\n\nimport { BadgeStyleType, BadgePosition } from './badge.types';\nimport type { BadgeProps } from './badge.types';\nimport styles from './badge.module.pcss';\nimport { useComponentDefaults } from '../configProvider';\nimport { ThemeColor } from '../themeProvider/color.types';\nimport { toneProps, useToneCheck } from '../styles/tone';\n\nconst CLASS_BY_STYLE: Record<BadgeStyleType, string> = {\n  [BadgeStyleType.Filled]: styles.badgeStyleFilled,\n  [BadgeStyleType.Light]: styles.badgeStyleLight,\n  [BadgeStyleType.Outlined]: styles.badgeStyleOutlined,\n};\n\nconst CLASS_BY_POSITION: Record<BadgePosition, string> = {\n  [BadgePosition.TopRight]: styles.badgePositionTopRight,\n  [BadgePosition.TopLeft]: styles.badgePositionTopLeft,\n  [BadgePosition.BottomRight]: styles.badgePositionBottomRight,\n  [BadgePosition.BottomLeft]: styles.badgePositionBottomLeft,\n};\n\nfunction Badge(props: BadgeProps): ReactElement {\n  const {\n    count,\n    max = 99,\n    content,\n    dot = false,\n    showZero = false,\n    styleType = BadgeStyleType.Filled,\n    color = ThemeColor.Primary,\n    position = BadgePosition.TopRight,\n    children = null,\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    ...rest\n  } = useComponentDefaults('Badge', props);\n  const hasCount = typeof count === 'number';\n  const hasContent = content != null;\n  const isHiddenByZero = !dot && !hasContent && hasCount && count === 0 && !showZero;\n  const showBadge = (dot || hasContent || hasCount) && !isHiddenByZero;\n\n  const displayValue = hasContent ? content : hasCount ? (count > max ? `${max}+` : count) : null;\n  const isOverlay = children != null;\n  const tone = toneProps(color);\n  useToneCheck('Badge', [color]);\n\n  return (\n    <span {...rest} className={cx(styles.wrapper, className)} style={style}>\n      {children}\n      {showBadge && (\n        <span\n          className={cx(\n            styles.badge,\n            tone.className,\n            dot ? styles.badgeDot : CLASS_BY_STYLE[styleType],\n            isOverlay && styles.badgeOverlay,\n            isOverlay && CLASS_BY_POSITION[position],\n            classNames?.badge,\n          )}\n          style={{ ...tone.style, ...slotStyles?.badge }}\n        >\n          {!dot && displayValue}\n        </span>\n      )}\n    </span>\n  );\n}\n\nexport default Badge;\n",".avatar {\n  --avatar-size: 2.5em;\n  --custom-avatar-radius: var(--avatar-radius, var(--default-border-radius));\n\n  display: inline-flex;\n  width: var(--avatar-size);\n  height: var(--avatar-size);\n  flex-shrink: 0;\n  box-sizing: border-box;\n  vertical-align: middle;\n\n  &-image {\n    width: 100%;\n    height: 100%;\n    object-fit: cover;\n    display: block;\n    border-radius: var(--custom-avatar-radius);\n  }\n\n  &-fallback {\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    width: 100%;\n    height: 100%;\n    border-radius: var(--custom-avatar-radius);\n    background: var(--background-accent);\n    color: var(--text-dark);\n    font-size: calc(var(--avatar-size) * 0.4);\n    font-weight: 600;\n    user-select: none;\n    text-transform: uppercase;\n  }\n\n  &-status-badge {\n    border: 2px solid var(--background);\n    box-sizing: border-box;\n  }\n}\n\n.avatarGroup {\n  display: inline-flex;\n  align-items: center;\n\n  &-item {\n    display: inline-flex;\n    border-radius: calc(var(--avatar-radius, var(--default-border-radius)) + 3px);\n    border: 2px solid var(--background);\n    box-sizing: content-box;\n    margin-left: -0.5em;\n\n    &:first-child {\n      margin-left: 0;\n    }\n  }\n}\n","import { useState, type ReactElement } from 'react';\nimport cx from 'classnames';\nimport { faUser } from '@fortawesome/free-solid-svg-icons';\nimport Badge, { BadgePosition } from '../badge';\nimport { ThemeColor, type ColorValue } from '../themeProvider/color.types';\n\nimport type { AvatarProps, AvatarStatus } from './avatar.types';\nimport styles from './avatar.module.pcss';\nimport { renderIcon } from '../utils/renderIcon';\n\nconst STATUS_COLOR: Record<AvatarStatus, ColorValue> = {\n  online: ThemeColor.Success,\n  away: ThemeColor.Warn,\n  busy: ThemeColor.Error,\n  offline: 'var(--text-dark)',\n};\n\nfunction getInitials(name?: string): string {\n  if (!name) return '';\n  const parts = name.trim().split(/\\s+/).filter(Boolean);\n  if (parts.length === 0) return '';\n  if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();\n  return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();\n}\n\nfunction Avatar({\n  src,\n  alt,\n  name,\n  icon = faUser,\n  content,\n  status,\n  className,\n  style,\n  classNames,\n  styles: slotStyles,\n  ...rest\n}: AvatarProps): ReactElement {\n  const [hasError, setHasError] = useState(false);\n  const [prevSrc, setPrevSrc] = useState(src);\n\n  if (src !== prevSrc) {\n    setPrevSrc(src);\n    setHasError(false);\n  }\n\n  const initials = getInitials(name);\n  const showImage = !!src && !hasError;\n\n  const rendered = showImage ? (\n    <img\n      className={cx(styles.avatarImage, classNames?.image)}\n      style={slotStyles?.image}\n      src={src}\n      alt={alt ?? name ?? ''}\n      onError={() => setHasError(true)}\n    />\n  ) : (\n    <span className={cx(styles.avatarFallback, classNames?.fallback)} style={slotStyles?.fallback}>\n      {content ?? (initials || renderIcon(icon))}\n    </span>\n  );\n\n  if (!status) {\n    return (\n      <span {...rest} className={cx(styles.avatar, className)} style={style}>\n        {rendered}\n      </span>\n    );\n  }\n\n  return (\n    <Badge\n      {...rest}\n      dot\n      position={BadgePosition.BottomRight}\n      color={STATUS_COLOR[status]}\n      className={className}\n      style={style}\n      classNames={{ badge: cx(styles.avatarStatusBadge, classNames?.status) }}\n      styles={{ badge: slotStyles?.status }}\n    >\n      <span className={styles.avatar}>{rendered}</span>\n    </Badge>\n  );\n}\n\nexport default Avatar;\n","import { Children, type ReactElement } from 'react';\nimport cx from 'classnames';\n\nimport Avatar from './avatar';\nimport type { AvatarGroupProps } from './avatar.types';\nimport styles from './avatar.module.pcss';\n\nfunction AvatarGroup({\n  children,\n  max,\n  className,\n  style,\n  classNames,\n  styles: slotStyles,\n  ...rest\n}: AvatarGroupProps): ReactElement {\n  const items = Children.toArray(children);\n  const visible = max != null ? items.slice(0, max) : items;\n  const overflow = max != null ? items.length - max : 0;\n\n  return (\n    <span {...rest} className={cx(styles.avatarGroup, className)} style={style}>\n      {visible.map((child, index) => (\n        <span key={index} className={cx(styles.avatarGroupItem, classNames?.item)} style={slotStyles?.item}>\n          {child}\n        </span>\n      ))}\n      {overflow > 0 && (\n        <span className={cx(styles.avatarGroupItem, classNames?.overflow)} style={slotStyles?.overflow}>\n          <Avatar content={`+${overflow}`} />\n        </span>\n      )}\n    </span>\n  );\n}\n\nexport default AvatarGroup;\n",".interactableDiv {\n}\n","import cx from 'classnames';\nimport React, { useCallback, type KeyboardEvent, type MouseEvent, type ReactElement } from 'react';\n\nimport styles from './interactableDiv.module.pcss';\nimport type { DataProps } from '../types/baseProps.types';\nimport { isSelfActivation } from '../utils/activationKey';\nimport { isInteractiveTarget } from '../utils/interactiveTarget';\n\ninterface Properties extends DataProps, Omit<React.HTMLAttributes<HTMLDivElement>, 'tabIndex'> {\n  tabIndex?: number | null;\n  ref?: React.Ref<HTMLDivElement>;\n}\n\nfunction InteractableDiv({\n  className,\n  children,\n  onClick,\n  onKeyDown,\n  tabIndex,\n  ref,\n  ...props\n}: Properties): ReactElement {\n  const onClickHandler = useCallback(\n    (e: MouseEvent<HTMLDivElement>) => {\n      // The pointer half of `isSelfActivation`: while only keydown checked its target, a click\n      // into a card's text field counted as pressing the card and a Space typed into it did not.\n      if (isInteractiveTarget(e.target, e.currentTarget)) return;\n      onClick?.(e);\n    },\n    [onClick],\n  );\n\n  const onKeyDownHandler = useCallback(\n    (e: KeyboardEvent<HTMLDivElement>) => {\n      if (isSelfActivation(e)) {\n        if (e.key === ' ') e.preventDefault();\n        // A real click, the way a native button answers Enter and Space, rather\n        // than calling `onClick` directly: it arrives as a MouseEvent instead of\n        // a keyboard event wearing one, and it bubbles, so a parent that owns the\n        // click sees it too. That parent is how a `Popover` or `Menu` opens from\n        // a trigger that is not a button.\n        e.currentTarget.click();\n      }\n      onKeyDown?.(e);\n    },\n    [onKeyDown],\n  );\n\n  // Compared to undefined rather than using ??, so an explicit null still means \"no tabindex\"\n  // instead of falling through to the default.\n  return (\n    <div\n      className={cx(styles.interactableDiv, className)}\n      onClick={onClick ? onClickHandler : undefined}\n      tabIndex={tabIndex === undefined ? (onClick ? 0 : undefined) : (tabIndex ?? undefined)}\n      role={onClick ? 'button' : undefined}\n      onKeyDown={onKeyDownHandler}\n      ref={ref}\n      {...props}\n    >\n      {children}\n    </div>\n  );\n}\n\nexport default InteractableDiv;\n","import InteractableDiv from './interactableDiv';\n\nexport default InteractableDiv;\n","/* Neither of these two generates a box, so the trigger lands in the consumer's\n   layout exactly as if Popover were not around it: it is the flex item, the grid\n   item, the block-level child. A field that fills its column still fills it, a\n   short button is still only as wide as itself, and a row that stretches its\n   children reaches the trigger instead of stopping at a wrapper around it. They\n   stay in the event path, so the handlers on them still fire, and\n   `usePopoverPosition` already looks past boxless wrappers for its anchor.\n\n   Every box we tried here was wrong in one direction or the other. A wrapper that\n   spans its container opens the panel from the empty space beside a short button.\n   `width: fit-content` collapses a field whose own width is a percentage of its\n   parent. A flex root stretches the trigger's height but sizes its width by\n   content, which collapsed KlipyPicker's search field.\n\n   The cost is that `className`, `style` and `classNames.root` cannot paint\n   anything on these two, because there is nothing to paint on. */\n.popover {\n  display: contents;\n\n  &-trigger {\n    display: contents;\n  }\n\n  &-floating {\n    position: fixed;\n    top: 0;\n    left: 0;\n\n    /* Shared by every overlay, so the order they are portaled in, which is the order they opened in, decides. */\n    z-index: 1;\n\n    /* Hidden until usePopoverPosition writes the first coordinates, to avoid a top-left flash. */\n    visibility: hidden;\n  }\n\n  &-panel {\n    position: relative;\n\n    /* Above the arrow so the panel background hides the arrow's inner half — only the\n       tip pokes out past the panel edge. */\n    z-index: 1;\n    color: var(--text-color);\n    font-family: var(--default-font);\n    font-size: var(--default-font-size);\n    background: var(--background-modal);\n    box-shadow: var(--card-shadow);\n    border-radius: calc(var(--default-border-radius) + 4px);\n    padding: 12px 14px;\n\n    /* A readable measure for a popover of text. A panel that brings its own\n       width — a calendar, a media grid — turns it off with `constrainWidth`.\n       `.sheet-panel` overrides this by name for the sheet path; check there\n       before touching the cap. */\n    &-constrained {\n      max-width: min(320px, calc(100vw - 24px));\n    }\n\n    animation: popoverIn 0.12s ease-out forwards;\n  }\n\n  &-arrow {\n    position: absolute;\n    width: 10px;\n    height: 10px;\n    background: var(--background-modal);\n    box-shadow: var(--card-shadow);\n    transform: rotate(45deg);\n  }\n\n  /* Arrow sits on the panel edge facing the trigger; --arrow-x / --arrow-y centre it on the trigger. */\n  &-floating[data-placement='bottom'] &-arrow {\n    top: -5px;\n    left: var(--arrow-x, 50%);\n    margin-left: -5px;\n  }\n\n  &-floating[data-placement='top'] &-arrow {\n    bottom: -5px;\n    left: var(--arrow-x, 50%);\n    margin-left: -5px;\n  }\n\n  &-floating[data-placement='right'] &-arrow {\n    left: -5px;\n    top: var(--arrow-y, 50%);\n    margin-top: -5px;\n  }\n\n  &-floating[data-placement='left'] &-arrow {\n    right: -5px;\n    top: var(--arrow-y, 50%);\n    margin-top: -5px;\n  }\n\n  /* Grow from the edge nearest the trigger. */\n  &-floating[data-placement='bottom'] &-panel {\n    transform-origin: top center;\n  }\n\n  &-floating[data-placement='top'] &-panel {\n    transform-origin: bottom center;\n  }\n\n  &-floating[data-placement='right'] &-panel {\n    transform-origin: center left;\n  }\n\n  &-floating[data-placement='left'] &-panel {\n    transform-origin: center right;\n  }\n\n  @keyframes popoverIn {\n    from {\n      opacity: 0.4;\n      transform: scale(0.7);\n    }\n\n    to {\n      opacity: 1;\n      transform: scale(1);\n    }\n  }\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .popover-panel {\n    animation: none;\n  }\n}\n","/*\n * Bottom-sheet presentation, shared by every popup that can switch away from being anchored to\n * its trigger (see `useSheet`).\n *\n * Every selector is doubled (`.sheet.sheet`) on purpose: these classes land on the same elements\n * as a component's own single-class panel styles and have to win over them. Which of two\n * equal-specificity rules wins otherwise depends on the order the bundler happens to emit the\n * two CSS modules in, which is not something a component should have to depend on.\n */\n\n.sheet.sheet {\n  position: fixed;\n  left: 0;\n  right: 0;\n  margin: 0 auto;\n  width: 100%;\n\n  /* Wide enough for a comfortable list on a phone, narrow enough not to span a tablet —\n     `Presentation.Auto` reaches a 744px one, where full width would be a banner rather than a\n     sheet. A token like the other two sheet dimensions, since this selector is doubled and a\n     consumer could otherwise only beat it with an inline style. */\n  max-width: var(--sheet-max-width, 560px);\n\n  /* Two separate caps: how much of the screen a sheet may take at most, and how much screen\n     there actually is — the latter set inline by `useSheet` from the visual viewport, since no\n     viewport unit shrinks when the on-screen keyboard opens. */\n  max-height: min(var(--sheet-max-height, 70vh), var(--sheet-available-height, 100vh));\n  max-height: min(var(--sheet-max-height, 70dvh), var(--sheet-available-height, 100dvh));\n  display: flex;\n  flex-direction: column;\n  z-index: 1;\n  animation: sheetSlideIn 0.2s ease-out;\n}\n\n/* What a sheet looks like where it meets the screen, split out from `.sheet-panel` because a\n   component whose own root is the surface (Modal) needs the look without the fill-the-root\n   layout below it. */\n.sheet-surface.sheet-surface {\n  overscroll-behavior: contain;\n  border-radius: calc(var(--default-border-radius) + 4px) calc(var(--default-border-radius) + 4px) 0 0;\n\n  /* Clears the home indicator on a phone without leaving a gap on a device that has none. */\n  padding-bottom: max(var(--sheet-safe-padding, 8px), env(safe-area-inset-bottom));\n}\n\n.sheet-panel {\n  composes: sheet-surface;\n}\n\n.sheet-panel.sheet-panel {\n  position: static;\n  width: 100%;\n  box-sizing: border-box;\n  margin: 0;\n  display: flex;\n  flex-direction: column;\n  min-height: 0;\n  max-height: 100%;\n\n  /* Overrides a panel that caps its own width when anchored, e.g. Popover's 320px. */\n  max-width: 100%;\n\n  /* The sheet root owns the entrance animation; a panel that scales itself in when anchored\n     (Popover) would otherwise play a second, conflicting one on top of the slide-up. */\n  animation: none;\n  overflow-y: auto;\n}\n\n/* For an inner list that caps its own height when anchored: the sheet caps the height instead. */\n.sheet-scroll.sheet-scroll {\n  max-height: none;\n  flex: 1 1 auto;\n  min-height: 0;\n}\n\n.sheet-backdrop.sheet-backdrop {\n  position: fixed;\n  top: 0;\n  left: 0;\n  width: 100%;\n  height: 100%;\n  background: #00000052;\n\n  /* Shared by every overlay, so the order they are portaled in, which is the order they opened in, decides. */\n  z-index: 1;\n  animation: sheetBackdropFadeIn 0.2s ease-out;\n}\n\n@keyframes sheetSlideIn {\n  from {\n    transform: translateY(100%);\n  }\n\n  to {\n    transform: translateY(0);\n  }\n}\n\n@keyframes sheetBackdropFadeIn {\n  from {\n    opacity: 0;\n  }\n\n  to {\n    opacity: 1;\n  }\n}\n\n/* Neither animation has a fill mode, so removing them lands both at their natural state: the\n   panel already at translateY(0), the backdrop already opaque. */\n@media (prefers-reduced-motion: reduce) {\n  .sheet-panel,\n  .sheet-backdrop.sheet-backdrop {\n    animation: none;\n  }\n}\n","import { useEffect, useRef } from 'react';\nimport cx from 'classnames';\n\nimport styles from '../styles/sheet.module.pcss';\nimport type { SheetBackdropProps } from './sheetBackdrop.types';\n\n/** The pointer events a popup's own outside-click handling listens for. */\nconst SWALLOWED_EVENTS = ['pointerdown', 'pointerup', 'mousedown', 'mouseup', 'touchstart', 'touchend'];\n\n/**\n * The dimmed layer behind a bottom sheet, and the thing that closes it when tapped.\n *\n * It closes on its own `click` rather than letting the sheet's usual outside-click handling see\n * the press, because that handling fires on mousedown/mouseup — and the sheet, backdrop included,\n * unmounts the moment it closes. The browser then dispatches the `click` that completes the tap\n * at whatever is under the cursor by then, which is the page behind the sheet: tapping to dismiss\n * would also press a button underneath it.\n *\n * The press is stopped with native listeners rather than React's `stopPropagation`, which would\n * not be enough: React listens at its own root container, so a native handler on `document` or\n * `window` still runs afterwards.\n */\nfunction SheetBackdrop({ onClose, className, style }: SheetBackdropProps) {\n  const ref = useRef<HTMLDivElement>(null);\n\n  useEffect(() => {\n    const element = ref.current;\n    if (!element) return;\n    const stop = (event: Event) => event.stopPropagation();\n    SWALLOWED_EVENTS.forEach((type) => element.addEventListener(type, stop));\n    return () => SWALLOWED_EVENTS.forEach((type) => element.removeEventListener(type, stop));\n  }, []);\n\n  return (\n    <div\n      ref={ref}\n      className={cx(styles.sheetBackdrop, className)}\n      style={style}\n      onClick={(event) => {\n        event.stopPropagation();\n        onClose();\n      }}\n    />\n  );\n}\n\nexport default SheetBackdrop;\n","// Scrollable ancestors up to (not including) <body>. This is computed once per open rather\n// than on every scroll tick, since walking the tree and reading computed style forces layout,\n// which is too expensive to repeat at scroll frequency (Floating UI's autoUpdate does the same\n// thing, resolving the ancestor list once via getOverflowAncestors). Plain page/viewport scroll\n// is deliberately left out: a trigger that scrolls off the top of the page already renders its\n// fixed-position anchor off-screen for free.\nexport function getScrollableAncestors(el: HTMLElement): HTMLElement[] {\n  const ancestors: HTMLElement[] = [];\n  let node = el.parentElement;\n  while (node && node !== document.body && node !== document.documentElement) {\n    const style = getComputedStyle(node);\n    if (style.overflowX !== 'visible' || style.overflowY !== 'visible') ancestors.push(node);\n    node = node.parentElement;\n  }\n  return ancestors;\n}\n\nexport function isHiddenByAnyAncestor(rect: DOMRect, ancestors: HTMLElement[]): boolean {\n  return ancestors.some((ancestor) => {\n    const a = ancestor.getBoundingClientRect();\n    return rect.bottom <= a.top || rect.top >= a.bottom || rect.right <= a.left || rect.left >= a.right;\n  });\n}\n\n/**\n * The ancestor whose scrollbar carries `el`, or null when that is the page.\n * `Sticky` pins against it and `VirtualList` virtualizes against it, and the two\n * have to agree or a sticky header and the rows under it come apart.\n *\n * Narrower than `getScrollableAncestors` above, which is about who *clips* and so\n * counts `hidden` too: a `hidden` box never scrolls, so pinning to it would pin\n * to something that never moves.\n */\nexport function getScrollParent(el: HTMLElement | null): HTMLElement | null {\n  let node = el?.parentElement ?? null;\n  while (node && node !== document.body) {\n    const { overflowY } = getComputedStyle(node);\n    if (overflowY === 'auto' || overflowY === 'scroll' || overflowY === 'overlay') return node;\n    node = node.parentElement;\n  }\n  return null;\n}\n","export interface VisualViewportRect {\n  left: number;\n  top: number;\n  width: number;\n  height: number;\n}\n\n// `window.visualViewport` reports the actually-visible area, which shrinks (and offsets) when a\n// mobile on-screen keyboard opens — unlike `window.innerWidth`/`innerHeight`, which stay pinned to\n// the full layout viewport. `getBoundingClientRect()` is relative to the layout viewport's origin,\n// so `left`/`top` here let callers translate into the same coordinate space before clamping.\nexport function getVisualViewportRect(): VisualViewportRect {\n  const vv = typeof window !== 'undefined' ? window.visualViewport : null;\n  if (vv) return { left: vv.offsetLeft, top: vv.offsetTop, width: vv.width, height: vv.height };\n  return {\n    left: 0,\n    top: 0,\n    width: typeof window !== 'undefined' ? window.innerWidth : 0,\n    height: typeof window !== 'undefined' ? window.innerHeight : 0,\n  };\n}\n","import { useLayoutEffect, useRef, type RefObject } from 'react';\nimport { getScrollableAncestors } from 'components/common/utils/scrollAncestors';\n\nexport interface UseAnchorTrackingArgs {\n  isOpen: boolean;\n  /** The element the popup is anchored to. Its scrollable ancestors are what get tracked. */\n  triggerRef: RefObject<HTMLElement | null>;\n  /**\n   * Every element the positioning maths needs. Tracking starts only once all of them, plus the\n   * trigger, are mounted. Read fresh on each setup, so an inline array literal is fine.\n   */\n  elementRefs: RefObject<HTMLElement | null>[];\n  /**\n   * Positions the popup. Called once on setup and then on every scroll, resize, tracked size\n   * change and end of a CSS animation that moved the trigger, plus whenever it calls `rerun` for\n   * an end none of those report. Reads the caller's own refs for everything else.\n   */\n  update: (trigger: HTMLElement, ancestors: HTMLElement[], rerun: () => void) => void;\n  /**\n   * Elements whose own size changes must re-run `update`: a popup whose content changes size\n   * while open (a filtered list shrinking, an async list loading in) otherwise keeps coordinates\n   * computed for its old height and ends up detached from the trigger. Re-read on every update,\n   * since a panel can mount a commit later than the element it hangs off.\n   */\n  resizeRefs?: RefObject<HTMLElement | null>[];\n  /** Hands the popup's elements back as they were found. Runs when tracking stops. */\n  teardown?: () => void;\n  /** Values that must restart tracking, and so re-run `update`, when they change. */\n  deps?: readonly unknown[];\n}\n\n/**\n * The lifecycle half of anchoring a portaled, `position: fixed` popup to a trigger: waits for the\n * elements to mount, resolves the trigger's scrolling ancestors once, and re-runs the caller's\n * positioning on every scroll, viewport resize and tracked size change until it closes.\n *\n * Callers supply only the geometry (`update`) and the inline styles to clear again (`teardown`);\n * `usePopoverPosition` is built on this.\n *\n * Position is deliberately written straight to the DOM by the caller rather than through React\n * state: going through setState -> re-render -> commit adds a round-trip that lags behind the\n * browser's own scroll painting by at least a frame.\n */\nexport function useAnchorTracking({\n  isOpen,\n  triggerRef,\n  elementRefs,\n  update,\n  resizeRefs,\n  teardown,\n  deps = [],\n}: UseAnchorTrackingArgs) {\n  // Latest-value refs, so the effect restarts only for `deps` the caller names, never merely\n  // because these were passed as fresh array/function literals this render.\n  const latest = useRef({ elementRefs, update, resizeRefs, teardown });\n  latest.current = { elementRefs, update, resizeRefs, teardown };\n\n  useLayoutEffect(() => {\n    if (!isOpen) return;\n\n    let rafId: number | undefined;\n    let stop: (() => void) | undefined;\n\n    // The elements may not be mounted yet on the very first invocation of this effect: a portal\n    // target can still be missing (a `#bodyEnd` the app renders, or any while hydrating), and then\n    // the portaled content and its ref commits one render later. Since refs never change\n    // identity, that later commit doesn't re-trigger this effect on its own, so retry each frame.\n    function trySetup() {\n      const trigger = triggerRef.current;\n      const elements = latest.current.elementRefs;\n      if (!trigger || elements.some((ref) => !ref.current)) {\n        rafId = requestAnimationFrame(trySetup);\n        return;\n      }\n      const ancestors = getScrollableAncestors(trigger);\n\n      const resizeObserver = new ResizeObserver(() => runUpdate());\n      const observed = new Set<Element>();\n\n      let active = true;\n      function runUpdate() {\n        // `rerun` may be called after tracking stopped, from a promise that settled late.\n        if (!active) return;\n        latest.current.update(trigger!, ancestors, runUpdate);\n        // After the update, so an element that only mounts during it is picked up too. Once\n        // observed this is a handful of Set lookups, so it costs nothing on the scroll path.\n        latest.current.resizeRefs?.forEach(({ current }) => {\n          if (!current || observed.has(current)) return;\n          observed.add(current);\n          resizeObserver.observe(current);\n        });\n      }\n\n      runUpdate();\n\n      const scrollTargets: (Window | HTMLElement)[] = [window, ...ancestors];\n      scrollTargets.forEach((t) => t.addEventListener('scroll', runUpdate, { passive: true }));\n      window.addEventListener('resize', runUpdate);\n      // `window.resize` doesn't reliably fire when a mobile keyboard opens/closes — visualViewport\n      // does, and is what the callers' flip/clamp maths is measured against.\n      window.visualViewport?.addEventListener('resize', runUpdate);\n      window.visualViewport?.addEventListener('scroll', runUpdate);\n      // A transform on an ancestor moves the trigger without any of the events above: a popup\n      // opened as a Modal scales in was placed, and hidden, against rects collapsed at scale(0).\n      function onAncestorMotionEnd(event: Event) {\n        if ((event.target as Node).contains(trigger)) runUpdate();\n      }\n      document.addEventListener('transitionend', onAncestorMotionEnd, true);\n      document.addEventListener('animationend', onAncestorMotionEnd, true);\n\n      stop = () => {\n        active = false;\n        scrollTargets.forEach((t) => t.removeEventListener('scroll', runUpdate));\n        window.removeEventListener('resize', runUpdate);\n        window.visualViewport?.removeEventListener('resize', runUpdate);\n        window.visualViewport?.removeEventListener('scroll', runUpdate);\n        document.removeEventListener('transitionend', onAncestorMotionEnd, true);\n        document.removeEventListener('animationend', onAncestorMotionEnd, true);\n        resizeObserver.disconnect();\n        latest.current.teardown?.();\n      };\n    }\n\n    trySetup();\n\n    return () => {\n      if (rafId !== undefined) cancelAnimationFrame(rafId);\n      stop?.();\n    };\n    // Spread rather than listed: `deps` is the caller's own set of geometry inputs, and changing\n    // any of them has to restart tracking so the new values get applied immediately.\n    // oxlint-disable-next-line react-hooks/exhaustive-deps\n  }, [isOpen, triggerRef, ...deps]);\n}\n","import type { CSSProperties, ReactNode } from 'react';\nimport type { SlotClassNames, SlotStyles, HtmlProps } from '../types/slots.types';\nimport type { Presentation } from '../configProvider/presentation.types';\n\n/** Preferred side of the trigger the panel opens on. Flips to the opposite side when there's no room. */\nexport enum PopoverPlacement {\n  Top = 'top',\n  Bottom = 'bottom',\n  Left = 'left',\n  Right = 'right',\n}\n\n/** Alignment along the trigger's cross axis (horizontal for top/bottom, vertical for left/right). */\nexport enum PopoverAlign {\n  Start = 'start',\n  Center = 'center',\n  End = 'end',\n}\n\n/** What opens the popover. */\nexport enum PopoverTrigger {\n  Click = 'click',\n  Hover = 'hover',\n  Focus = 'focus',\n}\n\nexport type PopoverSlots = 'root' | 'trigger' | 'floating' | 'panel' | 'arrow' | 'backdrop';\n\nexport interface PopoverProps extends Omit<HtmlProps, 'content'> {\n  /** The trigger element. */\n  children?: ReactNode;\n  /**\n   * Place the panel against this element instead of `children`, for one Popover does not wrap.\n   * Open it with `isOpen`; `null` falls back to `children`.\n   */\n  anchor?: HTMLElement | null;\n  /** The popover body. */\n  content?: ReactNode;\n  /** Preferred side (default `Bottom`). */\n  placement?: PopoverPlacement;\n  /** Cross-axis alignment (default `Center`). */\n  align?: PopoverAlign;\n  /** What opens the popover (default `Click`). */\n  trigger?: PopoverTrigger;\n  /** Controlled open state. Omit for uncontrolled. */\n  isOpen?: boolean;\n  /** Initial open state when uncontrolled (default `false`). */\n  defaultOpen?: boolean;\n  /**\n   * Fires whenever the popover wants to open or close. `fromKeyboard` says\n   * whether a key caused it, which is what a menu needs in order to put the\n   * focus on an entry only when the pointer was not what opened it.\n   */\n  onOpenChange?: (isOpen: boolean, meta: { fromKeyboard: boolean }) => void;\n  /** Gap in px between trigger and panel (default `10`). */\n  offset?: number;\n  /** Render the arrow pointing at the trigger (default `true`). */\n  withArrow?: boolean;\n  /** Close on a click outside, or when focus leaves the trigger and panel (default `true`). */\n  closeOnOutsideClick?: boolean;\n  /** Close when pressing Escape (default `true`). */\n  closeOnEscape?: boolean;\n  /** Delay in ms before a hover-triggered popover opens (default `100`). */\n  openDelay?: number;\n  /** Delay in ms before a hover-triggered popover closes (default `120`). */\n  closeDelay?: number;\n  /** Prevent the popover from opening. */\n  disabled?: boolean;\n  /**\n   * Move focus into the panel when it opens, and claim `role=\"dialog\"` with it.\n   * `'keyboard'` (the default) does it only when the open came from the\n   * keyboard: the panel is portaled to the end of the document, so Tab never\n   * reaches it on its own, while a click leaves the focus where the pointer\n   * already put it. `true` always, `false` never.\n   */\n  focusOnOpen?: boolean | 'keyboard';\n  /**\n   * Whether clicking the trigger while the panel is open closes it (default\n   * `true`). A menu button should toggle; a text field that opens a picker\n   * should not, or clicking into it to place the caret would close the panel.\n   */\n  closeOnTriggerClick?: boolean;\n  /**\n   * `'trigger'` gives the panel at least the trigger's width, for a list that\n   * should line up with the field it belongs to. `'auto'` (the default) lets it\n   * size to its own content.\n   */\n  width?: 'auto' | 'trigger';\n  /**\n   * Cap the panel at a readable measure (default `true`). Off for a panel with\n   * its own width, a calendar or a media grid.\n   */\n  constrainWidth?: boolean;\n  /** Where the panel opens: anchored to the trigger (`Default`), pinned to the bottom of the\n   * screen, or the latter only on a small touch screen. Only a `Click` trigger presents as a\n   * sheet. */\n  presentation?: Presentation;\n\n  className?: string;\n  style?: CSSProperties;\n  classNames?: SlotClassNames<PopoverSlots>;\n  styles?: SlotStyles<PopoverSlots>;\n}\n","import { useId, useRef, type RefObject } from 'react';\nimport { isHiddenByAnyAncestor } from 'components/common/utils/scrollAncestors';\nimport { getVisualViewportRect } from 'components/common/utils/visualViewport';\nimport { useAnchorTracking } from '../hooks/useAnchorTracking';\nimport { PopoverPlacement, PopoverAlign } from './popover.types';\n\ntype Axis = 'top' | 'bottom' | 'left' | 'right';\nconst OPPOSITE: Record<PopoverPlacement, Axis> = {\n  [PopoverPlacement.Top]: 'bottom',\n  [PopoverPlacement.Bottom]: 'top',\n  [PopoverPlacement.Left]: 'right',\n  [PopoverPlacement.Right]: 'left',\n};\n\nconst MARGIN = 8; // viewport gutter kept clear when shifting a panel back on-screen\n\n/**\n * Narrower than this and `shrinkToFit` gives up: a panel this size reads worse than\n * one that overhangs. Around four words a line for an error bubble, which is the\n * narrowest caller. A constant rather than an option, because the one caller that\n * shrinks has no reason to want a different floor; give it one the day a second\n * caller disagrees, as `shrinkToFit: boolean | number`.\n */\nconst MIN_SHRINK_WIDTH = 160;\n\n/** How far down to look for a box when the wrappers in between have none. */\nconst MAX_ANCHOR_DEPTH = 3;\n\n/**\n * The element to anchor to. A trigger with a box of its own is that element, which\n * is the case for a caller that hands over a real element (`Tooltip`).\n *\n * Popover's own trigger wrapper generates no box, deliberately, so that the\n * trigger keeps the place it would have had without Popover around it. Then the\n * real trigger is inside it, and this descends to find it, past any further\n * boxless element a consumer wrapped its trigger in: such an element has a zero\n * rect at the origin, which would otherwise send the panel to the top-left corner\n * of the screen.\n */\nfunction resolveAnchor(trigger: HTMLElement): { element: HTMLElement | null; rect: DOMRect } {\n  let el: HTMLElement = trigger;\n  for (let depth = 0; depth <= MAX_ANCHOR_DEPTH; depth++) {\n    const rect = el.getBoundingClientRect();\n    if (rect.width > 0 || rect.height > 0) return { element: el, rect };\n    const child = el.firstElementChild as HTMLElement | null;\n    if (!child) break;\n    el = child;\n  }\n  return { element: null, rect: trigger.getBoundingClientRect() };\n}\n\nlet anchorSupport: boolean | undefined;\n\n// Probed, not just parsed: Firefox 155 resolves `anchor()` without the anchor's transforms, which\n// put a Dropdown in a VirtualList row, placed by `translateY`, above the list.\nexport function supportsAnchoring(): boolean {\n  if (anchorSupport !== undefined) return anchorSupport;\n  if (typeof CSS === 'undefined' || !CSS.supports?.('top: anchor(top, 0px)')) return (anchorSupport = false);\n  const host = document.createElement('div');\n  host.style.cssText = 'position: fixed; top: 0; left: 0; transform: translateY(100px); visibility: hidden';\n  const anchor = document.createElement('div');\n  anchor.style.cssText = 'anchor-name: --popover-probe; width: 1px; height: 1px';\n  const probe = document.createElement('div');\n  probe.style.cssText =\n    'position: fixed; position-anchor: --popover-probe; top: anchor(top, 0px); left: 0; visibility: hidden';\n  host.append(anchor);\n  document.body.append(host, probe);\n  anchorSupport = Math.abs(probe.getBoundingClientRect().top - anchor.getBoundingClientRect().top) < 1;\n  host.remove();\n  probe.remove();\n  return anchorSupport;\n}\n\nfunction anchorNames(el: HTMLElement): string[] {\n  return el.style\n    .getPropertyValue('anchor-name')\n    .split(',')\n    .map((name) => name.trim())\n    .filter((name) => name && name !== 'none');\n}\n\n// Merged, not replaced: one trigger can carry a Tooltip and a Popover at once.\nfunction addAnchorName(el: HTMLElement, name: string) {\n  const names = anchorNames(el);\n  if (!names.includes(name)) el.style.setProperty('anchor-name', [...names, name].join(', '));\n}\n\nfunction removeAnchorName(el: HTMLElement, name: string) {\n  const names = anchorNames(el).filter((n) => n !== name);\n  if (names.length > 0) el.style.setProperty('anchor-name', names.join(', '));\n  else el.style.removeProperty('anchor-name');\n}\n\nconst MOVING_PROPERTIES = ['transform', 'translate', 'scale', 'rotate'];\n\n/**\n * Longer than this left to run and the panel shows anyway, mid-way, and is corrected when the\n * animation ends. A Modal or Drawer entrance is well under it; waiting out a long one read as\n * the panel not opening at all.\n */\nconst MAX_WAIT_MS = 1000;\n\n/** The running animations and transitions on ancestors of `trigger` that move it. */\nfunction animationsMoving(trigger: HTMLElement): Animation[] {\n  return (document.getAnimations?.() ?? []).filter((animation) => {\n    if (animation.playState !== 'running' || !(animation.effect instanceof KeyframeEffect)) return false;\n    const { target, pseudoElement } = animation.effect;\n    // A `::before` or `::after` is a box of its own: its transform moves nothing else.\n    if (!target || pseudoElement || target === trigger || !target.contains(trigger)) return false;\n    // Covers one that never ends, which would keep the panel hidden for good.\n    const { endTime, localTime } = animation.effect.getComputedTiming();\n    if (Number(endTime) - Number(localTime) > MAX_WAIT_MS) return false;\n    return animation.effect.getKeyframes().some((frame) => MOVING_PROPERTIES.some((property) => property in frame));\n  });\n}\n\n// Anchored, the browser moves the panel with the scroll in the same frame; the scroll listener\n// lands a frame or more later. The pixel fallback covers an anchor the browser refuses.\nfunction inset(side: 'top' | 'left', value: number, anchorEdge: number, anchored: boolean): string {\n  if (!anchored) return `${value}px`;\n  return `calc(anchor(${side}, ${anchorEdge}px) + ${value - anchorEdge}px)`;\n}\n\nexport interface UsePopoverPositionArgs {\n  isOpen: boolean;\n  placement: PopoverPlacement;\n  align: PopoverAlign;\n  offset: number;\n  triggerRef: RefObject<HTMLElement | null>;\n  /** `position: fixed` wrapper that receives top/left/visibility and the resolved-placement data attribute. */\n  floatingRef: RefObject<HTMLElement | null>;\n  /** Visible panel, measured to decide flip and shift. */\n  panelRef: RefObject<HTMLElement | null>;\n  /** Arrow element; gets `--arrow-x` / `--arrow-y` written to it. Pass a null ref when `withArrow` is false. */\n  arrowRef: RefObject<HTMLElement | null>;\n  /**\n   * Give the panel at least the trigger's width, for a list that lines up with\n   * the field it belongs to. A minimum rather than a fixed width: a menu on a\n   * narrow icon button still sizes to its own content.\n   */\n  matchTriggerWidth?: boolean;\n  /**\n   * When the panel does not fit on the side its alignment puts it, narrow it\n   * rather than sliding it away from the trigger. Sliding keeps the panel\n   * on-screen but detaches it: an end-aligned bubble stops ending where its\n   * trigger ends, and its arrow stops pointing at anything. Off by default, since\n   * a panel whose width is its content, a menu or a list, would rather overhang\n   * its trigger than wrap.\n   *\n   * Only down to `MIN_SHRINK_WIDTH`. Past that the panel is narrower than it is\n   * readable, and a trigger that close to the screen edge is better served by a\n   * panel that overhangs it.\n   */\n  shrinkToFit?: boolean;\n  /**\n   * Called when a newly opened panel first becomes visible. Focus has to wait for it: a panel\n   * still hidden, waiting out an ancestor's animation, takes none.\n   */\n  onShown?: () => void;\n}\n\n/**\n * Positions a portaled, `position: fixed` popover on any of the four sides of its trigger with\n * start/center/end cross-axis alignment. Flips to the opposite side when the preferred one has no\n * room, shifts back on-screen along the cross axis, points the arrow at the trigger centre, and\n * hides (not closes) the panel once the trigger scrolls behind a clipping ancestor.\n *\n * `useAnchorTracking` supplies the lifecycle around this: mount wait, ancestor resolution, and\n * re-running on scroll, viewport resize and panel size changes.\n */\nexport function usePopoverPosition({\n  isOpen,\n  placement,\n  align,\n  offset,\n  triggerRef,\n  floatingRef,\n  panelRef,\n  arrowRef,\n  matchTriggerWidth = false,\n  shrinkToFit = false,\n  onShown,\n}: UsePopoverPositionArgs) {\n  const anchorName = `--popover-${useId().replace(/[^\\w-]/g, '')}`;\n  const anchorRef = useRef<HTMLElement | null>(null);\n  const shownPanelRef = useRef<HTMLElement | null>(null);\n  // Every update before the first show finds the same animations again; one rerun each is enough.\n  // Keyed on `rerun`, since a restarted tracking brings a new one that has to be called too.\n  const awaitedRef = useRef(new WeakMap<Animation, () => void>());\n\n  function setAnchor(next: HTMLElement | null) {\n    if (anchorRef.current === next) return;\n    if (anchorRef.current) removeAnchorName(anchorRef.current, anchorName);\n    if (next) addAnchorName(next, anchorName);\n    anchorRef.current = next;\n  }\n\n  useAnchorTracking({\n    isOpen,\n    triggerRef,\n    elementRefs: [floatingRef, panelRef],\n    // The panel's own size feeds the flip decision and, on the `top`/`left` sides, the coordinate\n    // itself — so content that resizes while open has to be re-measured.\n    resizeRefs: [panelRef],\n    deps: [placement, align, offset, floatingRef, panelRef, arrowRef, matchTriggerWidth, shrinkToFit],\n\n    update: (trigger, ancestors, rerun) => {\n      const floating = floatingRef.current;\n      const panel = panelRef.current;\n      if (!floating || !panel) return;\n\n      const { element: anchorElement, rect: t } = resolveAnchor(trigger);\n      const anchored = anchorElement !== null && supportsAnchoring();\n      setAnchor(anchored ? anchorElement : null);\n      if (anchored) floating.style.setProperty('position-anchor', anchorName);\n      else floating.style.removeProperty('position-anchor');\n      // Written before the panel is measured, so `pw` below already accounts for it,\n      // and taken back off when it is no longer wanted: the panel outlives the\n      // switch, and an inline width would outrank whatever it switched to.\n      if (matchTriggerWidth) panel.style.minWidth = `${t.width}px`;\n      else panel.style.removeProperty('min-width');\n      // The wrapper's box, not the panel's: `offsetWidth` rounds to whole pixels, so\n      // aligning a panel 165.4px wide against a trigger edge at 406.47 left it 0.4px\n      // past that edge. The panel's own rect is fractional but carries the entrance\n      // animation's scale; the wrapper is the same layout box without the transform.\n      // Cleared before measuring, or a cap left over from a narrower screen would be\n      // read back as the panel's own width and never widen again.\n      if (shrinkToFit) panel.style.removeProperty('max-width');\n      let { width: pw, height: ph } = floating.getBoundingClientRect();\n      // The visible area, not the full layout viewport — shrinks on mobile when a keyboard is\n      // open, so the panel flips/clamps against what's actually on screen instead of ending up\n      // hidden behind the keyboard.\n      const vv = getVisualViewportRect();\n\n      // Flipping only swaps a side for its opposite, so which axis the alignment runs\n      // along is decided by the preferred placement alone, before any of that.\n      const alignsHorizontally = placement === PopoverPlacement.Top || placement === PopoverPlacement.Bottom;\n\n      if (shrinkToFit && alignsHorizontally) {\n        const alignedSide =\n          align === PopoverAlign.End\n            ? t.right - (vv.left + MARGIN)\n            : align === PopoverAlign.Start\n              ? vv.left + vv.width - MARGIN - t.left\n              : vv.width - 2 * MARGIN;\n        const room = Math.min(alignedSide, vv.width - 2 * MARGIN);\n        if (pw > room && room >= MIN_SHRINK_WIDTH) {\n          panel.style.maxWidth = `${room}px`;\n          // Wrapping makes it taller, and the height is what the flip below is decided on.\n          ({ width: pw, height: ph } = floating.getBoundingClientRect());\n        }\n      }\n\n      // Resolve the main-axis side, flipping when the preferred side lacks room and the\n      // opposite side has more.\n      const room: Record<Axis, number> = {\n        top: t.top - vv.top,\n        bottom: vv.top + vv.height - t.bottom,\n        left: t.left - vv.left,\n        right: vv.left + vv.width - t.right,\n      };\n      const need = placement === PopoverPlacement.Top || placement === PopoverPlacement.Bottom ? ph : pw;\n      let side: Axis = placement as unknown as Axis;\n      const opp = OPPOSITE[placement];\n      if (room[side] < need + offset && room[opp] > room[side]) side = opp;\n\n      const vertical = side === 'top' || side === 'bottom';\n\n      // Main axis: place the panel just outside the trigger on the resolved side.\n      let top: number;\n      let left: number;\n      if (side === 'bottom') {\n        top = t.bottom + offset;\n      } else if (side === 'top') {\n        top = t.top - offset - ph;\n      } else if (side === 'right') {\n        left = t.right + offset;\n      } else {\n        left = t.left - offset - pw;\n      }\n\n      // Cross axis: align start/center/end against the trigger, then shift on-screen.\n      let shiftedX = false;\n      let shiftedY = false;\n      if (vertical) {\n        if (align === PopoverAlign.Start) left = t.left;\n        else if (align === PopoverAlign.End) left = t.right - pw;\n        else left = t.left + t.width / 2 - pw / 2;\n        const clamped = Math.max(vv.left + MARGIN, Math.min(left!, vv.left + vv.width - pw - MARGIN));\n        shiftedX = clamped !== left!;\n        left = clamped;\n      } else {\n        if (align === PopoverAlign.Start) top = t.top;\n        else if (align === PopoverAlign.End) top = t.bottom - ph;\n        else top = t.top + t.height / 2 - ph / 2;\n        const clamped = Math.max(vv.top + MARGIN, Math.min(top!, vv.top + vv.height - ph - MARGIN));\n        shiftedY = clamped !== top!;\n        top = clamped;\n      }\n\n      // A shifted axis is pinned to the viewport edge, so it stays in pixels: anchored, the scroll\n      // would carry it off the edge until the next update pulled it back, which reads as shaking.\n      floating.style.top = inset('top', top!, t.top, anchored && !shiftedY);\n      floating.style.left = inset('left', left!, t.left, anchored && !shiftedX);\n      // Until first shown, it waits out an ancestor that is still moving the trigger, such as a\n      // Modal scaling in: placed mid-way, it showed away from the trigger and jumped at the end.\n      // Awaited through `finished`, since a Web Animation fires no `animationend`; a cancelled\n      // one stops moving the trigger too.\n      const firstShow = shownPanelRef.current !== panel;\n      const moving = firstShow ? animationsMoving(trigger) : [];\n      moving.forEach((animation) => {\n        if (awaitedRef.current.get(animation) === rerun) return;\n        awaitedRef.current.set(animation, rerun);\n        animation.finished.then(rerun, rerun);\n      });\n      const hidden = isHiddenByAnyAncestor(t, ancestors) || moving.length > 0;\n      floating.style.visibility = hidden ? 'hidden' : 'visible';\n      // The entrance animation starts on mount, so a panel first placed hidden played it unseen.\n      // Per panel, since a new one mounts on every open.\n      if (!hidden && firstShow) {\n        shownPanelRef.current = panel;\n        panel.getAnimations?.().forEach((animation) => {\n          animation.currentTime = 0;\n        });\n        onShown?.();\n      }\n      floating.dataset.placement = side;\n\n      // Arrow: centre it on the trigger along the cross axis, clamped inside the panel edges.\n      const arrow = arrowRef.current;\n      if (arrow) {\n        const half = arrow.offsetWidth / 2 || 5;\n        if (vertical) {\n          const x = t.left + t.width / 2 - left!;\n          arrow.style.setProperty('--arrow-x', `${Math.max(half + 2, Math.min(x, pw - half - 2))}px`);\n          arrow.style.removeProperty('--arrow-y');\n        } else {\n          const y = t.top + t.height / 2 - top!;\n          arrow.style.setProperty('--arrow-y', `${Math.max(half + 2, Math.min(y, ph - half - 2))}px`);\n          arrow.style.removeProperty('--arrow-x');\n        }\n      }\n    },\n\n    teardown: () => {\n      // Positioning is written as inline styles, which outrank any class the panel might\n      // switch to while it stays mounted — a popover turning into a bottom sheet (`useSheet`)\n      // would keep the coordinates computed for its anchored self.\n      setAnchor(null);\n      const floating = floatingRef.current;\n      if (!floating) return;\n      floating.style.top = '';\n      floating.style.left = '';\n      floating.style.visibility = '';\n      floating.style.removeProperty('position-anchor');\n      const panel = panelRef.current;\n      panel?.style.removeProperty('min-width');\n      panel?.style.removeProperty('max-width');\n    },\n  });\n}\n","import type { RefObject } from 'react';\n\nconst FOCUSABLE_SELECTOR = [\n  'a[href]',\n  'area[href]',\n  'button:not([disabled])',\n  'input:not([disabled])',\n  'select:not([disabled])',\n  'textarea:not([disabled])',\n  '[tabindex]:not([tabindex=\"-1\"])',\n  '[contenteditable=\"true\"]',\n].join(',');\n\n/** Tabbable descendants of `root`, in DOM order. */\nexport function tabbablesIn(root: ParentNode | null): HTMLElement[] {\n  if (!root) return [];\n  return (\n    Array.from(root.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR))\n      // A button or a link can be taken out of the tab order too, which the\n      // selector above only catches for elements whose focusability comes from\n      // the attribute. A roving tabindex, where one item of a list is the tab\n      // stop and the rest are reachable by arrow key, otherwise reads as a list\n      // of tab stops, and the Tab boundary lands on the wrong entry.\n      .filter((el) => el.getAttribute('tabindex') !== '-1')\n  );\n}\n\nfunction isTabbable(element: HTMLElement): boolean {\n  return element.matches(FOCUSABLE_SELECTOR) && element.getAttribute('tabindex') !== '-1';\n}\n\nexport interface UseFocusBoundaryArgs {\n  /** The trigger element focus returns to on Escape / Shift+Tab out of the content's top. */\n  triggerRef: RefObject<HTMLElement | null>;\n  /** The portaled content root — tabbables inside it define the Tab boundary. */\n  contentRef: RefObject<HTMLElement | null>;\n}\n\n/**\n * Focus helpers for content portaled to the end of the DOM (e.g. via BodyEnd), where Tab can't\n * reach it in visual document order on its own. Used by Popover and by Menu, which both need to\n * hand focus back to the trigger on Escape / Shift+Tab out of the top, and continue focus\n * naturally past the trigger when Tab exits the bottom.\n */\nexport function useFocusBoundary({ triggerRef, contentRef }: UseFocusBoundaryArgs) {\n  function firstTriggerFocusable(): HTMLElement | null {\n    const trigger = triggerRef.current;\n    // The trigger itself where it takes focus, as an element handed to Popover's `anchor` can;\n    // else the first element inside it that does, as inside Popover's and Menu's own wrappers.\n    if (trigger && isTabbable(trigger)) return trigger;\n    return tabbablesIn(trigger)[0] ?? trigger;\n  }\n\n  function restoreFocusToTrigger() {\n    firstTriggerFocusable()?.focus();\n  }\n\n  // Move focus to the tabbable element that follows the trigger in the document, skipping the\n  // portaled content — the \"natural next\" stop when Tab exits the bottom of the content.\n  function focusAfterTrigger() {\n    const trig = firstTriggerFocusable();\n    // Inside a Modal the next stop is the Modal's, wrapping as its trap does: past its last\n    // element lies the page behind it, and the focus stayed on the panel being removed.\n    const modal = trig?.closest<HTMLElement>('[aria-modal=\"true\"]');\n    const all = tabbablesIn(modal ?? document.body).filter((el) => !contentRef.current?.contains(el));\n    const idx = trig ? all.indexOf(trig) : -1;\n    if (idx < 0) return;\n    (all[idx + 1] ?? (modal ? all[0] : undefined))?.focus();\n  }\n\n  return { firstTriggerFocusable, restoreFocusToTrigger, focusAfterTrigger };\n}\n","import { useEffect, useState } from 'react';\nimport { getVisualViewportRect } from 'components/common/utils/visualViewport';\n\nexport interface VisualViewportSize {\n  /** Height of the actually-visible viewport in px — shrinks when a mobile on-screen keyboard\n   * opens, unlike `window.innerHeight` / CSS `vh`, which stay pinned to the full layout viewport. */\n  height: number;\n  /** Vertical offset of the visible viewport's top edge within the layout viewport — non-zero once\n   * the page has scrolled to keep a focused field above the keyboard. */\n  offsetTop: number;\n}\n\n/**\n * Tracks `window.visualViewport` so layout can react to the on-screen keyboard opening/closing.\n * Falls back to `window.innerHeight`/0 in browsers without the API.\n */\nexport function useVisualViewportSize(): VisualViewportSize {\n  const [size, setSize] = useState<VisualViewportSize>(() => {\n    const rect = getVisualViewportRect();\n    return { height: rect.height, offsetTop: rect.top };\n  });\n\n  useEffect(() => {\n    const vv = window.visualViewport;\n    if (!vv) return;\n\n    function update() {\n      const rect = getVisualViewportRect();\n      setSize({ height: rect.height, offsetTop: rect.top });\n    }\n\n    update();\n    vv.addEventListener('resize', update);\n    vv.addEventListener('scroll', update);\n    return () => {\n      vv.removeEventListener('resize', update);\n      vv.removeEventListener('scroll', update);\n    };\n  }, []);\n\n  return size;\n}\n","import type { CSSProperties } from 'react';\nimport { Presentation } from '../configProvider/presentation.types';\nimport { useCoarsePointer } from './useCoarsePointer';\nimport { useMediaQuery } from './useMediaQuery';\nimport { useVisualViewportSize } from './useVisualViewportSize';\n\n/** Below this, `Presentation.Auto` switches a touch device over to a sheet. */\nconst SMALL_SCREEN_QUERY = '(max-width: 767px)';\n\n/** Sliver kept above a full-height sheet, so it still reads as a sheet rather than a full screen. */\nconst TOP_GAP = 8;\n\nexport interface Sheet {\n  /** Whether the popup should render as a bottom sheet instead of anchored to its trigger. */\n  isSheet: boolean;\n  /**\n   * Inline style for the sheet root: keeps it above an open on-screen keyboard, and hands the CSS\n   * the height actually left on screen as `--sheet-available-height`.\n   */\n  sheetStyle: CSSProperties | undefined;\n}\n\n/**\n * Resolves whether a popup presents as a bottom sheet.\n *\n * `Auto` asks for a coarse pointer *and* a small screen: pointer alone turns a touchscreen laptop\n * into a phone, and width alone turns a narrow desktop window into one.\n */\nexport function useSheet(presentation: Presentation = Presentation.Default): Sheet {\n  const isCoarsePointer = useCoarsePointer();\n  const isSmallScreen = useMediaQuery(SMALL_SCREEN_QUERY);\n  const { height: viewportHeight, offsetTop } = useVisualViewportSize();\n\n  const isSheet =\n    presentation === Presentation.Sheet || (presentation === Presentation.Auto && isCoarsePointer && isSmallScreen);\n\n  if (!isSheet) return { isSheet: false, sheetStyle: undefined };\n\n  // `position: fixed` resolves against the layout viewport, which an open keyboard doesn't\n  // shrink — so a sheet at `bottom: 0` sits behind the keyboard. Lift it by the difference.\n  const keyboardHeight =\n    typeof window === 'undefined' ? 0 : Math.max(0, window.innerHeight - (offsetTop + viewportHeight));\n\n  // Lifting the sheet is only half of it: it also has to *fit* in what's left. No viewport unit\n  // can express that, because `dvh` (like `vh`) measures the layout viewport, which the keyboard\n  // doesn't shrink either — so a tall sheet capped at, say, `90dvh` and lifted by the keyboard\n  // height ends up with its top edge above the screen, taking the header and the first part of\n  // the body with it, out of reach of the body's own scrolling. The visual viewport is the only\n  // thing that knows the real number, so it's measured here and handed to the CSS to clamp with.\n  const availableHeight = Math.max(0, viewportHeight - TOP_GAP);\n\n  const sheetStyle = {\n    bottom: keyboardHeight,\n    '--sheet-available-height': `${availableHeight}px`,\n  } as CSSProperties;\n\n  return { isSheet: true, sheetStyle };\n}\n\nexport default useSheet;\n","import { useEffect, useRef, type RefObject } from 'react';\nimport { tabbablesIn } from './useFocusBoundary';\n\nexport interface UseOverlayArgs {\n  /** Whether the overlay is currently visible. Everything below is set up while this is true. */\n  isActive: boolean;\n  /** The overlay panel. Focus is trapped inside it and Escape is scoped to it. */\n  containerRef: RefObject<HTMLElement | null>;\n  /** Called when Escape is pressed while this is the topmost overlay. Omit to not handle Escape. */\n  onEscape?: () => void;\n  /**\n   * Keep Tab inside the panel and restore focus to the previously focused element on close, unless\n   * an overlay opened after this one is still open.\n   */\n  trapFocus?: boolean;\n  /** Prevent the page behind the overlay from scrolling. */\n  lockScroll?: boolean;\n}\n\n/**\n * The three behaviours every blocking overlay needs: Escape to dismiss, a focus trap, and a\n * scroll lock on the page behind it.\n *\n * They share one module-level stack because they share one question: overlays nest (a\n * ConfirmModal opened from inside a Modal), every instance listens on `document`, and only the\n * innermost one may react. Without the stack, Escape would close every open layer at once and\n * the outer trap would yank focus out of the inner panel on the next Tab.\n *\n * The scroll lock is reference-counted for the same reason: the page stays locked until the last\n * overlay closes, and the body's original inline styles are restored rather than cleared.\n */\nexport function useOverlay({ isActive, containerRef, onEscape, trapFocus = true, lockScroll = true }: UseOverlayArgs) {\n  const onEscapeRef = useRef(onEscape);\n  useEffect(() => {\n    onEscapeRef.current = onEscape;\n  });\n\n  // The layer's position in the stack is claimed during render, not in the effect below, because\n  // effects run child-first: an overlay opened from *inside* another one (a menu in a modal) has\n  // its effect run before its own parent's and would come out as the lower layer. Render runs\n  // parent-first, which is the order the layers are actually nested in. DOM order is no help\n  // either — React commits the inner portal ahead of the outer one.\n  const orderRef = useRef(0);\n  if (isActive && orderRef.current === 0) orderRef.current = nextLayerOrder++;\n  if (!isActive && orderRef.current !== 0) orderRef.current = 0;\n\n  useEffect(() => {\n    if (!isActive) return;\n\n    const order = orderRef.current;\n    activeLayers.set(order, { trapFocus, containerRef });\n    const isTopmost = () => order === Math.max(...activeLayers.keys());\n    const layersAbove = () => [...activeLayers].filter(([other]) => other > order).map(([, layer]) => layer);\n\n    const previouslyFocused = document.activeElement as HTMLElement | null;\n    if (trapFocus) {\n      const container = containerRef.current;\n      (tabbablesIn(container)[0] ?? container)?.focus();\n    }\n\n    function onKeyDown(event: KeyboardEvent) {\n      if (event.key === 'Escape' && onEscapeRef.current && isTopmost()) {\n        event.preventDefault();\n        onEscapeRef.current();\n        return;\n      }\n\n      if (event.key !== 'Tab' || !trapFocus || event.defaultPrevented) return;\n      // A layer that does not trap, a Popover, leaves Tab to the trap below it: with nothing to\n      // focus in its panel, Tab otherwise left the Modal for the browser's own controls. Tab\n      // inside it stays its own, and so does the move it already made out of either end.\n      const above = layersAbove();\n      if (above.some((layer) => layer.trapFocus)) return;\n      if (above.some((layer) => layer.containerRef.current?.contains(document.activeElement))) return;\n      const focusables = tabbablesIn(containerRef.current);\n      // Nothing to move to inside the panel, but Tab must still not escape it.\n      if (focusables.length === 0) {\n        event.preventDefault();\n        return;\n      }\n      const first = focusables[0];\n      const last = focusables[focusables.length - 1];\n      const active = document.activeElement;\n      if (!containerRef.current?.contains(active)) {\n        event.preventDefault();\n        first.focus();\n      } else if (!event.shiftKey && active === last) {\n        event.preventDefault();\n        first.focus();\n      } else if (event.shiftKey && active === first) {\n        event.preventDefault();\n        last.focus();\n      }\n    }\n\n    // A key held as this opened would repeat into what it focused: a held Enter pressed Close.\n    // Outside the panel it still repeats, since suggestions open while the user types.\n    const pressedSinceOpen = new Set<string>();\n    const keyId = (event: KeyboardEvent) => event.code || event.key;\n    function onKeyDownCapture(event: KeyboardEvent) {\n      if (!event.repeat) pressedSinceOpen.add(keyId(event));\n      else if (!pressedSinceOpen.has(keyId(event)) && containerRef.current?.contains(event.target as Node)) {\n        event.preventDefault();\n        event.stopPropagation();\n      }\n    }\n    function onKeyUp(event: KeyboardEvent) {\n      pressedSinceOpen.delete(keyId(event));\n    }\n\n    // `window`, not `document`: a key event on a focused element reaches both, but a synthetic\n    // one dispatched straight at `window` (as tests and some consumers do) never reaches\n    // `document`, since it sits above it in the propagation path.\n    window.addEventListener('keydown', onKeyDown);\n    window.addEventListener('keydown', onKeyDownCapture, true);\n    window.addEventListener('keyup', onKeyUp, true);\n    if (lockScroll) acquireScrollLock();\n\n    return () => {\n      window.removeEventListener('keydown', onKeyDown);\n      window.removeEventListener('keydown', onKeyDownCapture, true);\n      window.removeEventListener('keyup', onKeyUp, true);\n      if (lockScroll) releaseScrollLock();\n      // Modal closes a commit late, so a Popover opened by the same click already holds the focus.\n      const coveredByLaterLayer = layersAbove().length > 0;\n      activeLayers.delete(order);\n      if (trapFocus && !coveredByLaterLayer) previouslyFocused?.focus();\n    };\n  }, [isActive, trapFocus, lockScroll, containerRef]);\n}\n\n/** Monotonic, so a layer opened later always outranks one opened earlier. */\nlet nextLayerOrder = 1;\nconst activeLayers = new Map<number, { trapFocus: boolean; containerRef: RefObject<HTMLElement | null> }>();\n\nlet scrollLockCount = 0;\nlet previousOverflow = '';\nlet previousPaddingRight = '';\n\nfunction acquireScrollLock() {\n  const { body } = document;\n  if (scrollLockCount === 0) {\n    previousOverflow = body.style.overflow;\n    previousPaddingRight = body.style.paddingRight;\n    // Removing the page's scrollbar reclaims its width, so everything behind the overlay would\n    // shift right by that amount without the compensating padding.\n    const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;\n    if (scrollbarWidth > 0) {\n      const current = parseFloat(window.getComputedStyle(body).paddingRight) || 0;\n      body.style.paddingRight = `${current + scrollbarWidth}px`;\n    }\n    body.style.overflow = 'hidden';\n  }\n  scrollLockCount += 1;\n}\n\nfunction releaseScrollLock() {\n  scrollLockCount -= 1;\n  if (scrollLockCount > 0) return;\n  scrollLockCount = 0;\n  document.body.style.overflow = previousOverflow;\n  document.body.style.paddingRight = previousPaddingRight;\n}\n","// A press outside a Popover closes it on release, and the click that follows still reaches what\n// was under the pointer, so a click on another event can open that one. A click that creates\n// something (Calendar's empty time) asks here first, so a click that only closed a popover does not\n// also open the next one.\n\nconst dismissClicks = new WeakSet<Event>();\nlet armed = false;\nlet listening = false;\n\nfunction onClick(event: Event) {\n  if (armed) dismissClicks.add(event);\n  armed = false;\n}\n\n// A release with no click after it, such as the end of a drag, must not mark a later click.\nfunction onPress() {\n  armed = false;\n}\n\n/** Records that the release just dispatched closed an overlay, which marks the click it raises. */\nexport function markDismiss(): void {\n  armed = true;\n  if (listening) return;\n  listening = true;\n  // Capturing on the window, so the click is marked before any handler below it runs.\n  window.addEventListener('click', onClick, true);\n  window.addEventListener('pointerdown', onPress, true);\n}\n\n/** Whether the click `event` ends a press that closed an overlay. */\nexport function isDismissClick(event: Event): boolean {\n  return dismissClicks.has(event);\n}\n","import { useRef, useEffect, useState, useCallback, type RefObject } from 'react';\nimport cx from 'classnames';\nimport BodyEnd from 'components/common/bodyEnd';\n\nimport styles from './popover.module.pcss';\nimport sheetStyles from '../styles/sheet.module.pcss';\nimport SheetBackdrop from '../sheetBackdrop';\nimport { usePopoverPosition } from './usePopoverPosition';\nimport { useFocusBoundary, tabbablesIn } from '../hooks/useFocusBoundary';\nimport { useSheet } from '../hooks/useSheet';\nimport { useOverlay } from '../hooks/useOverlay';\nimport { useComponentDefaults, usePresentationDefault } from '../configProvider';\nimport { markDismiss } from '../utils/dismissClick';\nimport { Presentation } from '../configProvider/presentation.types';\nimport { PopoverPlacement, PopoverAlign, PopoverTrigger, type PopoverProps } from './popover.types';\n\n/**\n * How long after its click an anchored popover still counts as opened by it: long enough for the\n * render the click causes, short enough that an old keypress does not decide a later open.\n */\nconst ACTIVATION_WINDOW_MS = 1000;\n\nfunction toReference(\n  anchor: HTMLElement | null | undefined,\n  triggerRef: RefObject<HTMLElement | null>,\n): RefObject<HTMLElement | null> {\n  return anchor ? { current: anchor } : triggerRef;\n}\n\nfunction Popover(props: PopoverProps) {\n  const {\n    children = null,\n    anchor,\n    content = null,\n    placement = PopoverPlacement.Bottom,\n    align = PopoverAlign.Center,\n    trigger = PopoverTrigger.Click,\n    isOpen,\n    defaultOpen = false,\n    onOpenChange = () => {},\n    offset = 10,\n    withArrow = true,\n    closeOnOutsideClick = true,\n    closeOnEscape = true,\n    openDelay = 100,\n    closeDelay = 120,\n    disabled = false,\n    focusOnOpen = 'keyboard',\n    closeOnTriggerClick = true,\n    width = 'auto',\n    constrainWidth = true,\n    presentation: presentationProp,\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    ...rest\n  } = useComponentDefaults('Popover', props);\n  const triggerRef = useRef<HTMLDivElement>(null);\n  const floatingRef = useRef<HTMLDivElement>(null);\n  const panelRef = useRef<HTMLDivElement>(null);\n  const arrowRef = useRef<HTMLDivElement>(null);\n  const hoverTimer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n  // Set from a capture handler on the panel, which React fires for its whole\n  // React subtree — including children that portal elsewhere in the DOM, such as\n  // a Dropdown's list inside a DatePicker's calendar. A DOM `contains` check\n  // cannot see those, and without this the panel closes when one is used.\n  const panelInteractedRef = useRef(false);\n  // An outside click is a press that starts and ends outside while the panel is open. One that\n  // starts inside is a drag, such as selecting a field's text, and Chromium and WebKit send its\n  // `mouseup` to wherever it ends. One that started before the panel opened is the gesture that\n  // opened it, such as Calendar's drag to create, which opens an editor on `pointerup`.\n  const pressStartedOutsideRef = useRef(false);\n  // Whether the open that is running came from the keyboard, which is what the\n  // default `focusOnOpen` follows and what `onOpenChange` reports. A click raised\n  // by Enter or Space reports `detail === 0`, where a pointer press counts the\n  // clicks; an open with no trigger event at all, such as a panel that appears\n  // while the user types, leaves this false.\n  const openedByKeyboardRef = useRef(false);\n  // A nested portal sits outside the panel's DOM; this is how focus leaving one counts as leaving.\n  const lastPanelFocusRef = useRef<EventTarget | null>(null);\n  function markPanelInteraction() {\n    panelInteractedRef.current = true;\n  }\n  function markPanelFocus(event: React.FocusEvent) {\n    markPanelInteraction();\n    lastPanelFocusRef.current = event.target;\n  }\n\n  // What the panel is placed against, and where the focus goes back to. A new object exactly when\n  // `anchor` changes, which is what restarts the position tracking for the new element: state, not\n  // a memo, because a memo React chose to drop would restart it for no reason.\n  const [reference, setReference] = useState(() => ({ anchor, ref: toReference(anchor, triggerRef) }));\n  if (reference.anchor !== anchor) setReference({ anchor, ref: toReference(anchor, triggerRef) });\n  const referenceRef = reference.ref;\n\n  // An anchored popover is opened by its consumer through `isOpen`, so none of the key handling\n  // below sees how. The click that caused it does: Enter and Space raise one with `detail === 0`,\n  // the same test the trigger uses. Not `:focus-visible`, which a text field matches after a\n  // mouse click too, and which stays on a keyboard-focused button that is then clicked.\n  const activationRef = useRef<{ target: Node; keyboard: boolean; time: number } | null>(null);\n  const anchored = anchor !== undefined;\n  useEffect(() => {\n    if (!anchored) return;\n    function onClick(event: MouseEvent) {\n      activationRef.current = { target: event.target as Node, keyboard: event.detail === 0, time: performance.now() };\n    }\n    // Capturing, so it is recorded before the consumer's own handler opens the panel.\n    document.addEventListener('click', onClick, true);\n    return () => document.removeEventListener('click', onClick, true);\n  }, [anchored]);\n\n  const isControlled = isOpen !== undefined;\n  const [internalOpen, setInternalOpen] = useState(defaultOpen);\n  const open = isControlled ? isOpen : internalOpen;\n\n  // Keep the outside-click/Escape listeners pointed at the latest setter without re-subscribing.\n  const onOpenChangeRef = useRef(onOpenChange);\n  useEffect(() => {\n    onOpenChangeRef.current = onOpenChange;\n  });\n\n  const setOpen = useCallback(\n    (next: boolean, fromKeyboard = false) => {\n      if (disabled && next) return;\n      if (next) openedByKeyboardRef.current = fromKeyboard;\n      if (!isControlled) setInternalOpen(next);\n      onOpenChangeRef.current(next, { fromKeyboard });\n    },\n    [disabled, isControlled],\n  );\n\n  // Explicit prop → this component's global default → the app-wide one → anchored.\n  const appPresentation = usePresentationDefault();\n  const presentation = presentationProp ?? appPresentation ?? Presentation.Default;\n  const { isSheet: sheetPresentation, sheetStyle } = useSheet(presentation);\n  // Hover and focus are pointer affordances that open something small next to what caused them;\n  // a sheet is a deliberate, screen-owning open. Only a click asks for one.\n  const isSheet = sheetPresentation && trigger === PopoverTrigger.Click;\n\n  // Focus into the panel waits until it shows, since one that waits out a moving ancestor hidden\n  // takes none.\n  const shownRef = useRef(false);\n  const focusPendingRef = useRef(false);\n  function focusPanel() {\n    focusPendingRef.current = false;\n    const focusables = tabbablesIn(panelRef.current);\n    if (focusables.length === 0) return;\n    // Only claim the dialog role when we're actually managing focus for real content.\n    floatingRef.current?.setAttribute('role', 'dialog');\n    focusables[0].focus();\n  }\n\n  // A sheet isn't anchored to anything, so there's nothing to track, flip or clamp.\n  usePopoverPosition({\n    isOpen: !!open && !isSheet,\n    placement,\n    align,\n    offset,\n    triggerRef: referenceRef,\n    floatingRef,\n    panelRef,\n    arrowRef: withArrow ? arrowRef : { current: null },\n    matchTriggerWidth: width === 'trigger',\n    onShown: () => {\n      shownRef.current = true;\n      if (focusPendingRef.current) focusPanel();\n    },\n  });\n\n  // Registered for as long as it's open, not just as a sheet: this is what puts the popover on\n  // the overlay stack, so an enclosing Modal knows something sits above it and neither drags\n  // focus out of this (portaled, so outside the modal's panel) panel nor closes itself on the\n  // Escape meant for this. Focus isn't trapped: a Popover is deliberately non-modal, and it\n  // already moves focus into the panel on open by itself.\n  const { restoreFocusToTrigger, focusAfterTrigger } = useFocusBoundary({\n    triggerRef: referenceRef,\n    contentRef: floatingRef,\n  });\n  const restoreFocusRef = useRef(restoreFocusToTrigger);\n  useEffect(() => {\n    restoreFocusRef.current = restoreFocusToTrigger;\n  });\n\n  // Focus inside the panel returns to the trigger however it closes, as with `hidePopover()`. Read\n  // in a ref cleanup, while the panel is still in the DOM; moved in an effect, because StrictMode\n  // detaches a new panel's refs once more right after it took the focus.\n  const focusLeftWithPanelRef = useRef(false);\n  const floatingCallbackRef = useCallback((node: HTMLDivElement | null) => {\n    floatingRef.current = node;\n    focusLeftWithPanelRef.current = false;\n    if (!node) return;\n    return () => {\n      floatingRef.current = null;\n      const active = document.activeElement;\n      focusLeftWithPanelRef.current = !!active && (node.contains(active) || active === lastPanelFocusRef.current);\n    };\n  }, []);\n  useEffect(() => {\n    if (open || !focusLeftWithPanelRef.current) return;\n    focusLeftWithPanelRef.current = false;\n    const active = document.activeElement;\n    if (!active || active === document.body) restoreFocusRef.current();\n  }, [open]);\n\n  useOverlay({\n    isActive: !!open,\n    containerRef: panelRef,\n    onEscape: closeOnEscape\n      ? () => {\n          // Hand focus back to the trigger when it currently sits inside the panel.\n          if (floatingRef.current?.contains(document.activeElement)) restoreFocusToTrigger();\n          setOpen(false, true);\n        }\n      : undefined,\n    trapFocus: false,\n    lockScroll: isSheet,\n  });\n\n  // The panel is portaled to the end of the DOM, so Tab never flows into it naturally. When we\n  // manage focus (click trigger + focusable content), move focus in on open and, on Tab out of\n  // either end, close and continue focus as if the panel sat right after the trigger.\n  useEffect(() => {\n    if (!open) {\n      openedByKeyboardRef.current = false;\n      shownRef.current = false;\n      focusPendingRef.current = false;\n      return;\n    }\n    const activation = activationRef.current;\n    activationRef.current = null;\n    const anchorKeyed =\n      !!anchor &&\n      !!activation?.keyboard &&\n      anchor.contains(activation.target) &&\n      performance.now() - activation.time < ACTIVATION_WINDOW_MS;\n    const takeFocus =\n      focusOnOpen === true || (focusOnOpen === 'keyboard' && (openedByKeyboardRef.current || anchorKeyed));\n    if (!takeFocus || trigger !== PopoverTrigger.Click) return;\n    if (shownRef.current) focusPanel();\n    else focusPendingRef.current = true;\n  }, [open, trigger, focusOnOpen, anchor]);\n\n  function handleFloatingKeyDown(event: React.KeyboardEvent) {\n    if (event.key !== 'Tab' || trigger !== PopoverTrigger.Click) return;\n    const focusables = tabbablesIn(panelRef.current);\n    if (focusables.length === 0) return;\n    const active = document.activeElement;\n    if (!event.shiftKey && active === focusables[focusables.length - 1]) {\n      event.preventDefault();\n      focusAfterTrigger();\n      setOpen(false, true);\n    } else if (event.shiftKey && active === focusables[0]) {\n      event.preventDefault();\n      restoreFocusToTrigger();\n      setOpen(false, true);\n    }\n  }\n\n  // Outside click. Registered only while open and only for the relevant trigger mode; Escape is\n  // handled by `useOverlay` above, which scopes it to the innermost open layer.\n  useEffect(() => {\n    if (!open) return;\n    // Focus that landed in the panel before these listeners existed (ours on open, or content\n    // that focuses itself on mount) set the flag with nothing to clear it, and the first\n    // click outside was swallowed.\n    panelInteractedRef.current = false;\n    pressStartedOutsideRef.current = false;\n\n    function containsNode(node: Node) {\n      // Both trigger refs: with an anchor, `children` still toggle the panel, and a press on them is theirs.\n      return Boolean(\n        floatingRef.current?.contains(node) ||\n        triggerRef.current?.contains(node) ||\n        referenceRef.current?.contains(node),\n      );\n    }\n    function isInside(event: Event) {\n      // Read and clear: the flag describes this one event, and the panel's React\n      // handler has already run by the time this does.\n      const inPanelSubtree = panelInteractedRef.current;\n      panelInteractedRef.current = false;\n      return inPanelSubtree || containsNode(event.target as Node);\n    }\n    function onMouseDown(event: MouseEvent) {\n      pressStartedOutsideRef.current = !isInside(event);\n    }\n    function onMouseUp(event: MouseEvent) {\n      const pressStartedOutside = pressStartedOutsideRef.current;\n      pressStartedOutsideRef.current = false;\n      if (isInside(event) || !pressStartedOutside) return;\n      markDismiss();\n      setOpen(false);\n    }\n    // Keyboard counterpart to the outside click. Only focus that comes from inside closes it: code\n    // moving focus elsewhere (a Modal restoring it) must not, nor `null`, focus back from another tab.\n    function onFocusIn(event: FocusEvent) {\n      if (isInside(event)) return;\n      const from = event.relatedTarget as Node | null;\n      if (!from || (from !== lastPanelFocusRef.current && !containsNode(from))) return;\n      // A press on an outside field focuses it before its `mouseup`, and that is not the keyboard.\n      // It closes here, before the `mouseup` that would mark its click, so it marks it now.\n      const pressed = pressStartedOutsideRef.current;\n      if (pressed) markDismiss();\n      setOpen(false, !pressed);\n    }\n\n    if (closeOnOutsideClick && trigger !== PopoverTrigger.Hover) {\n      window.addEventListener('mousedown', onMouseDown);\n      window.addEventListener('mouseup', onMouseUp);\n      document.addEventListener('focusin', onFocusIn);\n    }\n    return () => {\n      window.removeEventListener('mousedown', onMouseDown);\n      window.removeEventListener('mouseup', onMouseUp);\n      document.removeEventListener('focusin', onFocusIn);\n    };\n  }, [open, closeOnOutsideClick, trigger, setOpen, referenceRef]);\n\n  useEffect(() => () => clearTimeout(hoverTimer.current), []);\n\n  function scheduleHover(next: boolean) {\n    clearTimeout(hoverTimer.current);\n    hoverTimer.current = setTimeout(() => setOpen(next), next ? openDelay : closeDelay);\n  }\n\n  // Opening from the keyboard without the trigger having to be a button that\n  // happens to toggle on Enter: the arrow keys are what the menu-button pattern\n  // asks for, and what a consumer reaches for first.\n  function handleTriggerKeyDown(event: React.KeyboardEvent) {\n    if (open || (event.key !== 'ArrowDown' && event.key !== 'ArrowUp')) return;\n    event.preventDefault();\n    setOpen(true, true);\n  }\n\n  const triggerHandlers =\n    trigger === PopoverTrigger.Click\n      ? {\n          onClick: (event: React.MouseEvent) => {\n            // A field that opens a picker keeps it open when clicked again, so\n            // that placing the caret in the field does not close the panel.\n            if (open && !closeOnTriggerClick) return;\n            setOpen(!open, event.detail === 0);\n          },\n          onKeyDown: handleTriggerKeyDown,\n        }\n      : trigger === PopoverTrigger.Hover\n        ? { onMouseEnter: () => scheduleHover(true), onMouseLeave: () => scheduleHover(false) }\n        : { onFocus: () => setOpen(true), onBlur: () => setOpen(false) };\n\n  const floatingHoverHandlers =\n    trigger === PopoverTrigger.Hover\n      ? { onMouseEnter: () => scheduleHover(true), onMouseLeave: () => scheduleHover(false) }\n      : {};\n\n  return (\n    <div\n      {...rest}\n      className={cx(styles.popover, className, classNames?.root)}\n      style={{ ...style, ...slotStyles?.root }}\n    >\n      <div\n        ref={triggerRef}\n        className={cx(styles.popoverTrigger, classNames?.trigger)}\n        style={slotStyles?.trigger}\n        {...triggerHandlers}\n      >\n        {children}\n      </div>\n      {open && (\n        <BodyEnd>\n          {isSheet && (\n            <SheetBackdrop\n              onClose={() => {\n                if (closeOnOutsideClick) setOpen(false);\n              }}\n              className={classNames?.backdrop}\n              style={slotStyles?.backdrop}\n            />\n          )}\n          <div\n            ref={floatingCallbackRef}\n            className={cx(isSheet ? sheetStyles.sheet : styles.popoverFloating, classNames?.floating)}\n            style={{ ...(isSheet ? sheetStyle : undefined), ...slotStyles?.floating }}\n            data-placement={placement}\n            onKeyDown={handleFloatingKeyDown}\n            onMouseDownCapture={markPanelInteraction}\n            onMouseUpCapture={markPanelInteraction}\n            onFocusCapture={markPanelFocus}\n            {...floatingHoverHandlers}\n          >\n            <div\n              ref={panelRef}\n              className={cx(\n                styles.popoverPanel,\n                constrainWidth && styles.popoverPanelConstrained,\n                isSheet && sheetStyles.sheetPanel,\n                classNames?.panel,\n              )}\n              style={slotStyles?.panel}\n            >\n              {content}\n            </div>\n            {withArrow && !isSheet && (\n              <div ref={arrowRef} className={cx(styles.popoverArrow, classNames?.arrow)} style={slotStyles?.arrow} />\n            )}\n          </div>\n        </BodyEnd>\n      )}\n    </div>\n  );\n}\n\nexport default Popover;\n",".breadcrumb {\n  font-family: var(--default-font);\n  font-size: var(--default-font-size);\n\n  &-list {\n    display: flex;\n    flex-wrap: wrap;\n    align-items: center;\n    gap: 6px;\n    margin: 0;\n    padding: 0;\n    list-style: none;\n  }\n\n  &-item {\n    display: flex;\n    align-items: center;\n    gap: 6px;\n  }\n\n  &-icon {\n    margin-right: 6px;\n    font-size: 0.9em;\n  }\n\n  &-link {\n    display: flex;\n    align-items: center;\n    text-decoration: none;\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n    transition: color 0.15s;\n\n    /* Explicit :link/:visited beats the browser default a:visited { color: purple } on\n       specificity, otherwise every crumb using the same href (e.g. \"#\") turns purple as\n       soon as any one of them is clicked. */\n    &:link,\n    &:visited {\n      color: var(--text-dark);\n    }\n\n    &:hover {\n      color: var(--primary-color);\n    }\n\n    &-disabled {\n      color: var(--text-dark);\n      opacity: 0.5;\n      cursor: default;\n      pointer-events: none;\n    }\n  }\n\n  &-current {\n    display: flex;\n    align-items: center;\n    color: var(--text-color);\n    font-weight: var(--header-font-weight, 600);\n  }\n\n  &-separator {\n    display: flex;\n    align-items: center;\n    color: var(--text-dark);\n    font-size: 0.75em;\n    opacity: 0.7;\n  }\n\n  /* Two classes, to outrank Button's font-size and ActionIcon's box whichever stylesheet loads\n     first. */\n  & &-ellipsis {\n    width: 1.8em;\n    height: 1.8em;\n    font-size: 1em;\n\n    &:hover {\n      color: var(--primary-color);\n    }\n  }\n\n  /* The popover panel pads free content; this list pads its own rows instead, so\n     that a row's hover highlight reaches the panel edge. */\n  &-hiddenPanel {\n    padding: 0;\n  }\n\n  &-hiddenList {\n    display: flex;\n    flex-direction: column;\n    gap: 2px;\n    margin: 0;\n    padding: 6px;\n    list-style: none;\n    min-width: 160px;\n  }\n\n  &-hiddenItem {\n    display: flex;\n\n    .breadcrumb-link {\n      width: 100%;\n      padding: 6px 8px;\n      border-radius: var(--default-border-radius);\n\n      /* `:focus` as well, because the panel takes the focus when the keyboard\n         opens it. Not `--background-accent-light`: it is the same colour as that\n         panel in the default theme. */\n      &:hover,\n      &:focus {\n        background: var(--background-accent);\n      }\n    }\n  }\n}\n","import { useState } from 'react';\nimport type { MouseEvent, ReactElement } from 'react';\nimport cx from 'classnames';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faChevronRight, faEllipsis } from '@fortawesome/free-solid-svg-icons';\n\nimport InteractableDiv from '../interactableDiv';\nimport ActionIcon from '../actionIcon';\nimport Popover from '../popover';\nimport { PopoverAlign, PopoverPlacement } from '../popover/popover.types';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport type { BreadcrumbItem, BreadcrumbLabels, BreadcrumbProps } from './breadcrumb.types';\n\nimport styles from './breadcrumb.module.pcss';\nimport { renderIcon } from '../utils/renderIcon';\nimport { useLinkClick } from '../utils/navigation';\n\ninterface IndexedItem {\n  item: BreadcrumbItem;\n  index: number;\n}\n\nconst DEFAULT_LABELS: Required<BreadcrumbLabels> = {\n  showHidden: 'Show hidden breadcrumbs',\n};\n\nfunction getGroups(items: BreadcrumbItem[], maxItems?: number) {\n  const indexed = items.map((item, index) => ({ item, index }));\n  if (!maxItems || items.length <= maxItems) {\n    return { leading: indexed, hidden: [] as IndexedItem[], trailing: [] as IndexedItem[] };\n  }\n\n  const trailingCount = Math.max(1, maxItems - 2);\n  const hiddenCount = items.length - 1 - trailingCount;\n  if (hiddenCount <= 0) {\n    return { leading: indexed, hidden: [] as IndexedItem[], trailing: [] as IndexedItem[] };\n  }\n\n  return {\n    leading: indexed.slice(0, 1),\n    hidden: indexed.slice(1, 1 + hiddenCount),\n    trailing: indexed.slice(1 + hiddenCount),\n  };\n}\n\nfunction Breadcrumb(props: BreadcrumbProps): ReactElement {\n  const {\n    items = [],\n    separator,\n    maxItems,\n    'aria-label': ariaLabel = 'Breadcrumb',\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    labels,\n    ...rest\n  } = useComponentDefaults('Breadcrumb', props);\n  const text = useLabels('Breadcrumb', labels, DEFAULT_LABELS);\n  const followLink = useLinkClick();\n\n  const [isHiddenMenuOpen, setIsHiddenMenuOpen] = useState(false);\n  const { leading, hidden, trailing } = getGroups(items, maxItems);\n  const lastIndex = items.length - 1;\n\n  function renderSeparatorAfter(index: number) {\n    if (index === lastIndex) return null;\n    return (\n      <span className={cx(styles.breadcrumbSeparator, classNames?.separator)} style={slotStyles?.separator}>\n        {separator ?? <FontAwesomeIcon icon={faChevronRight} />}\n      </span>\n    );\n  }\n\n  function renderItem({ item, index }: IndexedItem) {\n    return (\n      <li key={item.id ?? index} className={cx(styles.breadcrumbItem, classNames?.item)} style={slotStyles?.item}>\n        {renderCrumb(item, index === lastIndex, styles, classNames, slotStyles, followLink)}\n        {renderSeparatorAfter(index)}\n      </li>\n    );\n  }\n\n  return (\n    <nav\n      {...rest}\n      aria-label={ariaLabel}\n      className={cx(styles.breadcrumb, classNames?.root, className)}\n      style={{ ...style, ...slotStyles?.root }}\n    >\n      <ol className={cx(styles.breadcrumbList, classNames?.list)} style={slotStyles?.list}>\n        {leading.map(renderItem)}\n\n        {hidden.length > 0 && (\n          <li className={cx(styles.breadcrumbItem, classNames?.item)} style={slotStyles?.item}>\n            <Popover\n              isOpen={isHiddenMenuOpen}\n              onOpenChange={setIsHiddenMenuOpen}\n              placement={PopoverPlacement.Bottom}\n              align={PopoverAlign.Start}\n              // The list brings its own padding and width.\n              constrainWidth={false}\n              classNames={{ panel: styles.breadcrumbHiddenPanel }}\n              content={\n                <ul className={cx(styles.breadcrumbHiddenList, classNames?.hiddenList)} style={slotStyles?.hiddenList}>\n                  {hidden.map(({ item, index }) => (\n                    <li\n                      key={item.id ?? index}\n                      className={cx(styles.breadcrumbHiddenItem, classNames?.hiddenItem)}\n                      style={slotStyles?.hiddenItem}\n                    >\n                      {renderCrumb(\n                        {\n                          ...item,\n                          onClick: (event) => {\n                            item.onClick?.(event);\n                            setIsHiddenMenuOpen(false);\n                          },\n                        },\n                        false,\n                        styles,\n                        classNames,\n                        slotStyles,\n                        followLink,\n                      )}\n                    </li>\n                  ))}\n                </ul>\n              }\n            >\n              <ActionIcon\n                icon={faEllipsis}\n                color='var(--text-dark)'\n                title={text.showHidden}\n                aria-label={text.showHidden}\n                aria-expanded={isHiddenMenuOpen}\n                className={cx(styles.breadcrumbEllipsis, classNames?.ellipsis)}\n                style={slotStyles?.ellipsis}\n              />\n            </Popover>\n            {renderSeparatorAfter(hidden[hidden.length - 1].index)}\n          </li>\n        )}\n\n        {trailing.map(renderItem)}\n      </ol>\n    </nav>\n  );\n}\n\nfunction renderCrumb(\n  item: BreadcrumbItem,\n  isCurrent: boolean,\n  styles: Record<string, string>,\n  classNames: BreadcrumbProps['classNames'],\n  slotStyles: BreadcrumbProps['styles'],\n  followLink: (event: MouseEvent<HTMLAnchorElement>) => void,\n) {\n  const content = (\n    <>\n      {item.icon\n        ? renderIcon(item.icon, {\n            className: cx(styles.breadcrumbIcon, classNames?.icon),\n            style: slotStyles?.icon,\n          })\n        : null}\n      {item.label}\n    </>\n  );\n\n  if (isCurrent) {\n    return (\n      <span\n        aria-current='page'\n        className={cx(styles.breadcrumbCurrent, classNames?.current)}\n        style={slotStyles?.current}\n      >\n        {content}\n      </span>\n    );\n  }\n\n  if (item.disabled) {\n    return (\n      <span\n        className={cx(styles.breadcrumbLink, styles.breadcrumbLinkDisabled, classNames?.link)}\n        style={slotStyles?.link}\n      >\n        {content}\n      </span>\n    );\n  }\n\n  if (item.href) {\n    return (\n      <a\n        href={item.href}\n        onClick={(event) => {\n          item.onClick?.(event);\n          followLink(event);\n        }}\n        className={cx(styles.breadcrumbLink, classNames?.link)}\n        style={slotStyles?.link}\n      >\n        {content}\n      </a>\n    );\n  }\n\n  if (item.onClick) {\n    return (\n      <InteractableDiv\n        onClick={item.onClick}\n        className={cx(styles.breadcrumbLink, classNames?.link)}\n        style={slotStyles?.link}\n      >\n        {content}\n      </InteractableDiv>\n    );\n  }\n\n  return (\n    <span\n      className={cx(styles.breadcrumbLink, styles.breadcrumbLinkDisabled, classNames?.link)}\n      style={slotStyles?.link}\n    >\n      {content}\n    </span>\n  );\n}\n\nexport default Breadcrumb;\n","import type { CSSProperties, ReactNode } from 'react';\nimport type { HtmlProps } from '../types/slots.types';\n\nexport enum ButtonGroupOrientation {\n  Horizontal = 'horizontal',\n  Vertical = 'vertical',\n}\n\nexport interface ButtonGroupProps extends HtmlProps {\n  children?: ReactNode;\n  orientation?: ButtonGroupOrientation;\n  className?: string;\n  style?: CSSProperties;\n}\n",".buttonGroup {\n  display: inline-flex;\n  align-items: stretch;\n\n  /* At a fractional scale two overlapping borders round to different device pixels, so each seam\n     keeps one: the earlier child clips its shared 1px, or the next one does while the earlier has\n     the focus (docs/ButtonGroup.md). Reset per group so a nested one starts clean. */\n  --group-clip-start: initial;\n  --group-clip-end: initial;\n\n  /* Each rule below targets its position with a pseudo-class (:first-child, :last-child, or\n     :not()), which contributes extra specificity beyond a plain class selector. That's what\n     reliably beats a child component's own base border-radius rule (e.g. Button's), regardless\n     of which CSS module happens to load first in the final bundle — a plain \".buttonGroup > *\"\n     reset would tie on specificity with Button's own class and depend on load order.\n\n     Alongside the direct border-radius reset (for a child whose visible border lives right on\n     its own root, e.g. Button), each rule also sets --group-radius-* custom properties. Unlike\n     border-radius itself, custom properties inherit through every descendant regardless of\n     nesting depth, so a child whose actual border lives on a nested element (e.g. Input's\n     `fieldset`, buried inside a `<label>`) can flatten correctly too, as long as that nested\n     element's own border-radius reads `var(--group-radius-tl, var(--default-border-radius))`\n     etc. instead of hardcoding `var(--default-border-radius)` — see input.module.pcss.\n\n     A nested ButtonGroup (role=\"group\") is excluded throughout: it manages its own corners and\n     spacing internally, so the outer group only arranges it as a self-contained unit rather than\n     reaching into it. */\n  > *:not(:first-child):not(:last-child):not([role='group']) {\n    border-radius: 0;\n\n    --group-radius-tl: 0;\n    --group-radius-tr: 0;\n    --group-radius-br: 0;\n    --group-radius-bl: 0;\n  }\n\n  > *:hover,\n  > *:focus-within {\n    z-index: 1;\n  }\n\n  &:not(&-vertical) {\n    /* A separator (e.g. a vertical Divider) is meant to stay visible between two buttons, not\n       merge into one of them, so it's excluded from the border-collapsing overlap below — and\n       so is whatever comes right after one, since that overlap is only meant to collapse two\n       adjacent button borders, not eat into the separator itself. A nested ButtonGroup is\n       excluded the same way, and given real spacing instead, rather than being squeezed 1px\n       into whatever's next to it. */\n    > *:not(:first-child):not([role='separator']):not([role='group']) {\n      margin-left: -1px;\n    }\n\n    > *:not([role='separator']):not([role='group']):has(+ :not([role='separator']):not([role='group'])) {\n      --group-clip-end: 1px;\n\n      clip-path: inset(-100vmax var(--group-clip-end, -100vmax) -100vmax var(--group-clip-start, -100vmax));\n    }\n\n    > *:not([role='separator']):not([role='group']):has(+ :not([role='separator']):not([role='group'])):focus-within {\n      --group-clip-end: initial;\n    }\n\n    > :focus-within:not([role='separator']):not([role='group']) + *:not([role='separator']):not([role='group']) {\n      --group-clip-start: 1px;\n\n      clip-path: inset(-100vmax var(--group-clip-end, -100vmax) -100vmax var(--group-clip-start, -100vmax));\n    }\n\n    /* Matches the specificity of the collapse rule above (three :not()s deep, not two) so this\n       reliably wins the override regardless of source order — the collapse rule's own three\n       :not()s (:first-child, [role=separator], [role=group]) outweigh an adjacency selector with\n       only one, so a fourth, redundant :not() is added here purely to close that gap. The\n       trailing :not([role='group']) on both lines also keeps this from matching when the\n       following element is itself a nested group — that case must fall through to the dedicated\n       8px group-spacing rule below, not get zeroed. */\n    > [role='separator'] + *:not(:first-child):not([role='group']),\n    > [role='group'] + *:not(:first-child):not([role='separator']):not([role='group']) {\n      margin-left: 0;\n    }\n\n    > [role='group']:not(:first-child) {\n      margin-left: 8px;\n    }\n\n    > *:first-child:not(:last-child):not([role='group']) {\n      border-top-right-radius: 0;\n      border-bottom-right-radius: 0;\n\n      --group-radius-tr: 0;\n      --group-radius-br: 0;\n    }\n\n    > *:last-child:not(:first-child):not([role='group']) {\n      border-top-left-radius: 0;\n      border-bottom-left-radius: 0;\n\n      --group-radius-tl: 0;\n      --group-radius-bl: 0;\n    }\n  }\n\n  &-vertical {\n    flex-direction: column;\n\n    > *:not(:first-child):not([role='separator']):not([role='group']) {\n      margin-top: -1px;\n    }\n\n    > *:not([role='separator']):not([role='group']):has(+ :not([role='separator']):not([role='group'])) {\n      --group-clip-end: 1px;\n\n      clip-path: inset(var(--group-clip-start, -100vmax) -100vmax var(--group-clip-end, -100vmax) -100vmax);\n    }\n\n    > *:not([role='separator']):not([role='group']):has(+ :not([role='separator']):not([role='group'])):focus-within {\n      --group-clip-end: initial;\n    }\n\n    > :focus-within:not([role='separator']):not([role='group']) + *:not([role='separator']):not([role='group']) {\n      --group-clip-start: 1px;\n\n      clip-path: inset(var(--group-clip-start, -100vmax) -100vmax var(--group-clip-end, -100vmax) -100vmax);\n    }\n\n    > [role='separator'] + *:not(:first-child):not([role='group']),\n    > [role='group'] + *:not(:first-child):not([role='separator']):not([role='group']) {\n      margin-top: 0;\n    }\n\n    > [role='group']:not(:first-child) {\n      margin-top: 8px;\n    }\n\n    > *:first-child:not(:last-child):not([role='group']) {\n      border-bottom-left-radius: 0;\n      border-bottom-right-radius: 0;\n\n      --group-radius-bl: 0;\n      --group-radius-br: 0;\n    }\n\n    > *:last-child:not(:first-child):not([role='group']) {\n      border-top-left-radius: 0;\n      border-top-right-radius: 0;\n\n      --group-radius-tl: 0;\n      --group-radius-tr: 0;\n    }\n  }\n}\n","import cx from 'classnames';\nimport type { ReactElement } from 'react';\n\nimport { ButtonGroupOrientation } from './buttonGroup.types';\nimport type { ButtonGroupProps } from './buttonGroup.types';\nimport styles from './buttonGroup.module.pcss';\n\nfunction ButtonGroup({\n  children,\n  orientation = ButtonGroupOrientation.Horizontal,\n  className,\n  style,\n  ...rest\n}: ButtonGroupProps): ReactElement {\n  return (\n    <div\n      {...rest}\n      role='group'\n      className={cx(\n        styles.buttonGroup,\n        { [styles.buttonGroupVertical]: orientation === ButtonGroupOrientation.Vertical },\n        className,\n      )}\n      style={style}\n    >\n      {children}\n    </div>\n  );\n}\n\nexport default ButtonGroup;\n","import type { Ref } from 'react';\n\n/**\n * Fans one element out to several refs, for a component that needs the node itself *and*\n * hands it to the consumer through a `ref` prop.\n *\n * The returned callback is a new function on every render, so React detaches and reattaches\n * it each time (null, then the node). Every ref here is therefore written twice per render,\n * which is fine for storing a node and wrong for anything with a side effect. Call it inline\n * rather than memoising: a memo would have to list the refs it closes over, and a consumer\n * passing an inline callback ref changes that identity every render anyway.\n */\nexport function mergeRefs<T>(...refs: (Ref<T> | undefined)[]): (node: T | null) => void {\n  return (node) => {\n    for (const ref of refs) {\n      if (typeof ref === 'function') ref(node);\n      else if (ref) (ref as { current: T | null }).current = node;\n    }\n  };\n}\n","// A day is a `'YYYY-MM-DD'` string, the value of `<input type=\"date\">`: no time and no time zone,\n// so it is the same day for everyone. A `Date` is a moment and cannot be one.\n//\n// Dates are built with `setFullYear` rather than the `Date` constructor or `Date.UTC`, which read\n// the years 0 to 99 as 1900 to 1999.\n\nexport interface DayParts {\n  year: number;\n  /** 1 to 12. */\n  month: number;\n  day: number;\n}\n\nconst DAY = /^(\\d{4})-(\\d{2})-(\\d{2})$/;\n\nexport function daysInMonth({ year, month }: Pick<DayParts, 'year' | 'month'>): number {\n  const date = new Date(0);\n  date.setUTCFullYear(year, month, 0);\n  return date.getUTCDate();\n}\n\n/** The parts of a `'YYYY-MM-DD'` string, or `null` for anything else, 31 February included. */\nexport function parseDay(value: unknown): DayParts | null {\n  if (typeof value !== 'string') return null;\n  const match = DAY.exec(value);\n  if (!match) return null;\n  const year = Number(match[1]);\n  const month = Number(match[2]);\n  const day = Number(match[3]);\n  if (month < 1 || month > 12 || day < 1 || day > daysInMonth({ year, month })) return null;\n  return { year, month, day };\n}\n\n/** `value` itself when it is a `'YYYY-MM-DD'` day that exists, otherwise `null`. */\nexport function asDay(value: unknown): string | null {\n  return parseDay(value) ? (value as string) : null;\n}\n\nexport function formatDay({ year, month, day }: DayParts): string {\n  return `${String(year).padStart(4, '0')}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`;\n}\n\n/** The day `date` falls on in the browser's time zone, as `'YYYY-MM-DD'`; `''` for an Invalid Date. */\nexport function dateToDay(date: Date): string {\n  if (Number.isNaN(date.getTime())) return '';\n  return formatDay({ year: date.getFullYear(), month: date.getMonth() + 1, day: date.getDate() });\n}\n\n/** The first moment of a `'YYYY-MM-DD'` day in the browser's time zone. An Invalid Date for anything else. */\nexport function dayToDate(day: string): Date {\n  const parts = parseDay(day);\n  if (!parts) return new Date(NaN);\n  const date = new Date(2000, 0, 1);\n  date.setFullYear(parts.year, parts.month - 1, parts.day);\n  return date;\n}\n\nconst TIME = /^(\\d{2}):(\\d{2})$/;\n\n/** The `'HH:MM'` of `date` in the browser's time zone, the value a TimeInput holds; `''` for an Invalid Date. */\nexport function dateToTime(date: Date): string {\n  if (Number.isNaN(date.getTime())) return '';\n  return `${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`;\n}\n\n/**\n * The moment a `'YYYY-MM-DD'` day and an `'HH:MM'` time stand for in the browser's time zone,\n * DatePicker's and TimeInput's values together. A time the clock skips that day resolves forwards\n * (02:30 becomes 03:30), a repeated one to its first occurrence. An Invalid Date for anything else.\n */\nexport function dayTimeToDate(day: string, time: string): Date {\n  const date = dayToDate(day);\n  const match = TIME.exec(time);\n  const hours = match ? Number(match[1]) : NaN;\n  const minutes = match ? Number(match[2]) : NaN;\n  if (Number.isNaN(date.getTime()) || !(hours <= 23 && minutes <= 59)) return new Date(NaN);\n  date.setHours(hours, minutes, 0, 0);\n  return date;\n}\n","import type { CSSProperties, ReactNode, Ref } from 'react';\nimport type { SlotClassNames, SlotStyles, HtmlProps } from '../types/slots.types';\nimport type { ColorValue } from '../themeProvider/color.types';\nimport type { Weekday } from '../utils/weekStart';\n\nexport enum CalendarView {\n  Month = 'month',\n  Week = 'week',\n  Day = 'day',\n  Agenda = 'agenda',\n}\n\n/** A day of the week, as `Date#getDay()` counts: 0 is Sunday, 1 is Monday. */\nexport type CalendarWeekday = Weekday;\n\n/** First day of the week, as `Date#getDay()` counts: 0 is Sunday, 1 is Monday. */\nexport type CalendarWeekStart = CalendarWeekday;\n\nexport interface CalendarEvent {\n  /** Unique among `events`: each occurrence of a recurring series needs an id of its own. */\n  id: string | number;\n  title: string;\n  /** Local time. */\n  start: Date;\n  /** Local time, exclusive. An all-day event on the 26th ends at midnight on the 27th. */\n  end: Date;\n  /** Shown in the all-day row instead of the time grid. Timed events of 24 hours or more are too. */\n  allDay?: boolean;\n  /** A `ThemeColor` or any CSS colour. Falls back to `--primary-color`. */\n  color?: ColorValue;\n  /** Can be clicked but not moved or resized. */\n  readOnly?: boolean;\n}\n\n/** A span of local time. `end` is exclusive. */\nexport interface CalendarRange {\n  start: Date;\n  end: Date;\n}\n\nexport interface CalendarEventChange<E extends CalendarEvent = CalendarEvent> {\n  event: E;\n  start: Date;\n  end: Date;\n}\n\nexport interface CalendarSlot {\n  start: Date;\n  end: Date;\n  allDay: boolean;\n}\n\n/** Where `renderEvent` draws an event. */\nexport interface CalendarEventContext {\n  /**\n   * The event has one line: an event under 45 minutes in the week and day views, an all-day bar,\n   * or a row of the month view. The day and agenda lists have room for more.\n   */\n  singleLine: boolean;\n}\n\nexport type CalendarSlots = 'root' | 'header' | 'dayHeader' | 'cell' | 'event';\n\n/** Text Calendar renders of its own, for translating it. Dates and times come from `locale`. */\nexport interface CalendarLabels {\n  /** Previous period button (default `Previous`). */\n  previous?: string;\n  /** Next period button (default `Next`). */\n  next?: string;\n  /** Jump to today (default `Today`). */\n  today?: string;\n  /** Name of the view switcher, read out by screen readers (default `Calendar view`). */\n  viewSwitcher?: string;\n  month?: string;\n  week?: string;\n  day?: string;\n  agenda?: string;\n  /** All-day row and all-day events (default `All day`). */\n  allDay?: string;\n  /** Overflow button in a month cell (default `+3 more`). */\n  more?: (count: number) => string;\n  /** Appended to a day's name for screen readers (default `3 events`). */\n  eventCount?: (count: number) => string;\n  /** A day or period without events (default `No events`). */\n  noEvents?: string;\n  /** Create button in the header and in a day's event list, shown when `onSlotCreate` is set (default `New event`). */\n  newEvent?: string;\n  /** A week number, shown with `showWeekNumbers` (default `Week 40`). */\n  weekNumber?: (week: number) => string;\n}\n\nexport interface CalendarProps<E extends CalendarEvent = CalendarEvent> extends HtmlProps<HTMLDivElement> {\n  /** The events to show. Only the ones inside the range `onRangeChange` reports are needed. */\n  events: E[];\n  /** Controlled: the selected day, `'YYYY-MM-DD'`, whose period is shown. Omit for uncontrolled. */\n  date?: string;\n  /** Day shown initially when uncontrolled, `'YYYY-MM-DD'` (default today). */\n  defaultDate?: string;\n  /** Called with the new day, `'YYYY-MM-DD'`: the value a DatePicker holds. */\n  onDateChange?: (day: string) => void;\n  /** Controlled view. Omit for uncontrolled. */\n  view?: CalendarView;\n  /** View shown initially when uncontrolled (default the first of `views`). */\n  defaultView?: CalendarView;\n  onViewChange?: (view: CalendarView) => void;\n  /** Views offered in the switcher, in order. The switcher is hidden when there is only one (default all four). */\n  views?: CalendarView[];\n  /** BCP 47 tag for dates, times and the week start (default the app's `locale`, else the browser's). */\n  locale?: string;\n  /** Overrides the locale's first day of the week. */\n  weekStartsOn?: CalendarWeekStart;\n  /** Minutes that creating, moving and resizing snap to (default `15`). */\n  slotMinutes?: number;\n  /** Days of the week left out of every view: `[0, 6]` hides the weekend. */\n  hiddenDays?: CalendarWeekday[];\n  /**\n   * Hour the week and day views open at, such as `6` for 06:00 (default an hour before now while\n   * today is shown, otherwise the start of `workingHours`, or 07:00).\n   */\n  scrollToHour?: number;\n  /**\n   * Working time in the week and day views, `[8, 17]` for 08:00 to 17:00: the hours outside it are\n   * shaded. `[22, 6]` runs overnight. Its start is also the hour those views open at when today is\n   * not shown and `scrollToHour` is not set.\n   */\n  workingHours?: readonly [start: number, end: number];\n  /** Shows ISO 8601 week numbers: on each row of the month view, and in the corner of the week and day views. */\n  showWeekNumbers?: boolean;\n  /**\n   * Called on mount and whenever the shown period changes, with the period and one either side of\n   * it, so the neighbours are already filled while swiping. Fetch events and expand recurring\n   * series for this range.\n   */\n  onRangeChange?: (range: CalendarRange) => void;\n  /** An event was clicked or tapped. `anchor` is its element, for positioning a popover. */\n  onEventClick?: (event: E, anchor: HTMLElement) => void;\n  /**\n   * An event was moved or resized. Setting it is what makes events draggable. Update `events`\n   * in the same render, or return a promise: the event then stays where it was dropped until the\n   * promise settles, and is drawn from `events` again after that, back in place if it rejected.\n   */\n  onEventChange?: (change: CalendarEventChange<E>) => void | Promise<unknown>;\n  /**\n   * Empty time was clicked or dragged over, or the header's New event button pressed. Setting it is\n   * what enables creating: a click creates an hour, or an all-day slot in the month view and the\n   * all-day row. `anchor` marks the slot on screen, for a popover's `anchor`: the first day's cell\n   * in the month view, the first day's header for the all-day row, an invisible box over the slot\n   * in the time grid, and the button itself from the header.\n   */\n  onSlotCreate?: (slot: CalendarSlot, anchor: HTMLElement) => void;\n  /**\n   * Content of an event, in place of its title. Must not contain interactive elements. Keep to the\n   * title where `context.singleLine` is set: further lines have no room there.\n   */\n  renderEvent?: (event: E, context: CalendarEventContext) => ReactNode;\n  /** A class for one event's element, next to `classNames.event`: a tentative, cancelled or selected event. */\n  eventClassName?: (event: E) => string | undefined;\n  /** The root element. */\n  ref?: Ref<HTMLDivElement>;\n\n  className?: string;\n  style?: CSSProperties;\n  classNames?: SlotClassNames<CalendarSlots>;\n  styles?: SlotStyles<CalendarSlots>;\n  /** Overrides the text Calendar renders of its own. Merged per key. */\n  labels?: CalendarLabels;\n}\n\n/**\n * The props `ConfigProvider` can set for every Calendar. The rest depend on the event type, and\n * `locale` is the provider's own.\n */\nexport type CalendarDefaults = Pick<\n  CalendarProps,\n  | 'labels'\n  | 'weekStartsOn'\n  | 'slotMinutes'\n  | 'views'\n  | 'hiddenDays'\n  | 'scrollToHour'\n  | 'workingHours'\n  | 'showWeekNumbers'\n>;\n","import { CalendarView, type CalendarRange, type CalendarSlot, type CalendarWeekStart } from './calendar.types';\n\n// All arithmetic goes through the local date fields rather than milliseconds: a day with a\n// daylight-saving change is 23 or 25 hours long, so adding 86 400 000 ms lands on the wrong day.\n\nexport const MINUTES_PER_DAY = 1440;\n\nexport function startOfDay(date: Date): Date {\n  return new Date(date.getFullYear(), date.getMonth(), date.getDate());\n}\n\n/** Midnight, `days` calendar days away. */\nexport function addDays(date: Date, days: number): Date {\n  return new Date(date.getFullYear(), date.getMonth(), date.getDate() + days);\n}\n\n/** The same wall-clock time, `days` calendar days away. */\nexport function shiftDays(date: Date, days: number): Date {\n  return new Date(\n    date.getFullYear(),\n    date.getMonth(),\n    date.getDate() + days,\n    date.getHours(),\n    date.getMinutes(),\n    date.getSeconds(),\n    date.getMilliseconds(),\n  );\n}\n\nexport function daysInMonth(year: number, month: number): number {\n  return new Date(year, month + 1, 0).getDate();\n}\n\n/** Midnight, `months` months away. The day is clamped, so 31 January plus one month is the end of February. */\nexport function addMonths(date: Date, months: number): Date {\n  const target = new Date(date.getFullYear(), date.getMonth() + months, 1);\n  const day = Math.min(date.getDate(), daysInMonth(target.getFullYear(), target.getMonth()));\n  return new Date(target.getFullYear(), target.getMonth(), day);\n}\n\nexport function startOfMonth(date: Date): Date {\n  return new Date(date.getFullYear(), date.getMonth(), 1);\n}\n\nexport function startOfWeek(date: Date, weekStartsOn: CalendarWeekStart): Date {\n  return addDays(date, -((date.getDay() - weekStartsOn + 7) % 7));\n}\n\n/** Calendar days from `a` to `b`, whatever their times of day. */\nexport function dayDiff(a: Date, b: Date): number {\n  const utcA = Date.UTC(a.getFullYear(), a.getMonth(), a.getDate());\n  const utcB = Date.UTC(b.getFullYear(), b.getMonth(), b.getDate());\n  return Math.round((utcB - utcA) / 86_400_000);\n}\n\nexport function isSameDay(a: Date, b: Date): boolean {\n  return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();\n}\n\n// `'2026-09-26'` is also the key for lookups and React keys. Never `getTime()`: where a clock\n// change happens at midnight (America/Santiago), the day's first moment is 01:00, and two paths to\n// the same day disagree.\nexport { dateToDay } from '../utils/day';\n\n/** Wall-clock minutes since midnight. On a 25-hour day the repeated hour maps onto itself. */\nexport function minutesOfDay(date: Date): number {\n  return date.getHours() * 60 + date.getMinutes();\n}\n\n/**\n * The wall-clock time `minutes` after the start of `day`; 1440 is the next midnight. A time the\n * clock skips resolves forwards (02:30 becomes 03:30) and a repeated one to its first occurrence.\n */\nexport function atMinutes(day: Date, minutes: number): Date {\n  return new Date(day.getFullYear(), day.getMonth(), day.getDate(), 0, minutes);\n}\n\n/**\n * `end`, unless a clock change turned the range around: where one end lands in the hour the\n * clocks skip, a wall-clock end can fall on or before its start. Then `minutes` of real time\n * after the start instead.\n */\nexport function endAfter(start: Date, end: Date, minutes: number): Date {\n  return end > start ? end : new Date(start.getTime() + minutes * 60_000);\n}\n\nconst NO_DAYS: ReadonlySet<number> = new Set();\n\n/**\n * The Week view of a narrow calendar: three shown days from the selected one, paged by three, where\n * seven columns would leave no room for a title. Not a `CalendarView`: the switcher still says Week.\n */\nexport const NARROW_WEEK = 'narrowWeek';\n\n/** What the period arithmetic is keyed by: a view, or the narrow calendar's Week. */\nexport type CalendarPeriod = CalendarView | typeof NARROW_WEEK;\n\n/** Shown days in a period that runs from the selected day, or 0 for one fixed to a week or month. */\nfunction rollingDays(period: CalendarPeriod): number {\n  if (period === CalendarView.Day) return 1;\n  return period === NARROW_WEEK ? 3 : 0;\n}\n\n/** Midnight of the first day of the period `date` is in. */\nexport function periodStart(period: CalendarPeriod, date: Date, weekStartsOn: CalendarWeekStart): Date {\n  if (rollingDays(period) > 0) return startOfDay(date);\n  return period === CalendarView.Week ? startOfWeek(date, weekStartsOn) : startOfMonth(date);\n}\n\n/** The days a view shows for `date`. The month view is always six full weeks. */\nexport function periodRange(\n  period: CalendarPeriod,\n  date: Date,\n  weekStartsOn: CalendarWeekStart,\n  hidden: ReadonlySet<number> = NO_DAYS,\n): CalendarRange {\n  const start = periodStart(period, date, weekStartsOn);\n  const rolling = rollingDays(period);\n  if (rolling > 0) return { start, end: addDays(stepShownDays(start, rolling - 1, hidden), 1) };\n  switch (period) {\n    case CalendarView.Month: {\n      const gridStart = startOfWeek(start, weekStartsOn);\n      return { start: gridStart, end: addDays(gridStart, 42) };\n    }\n    case CalendarView.Week:\n      return { start, end: addDays(start, 7) };\n    default:\n      return { start, end: addMonths(start, 1) };\n  }\n}\n\n/** `hiddenDays` as a lookup. A list that leaves no day of the week hides nothing. */\nexport function hiddenDaySet(hiddenDays: readonly number[] | undefined): ReadonlySet<number> {\n  const hidden = new Set(hiddenDays);\n  return hidden.size === 0 || [0, 1, 2, 3, 4, 5, 6].every((day) => hidden.has(day)) ? NO_DAYS : hidden;\n}\n\n/** `date`, or the first shown day from it on in `direction`. */\nexport function shownDay(date: Date, hidden: ReadonlySet<number>, direction: 1 | -1 = 1): Date {\n  let day = startOfDay(date);\n  while (hidden.has(day.getDay())) day = addDays(day, direction);\n  return day;\n}\n\n/** `steps` shown days on from the shown day `date`, or back for a negative count. */\nfunction stepShownDays(date: Date, steps: number, hidden: ReadonlySet<number>): Date {\n  const direction = steps < 0 ? -1 : 1;\n  let day = date;\n  for (let i = 0; i < Math.abs(steps); i++) day = shownDay(addDays(day, direction), hidden, direction);\n  return day;\n}\n\n/** `date` moved by `steps` periods, keeping its place in the period where it can. */\nexport function stepDate(\n  period: CalendarPeriod,\n  date: Date,\n  steps: number,\n  hidden: ReadonlySet<number> = NO_DAYS,\n): Date {\n  const rolling = rollingDays(period);\n  if (rolling > 0) return stepShownDays(date, steps * rolling, hidden);\n  return period === CalendarView.Week ? addDays(date, 7 * steps) : addMonths(date, steps);\n}\n\n/** The shown period plus one on either side: what the swipe pager can reveal. */\nexport function rangeWithNeighbours(\n  period: CalendarPeriod,\n  date: Date,\n  weekStartsOn: CalendarWeekStart,\n  hidden: ReadonlySet<number> = NO_DAYS,\n): CalendarRange {\n  return {\n    start: periodRange(period, stepDate(period, date, -1, hidden), weekStartsOn, hidden).start,\n    end: periodRange(period, stepDate(period, date, 1, hidden), weekStartsOn, hidden).end,\n  };\n}\n\n/** Every day from `first` to `last`, in either order: what creating whole days hands out. */\nexport function allDaySlot(first: Date, last: Date = first): CalendarSlot {\n  const [from, to] = last < first ? [last, first] : [first, last];\n  return { start: startOfDay(from), end: addDays(to, 1), allDay: true };\n}\n\n/**\n * What the header's New event button creates on `day`: in the week and day views the hour from\n * the next full hour of the clock, as calendar apps do, and elsewhere the whole day.\n */\nexport function newEventSlot(period: CalendarPeriod, day: Date, now: Date, slotMinutes: number): CalendarSlot {\n  if (period === CalendarView.Month || period === CalendarView.Agenda) return allDaySlot(day);\n  // 23:00 at the latest, even after 23:00 today: the slot stays on the selected day.\n  const start = atMinutes(day, Math.min(now.getHours() + 1, 23) * 60);\n  // Real time, as a click on empty time makes it: across a clock change an hour on the clock is none or two.\n  const end = new Date(start.getTime() + Math.max(60, slotMinutes) * 60_000);\n  const midnight = addDays(day, 1);\n  return { start, end: end < midnight ? end : midnight, allDay: false };\n}\n\n/** The ISO 8601 week `date` falls in: weeks run Monday to Sunday, and week 1 holds 4 January. */\nexport function weekNumber(date: Date): number {\n  const monday = startOfWeek(date, 1);\n  // A week across New Year belongs to the year of its Thursday.\n  const year = addDays(monday, 3).getFullYear();\n  return dayDiff(startOfWeek(new Date(year, 0, 4), 1), monday) / 7 + 1;\n}\n\n/** Midnight of each of `count` days from `start`. */\nexport function daysFrom(start: Date, count: number): Date[] {\n  return Array.from({ length: count }, (_, i) => addDays(start, i));\n}\n\n/** The days of `count` from `start` that are not hidden: the columns a row of them draws. */\nexport function shownDays(start: Date, count: number, hidden: ReadonlySet<number> = NO_DAYS): Date[] {\n  return daysFrom(start, count).filter((day) => !hidden.has(day.getDay()));\n}\n\nconst KEY_DAYS: Record<string, number> = { ArrowLeft: -1, ArrowRight: 1, ArrowUp: -7, ArrowDown: 7 };\n\nfunction keyTarget(key: string, day: Date, weekStartsOn: CalendarWeekStart): Date | null {\n  if (key in KEY_DAYS) return addDays(day, KEY_DAYS[key]);\n  if (key === 'Home') return startOfWeek(day, weekStartsOn);\n  if (key === 'End') return addDays(startOfWeek(day, weekStartsOn), 6);\n  if (key === 'PageUp') return addMonths(day, -1);\n  if (key === 'PageDown') return addMonths(day, 1);\n  return null;\n}\n\n/**\n * The day a month grid moves to for `key`, or `null` for a key it does not handle. A hidden day\n * gives way to the next shown one in the direction the key points, so the focus stays on a cell.\n */\nexport function gridKeyTarget(\n  key: string,\n  day: Date,\n  weekStartsOn: CalendarWeekStart,\n  hidden: ReadonlySet<number> = NO_DAYS,\n): Date | null {\n  const target = keyTarget(key, day, weekStartsOn);\n  return target && shownDay(target, hidden, key === 'ArrowLeft' || key === 'End' ? -1 : 1);\n}\n","import { CalendarView, type CalendarRange } from './calendar.types';\nimport { addDays, NARROW_WEEK, type CalendarPeriod } from './dates';\n\nconst formatters = new Map<string, Intl.DateTimeFormat>();\nlet zone: string | undefined;\n\n/**\n * Read once per task: reading the zone costs as much as building a formatter. Its name, not its\n * offsets, because Casablanca and Brazzaville share January's and July's but not March's.\n */\nfunction currentZone(): string {\n  if (zone === undefined) {\n    zone = new Intl.DateTimeFormat().resolvedOptions().timeZone;\n    queueMicrotask(() => {\n      zone = undefined;\n    });\n  }\n  return zone;\n}\n\n/**\n * Cached, since building one costs about fifty times more than using it. A formatter keeps the\n * time zone it was built in while `Date` follows the current one, so the key carries the zone: a\n * zone change (the device's, or `process.env.TZ` between tests) then builds a new formatter\n * instead of labelling days in the old zone.\n */\nexport function formatter(locale: string | undefined, options: Intl.DateTimeFormatOptions): Intl.DateTimeFormat {\n  const key = `${currentZone()}|${locale ?? ''}|${JSON.stringify(options)}`;\n  let cached = formatters.get(key);\n  if (!cached) {\n    cached = new Intl.DateTimeFormat(locale, options);\n    formatters.set(key, cached);\n  }\n  return cached;\n}\n\n// ES2020's lib predates `formatRange`, which every supported browser has had since 2021.\nfunction formatRange(format: Intl.DateTimeFormat, start: Date, end: Date): string {\n  return (format as Intl.DateTimeFormat & { formatRange(a: Date, b: Date): string }).formatRange(start, end);\n}\n\nfunction uses12Hour(locale: string | undefined): boolean {\n  // `hourCycle` is missing from ES2020's lib typings, not from any supported browser.\n  const options = formatter(locale, { hour: 'numeric' }).resolvedOptions() as { hourCycle?: string };\n  const cycle = options.hourCycle;\n  return cycle === 'h11' || cycle === 'h12';\n}\n\nexport function formatTime(date: Date, locale: string | undefined): string {\n  return formatter(locale, { hour: 'numeric', minute: '2-digit' }).format(date);\n}\n\n/** A time span, with the dates added by Intl where the two ends fall on different days. */\nexport function formatTimeRange(start: Date, end: Date, locale: string | undefined): string {\n  return formatRange(formatter(locale, { hour: 'numeric', minute: '2-digit' }), start, end);\n}\n\n/**\n * The clock times alone, `10:00 PM to 2:00 AM` as the locale writes it, where `formatTimeRange` would add both dates for\n * an event that crosses midnight. Both ends are moved onto one day for it. The first spec of\n * `formatRange` threw a RangeError for an end earlier than the start; an engine still doing that\n * gets the start time alone.\n */\nexport function formatClockRange(start: Date, end: Date, locale: string | undefined): string {\n  const from = new Date(2000, 0, 1, start.getHours(), start.getMinutes());\n  const to = new Date(2000, 0, 1, end.getHours(), end.getMinutes());\n  try {\n    return formatRange(formatter(locale, { hour: 'numeric', minute: '2-digit' }), from, to);\n  } catch {\n    return formatTime(start, locale);\n  }\n}\n\n/** `9 AM` where the locale counts to twelve, `09:00` where it counts to 24. */\nexport function formatHour(hour: number, locale: string | undefined): string {\n  const date = new Date(2000, 0, 1, hour);\n  return uses12Hour(locale)\n    ? formatter(locale, { hour: 'numeric' }).format(date)\n    : formatter(locale, { hour: '2-digit', minute: '2-digit' }).format(date);\n}\n\nexport function formatWeekday(date: Date, locale: string | undefined, width: 'long' | 'short' | 'narrow'): string {\n  return formatter(locale, { weekday: width }).format(date);\n}\n\n/** The day of the month in the locale's own digits. */\nexport function formatDayNumber(date: Date, locale: string | undefined): string {\n  return formatter(locale, { day: 'numeric' }).format(date);\n}\n\nexport function formatLongDate(date: Date, locale: string | undefined): string {\n  return formatter(locale, { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' }).format(date);\n}\n\nexport function formatShortDate(date: Date, locale: string | undefined): string {\n  return formatter(locale, { weekday: 'short', day: 'numeric', month: 'short' }).format(date);\n}\n\nexport function formatMonthTitle(date: Date, locale: string | undefined): string {\n  return formatter(locale, { month: 'long', year: 'numeric' }).format(date);\n}\n\nexport function formatPeriodTitle(\n  period: CalendarPeriod,\n  date: Date,\n  range: CalendarRange,\n  locale: string | undefined,\n): string {\n  switch (period) {\n    case CalendarView.Week:\n    case NARROW_WEEK:\n      return formatRange(\n        formatter(locale, { day: 'numeric', month: 'short', year: 'numeric' }),\n        range.start,\n        addDays(range.end, -1),\n      );\n    case CalendarView.Day:\n      return formatLongDate(date, locale);\n    default:\n      return formatMonthTitle(date, locale);\n  }\n}\n","/** A day of the week, as `Date#getDay()` counts: 0 is Sunday, 1 is Monday. */\nexport type Weekday = 0 | 1 | 2 | 3 | 4 | 5 | 6;\n\ntype WeekInfoLocale = Intl.Locale & {\n  getWeekInfo?: () => { firstDay: number };\n  /** The accessor form Chrome 99 to 129 and Safari 15.4 to 16 shipped before the method. */\n  weekInfo?: { firstDay: number };\n};\n\nconst weekStarts = new Map<string, Weekday>();\n\n/**\n * The locale's first day of the week, falling back to Monday where `getWeekInfo` is missing:\n * it is Baseline only since July 2026 (Chrome 130, Firefox 153, Safari 17).\n */\nexport function localeWeekStart(locale: string | undefined): Weekday {\n  const key = locale ?? '';\n  const cached = weekStarts.get(key);\n  if (cached !== undefined) return cached;\n\n  let result: Weekday = 1;\n  try {\n    const tag = locale ?? new Intl.DateTimeFormat().resolvedOptions().locale;\n    const info = new Intl.Locale(tag) as WeekInfoLocale;\n    const weekInfo = info.getWeekInfo?.() ?? info.weekInfo;\n    // `firstDay` counts 1 (Monday) to 7 (Sunday); `getDay()` counts 0 (Sunday) to 6.\n    if (weekInfo) result = (weekInfo.firstDay % 7) as Weekday;\n  } catch {\n    // An invalid tag: Intl.DateTimeFormat would have thrown for it too, so keep the fallback.\n  }\n  weekStarts.set(key, result);\n  return result;\n}\n","import { useEffect, useState } from 'react';\n\nimport { isSameDay, startOfDay } from './dates';\n\nfunction read(day: boolean): Date {\n  return day ? startOfDay(new Date()) : new Date();\n}\n\n/**\n * Checked on each minute boundary rather than once at midnight: a long timer is paused while the\n * device sleeps, and would mark the old day for hours after waking.\n */\nfunction useClock(day: boolean): Date {\n  const [now, setNow] = useState(() => read(day));\n  useEffect(() => {\n    let timer: ReturnType<typeof setTimeout>;\n    function schedule() {\n      timer = setTimeout(\n        () => {\n          const next = read(day);\n          // The same object while the day lasts, so nothing re-renders for it.\n          setNow((previous) => (day && isSameDay(previous, next) ? previous : next));\n          schedule();\n        },\n        60_000 - (Date.now() % 60_000),\n      );\n    }\n    schedule();\n    return () => clearTimeout(timer);\n  }, [day]);\n  return now;\n}\n\n/** The current time, updated on each minute boundary: it carries the now-line through the day. */\nexport function useNow(): Date {\n  return useClock(false);\n}\n\n/** Midnight of today, updated when the day changes: today's mark, for a calendar left open overnight. */\nexport function useToday(): Date {\n  return useClock(true);\n}\n",".emptyState {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  flex-flow: column;\n  gap: var(--spacing-m);\n}\n","import cx from 'classnames';\nimport type { ReactElement } from 'react';\n\nimport styles from './emptyState.module.pcss';\nimport { useLabels } from '../configProvider';\nimport { renderIcon } from '../utils/renderIcon';\nimport type { EmptyStateLabels, EmptyStateProps } from './emptyState.types';\n\nconst DEFAULT_LABELS: Required<EmptyStateLabels> = {\n  empty: 'No entries available',\n};\n\nexport default function EmptyState({\n  className,\n  icon,\n  children,\n  labels,\n  style,\n  classNames,\n  styles: slotStyles,\n  ...rest\n}: EmptyStateProps): ReactElement {\n  const text = useLabels('EmptyState', labels, DEFAULT_LABELS);\n  return (\n    <div\n      {...rest}\n      className={cx(styles.emptyState, className, classNames?.root)}\n      style={{ ...style, ...slotStyles?.root }}\n    >\n      {icon ? renderIcon(icon, { className: classNames?.icon, style: slotStyles?.icon }) : null}\n      <div className={classNames?.text} style={slotStyles?.text}>\n        {children ?? text.empty}\n      </div>\n    </div>\n  );\n}\n","import type { CalendarEvent } from './calendar.types';\nimport { addDays, dayDiff, endAfter, MINUTES_PER_DAY, minutesOfDay, shiftDays, startOfDay } from './dates';\n\nconst DAY_MS = 86_400_000;\n\n/**\n * Below this an event has no room for a line of text at the default hour height. It is drawn as a\n * bar of its real length with its label beneath, and packed with room for that label.\n */\nexport const BRIEF_EVENT_MINUTES = 15;\n/** The label's line (`--label` in timeGrid.module.pcss) at the default 48px hour: 15px. */\nconst BRIEF_LABEL_MINUTES = 20;\n\n/** `end`, clamped so an event that ends before it starts is treated as having no length. */\nexport function eventEnd(event: CalendarEvent): Date {\n  return event.end < event.start ? event.start : event.end;\n}\n\n/** `event` moved by `days` calendar days, keeping its clock times and its real length. */\nexport function moveByDays(event: CalendarEvent, days: number): { start: Date; end: Date } {\n  const start = shiftDays(event.start, days);\n  const length = (eventEnd(event).getTime() - event.start.getTime()) / 60_000;\n  return { start, end: endAfter(start, shiftDays(eventEnd(event), days), length) };\n}\n\n/** Drawn in the all-day row rather than the time grid. */\nexport function isAllDayLike(event: CalendarEvent): boolean {\n  return Boolean(event.allDay) || eventEnd(event).getTime() - event.start.getTime() >= DAY_MS;\n}\n\n/** First and last day (both midnight, both inclusive) the event appears on. */\nexport function eventDays(event: CalendarEvent): { first: Date; last: Date } {\n  const first = startOfDay(event.start);\n  const end = eventEnd(event);\n  if (event.allDay) {\n    // `end` is exclusive, but an all-day event ending on its own start day still takes that day.\n    const endDay = startOfDay(end);\n    return { first, last: dayDiff(first, endDay) >= 1 ? addDays(endDay, -1) : first };\n  }\n  // One millisecond back, so a timed event ending at midnight does not appear on the next day.\n  const last = end > event.start ? startOfDay(new Date(end.getTime() - 1)) : first;\n  return { first, last };\n}\n\n/** All-day events first, then by start, then the longer first, so a day reads the same everywhere. */\nexport function compareEvents(a: CalendarEvent, b: CalendarEvent): number {\n  const allDay = Number(isAllDayLike(b)) - Number(isAllDayLike(a));\n  if (allDay !== 0) return allDay;\n  const start = a.start.getTime() - b.start.getTime();\n  if (start !== 0) return start;\n  const length = eventEnd(b).getTime() - eventEnd(a).getTime();\n  if (length !== 0) return length;\n  return a.title.localeCompare(b.title);\n}\n\n/** The events on each of `dates`, sorted with `compareEvents`. */\nexport function eventsByDay<E extends CalendarEvent>(events: readonly E[], dates: readonly Date[]): E[][] {\n  const result: E[][] = dates.map(() => []);\n  for (const event of events) {\n    const { first, last } = eventDays(event);\n    dates.forEach((day, i) => {\n      if (dayDiff(first, day) >= 0 && dayDiff(day, last) >= 0) result[i].push(event);\n    });\n  }\n  for (const day of result) day.sort(compareEvents);\n  return result;\n}\n\nexport interface TimedBox<E extends CalendarEvent = CalendarEvent> {\n  event: E;\n  /** Wall-clock minutes from the column's midnight. */\n  top: number;\n  bottom: number;\n  /** Where the next event in its column may start: below the label of a brief event. */\n  reach: number;\n  /** Left edge, as a column of `columns`. */\n  column: number;\n  columns: number;\n  /** Columns it can widen into to its right without covering another event. */\n  span: number;\n  continuesBefore: boolean;\n  continuesAfter: boolean;\n}\n\n/**\n * Lays out one day column of the time grid: events that overlap share the width in columns, and\n * each one widens right into columns that are free for its whole height.\n *\n * Positions are wall-clock minutes, never milliseconds from midnight. Drawing and packing have\n * to agree on that: on a 25-hour day two events in the repeated hour occupy the same rows, and\n * packing them by real time would put them in one column, drawn over each other.\n */\nexport function packDay<E extends CalendarEvent>(events: readonly E[], day: Date): TimedBox<E>[] {\n  const dayStart = startOfDay(day);\n  const next = addDays(dayStart, 1);\n\n  const boxes: TimedBox<E>[] = [];\n  for (const event of events) {\n    if (isAllDayLike(event)) continue;\n    const { start } = event;\n    const end = eventEnd(event);\n    if (start >= next) continue;\n    // A zero-length event at midnight belongs to the day it starts on.\n    if (end <= dayStart && start < dayStart) continue;\n\n    const continuesBefore = start < dayStart;\n    const continuesAfter = end > next;\n    const top = continuesBefore ? 0 : minutesOfDay(start);\n    // Never upside down: on the day the clocks go back, an event can end on an earlier clock time.\n    const bottom = Math.max(end >= next ? MINUTES_PER_DAY : minutesOfDay(end), top);\n    const reach = bottom - top < BRIEF_EVENT_MINUTES ? Math.min(bottom + BRIEF_LABEL_MINUTES, MINUTES_PER_DAY) : bottom;\n    boxes.push({ event, top, bottom, reach, column: 0, columns: 1, span: 1, continuesBefore, continuesAfter });\n  }\n\n  boxes.sort((a, b) => a.top - b.top || b.reach - a.reach);\n\n  let cluster: TimedBox<E>[] = [];\n  let clusterEnd = -1;\n  for (const box of boxes) {\n    if (cluster.length > 0 && box.top >= clusterEnd) {\n      packCluster(cluster);\n      cluster = [];\n    }\n    cluster.push(box);\n    clusterEnd = Math.max(clusterEnd, box.reach);\n  }\n  if (cluster.length > 0) packCluster(cluster);\n\n  return boxes;\n}\n\nfunction packCluster(cluster: TimedBox[]): void {\n  const columnBottoms: number[] = [];\n  for (const box of cluster) {\n    let column = columnBottoms.findIndex((bottom) => bottom <= box.top);\n    if (column === -1) column = columnBottoms.push(0) - 1;\n    box.column = column;\n    columnBottoms[column] = box.reach;\n  }\n  const columns = columnBottoms.length;\n  for (const box of cluster) {\n    box.columns = columns;\n    let span = 1;\n    for (let column = box.column + 1; column < columns; column++) {\n      const blocked = cluster.some(\n        (other) => other.column === column && other.top < box.reach && other.reach > box.top,\n      );\n      if (blocked) break;\n      span++;\n    }\n    box.span = span;\n  }\n}\n\nexport interface LaneBar<E extends CalendarEvent = CalendarEvent> {\n  event: E;\n  /** First column of the row it covers, 0-based. */\n  start: number;\n  /** Columns it covers within the row. */\n  span: number;\n  lane: number;\n  /** It starts before its first column: in an earlier row, or on a hidden day. */\n  continuesBefore: boolean;\n  continuesAfter: boolean;\n}\n\n/**\n * Stacks the events of one row of days into lanes, as the month view and the all-day row draw\n * them: longer bars first, so a multi-day event keeps one lane across the week. `dates` are the\n * row's columns, so a hidden day between two of them is simply not drawn.\n */\nexport function packLanes<E extends CalendarEvent>(\n  events: readonly E[],\n  dates: readonly Date[],\n  include: (event: E) => boolean = () => true,\n): LaneBar<E>[] {\n  const bars: LaneBar<E>[] = [];\n  for (const event of events) {\n    if (!include(event)) continue;\n    const { first, last } = eventDays(event);\n    const start = dates.findIndex((day) => dayDiff(first, day) >= 0 && dayDiff(day, last) >= 0);\n    if (start === -1) continue;\n    let end = start;\n    while (end + 1 < dates.length && dayDiff(dates[end + 1], last) >= 0) end++;\n    bars.push({\n      event,\n      start,\n      span: end - start + 1,\n      lane: 0,\n      continuesBefore: dayDiff(first, dates[start]) > 0,\n      continuesAfter: dayDiff(dates[end], last) > 0,\n    });\n  }\n\n  bars.sort((a, b) => a.start - b.start || b.span - a.span || compareEvents(a.event, b.event));\n\n  const laneEnds: number[] = [];\n  for (const bar of bars) {\n    let lane = laneEnds.findIndex((end) => end <= bar.start);\n    if (lane === -1) lane = laneEnds.push(0) - 1;\n    bar.lane = lane;\n    laneEnds[lane] = bar.start + bar.span;\n  }\n  return bars;\n}\n\nexport interface LimitedLanes<E extends CalendarEvent = CalendarEvent> {\n  visible: LaneBar<E>[];\n  /** Lane the `+N` buttons go in, on the days that have one. */\n  moreLane: number;\n  /** Events hidden on each day of the row. */\n  hidden: number[];\n}\n\n/**\n * Fits the bars into `capacity` lanes. A day that needs more gives its last lane to a `+N`\n * button; a day that does not keeps every bar. Decided per day, so one crowded day does not\n * hide events on the quiet days next to it. A bar crossing into a crowded day's `+N` lane is\n * hidden along its whole length and counted on each day it covers.\n */\nexport function limitLanes<E extends CalendarEvent>(\n  bars: LaneBar<E>[],\n  days: number,\n  capacity: number,\n): LimitedLanes<E> {\n  const moreLane = Math.max(capacity - 1, 0);\n  const lanesPerDay = Array.from({ length: days }, () => 0);\n  for (const bar of bars) {\n    for (let day = bar.start; day < bar.start + bar.span; day++) {\n      lanesPerDay[day] = Math.max(lanesPerDay[day], bar.lane + 1);\n    }\n  }\n\n  const hidden = Array.from({ length: days }, () => 0);\n  const visible: LaneBar<E>[] = [];\n  for (const bar of bars) {\n    let fits = bar.lane < moreLane;\n    if (!fits && bar.lane === moreLane) {\n      fits = true;\n      for (let day = bar.start; day < bar.start + bar.span; day++) {\n        if (lanesPerDay[day] > capacity) fits = false;\n      }\n    }\n    if (fits) {\n      visible.push(bar);\n      continue;\n    }\n    for (let day = bar.start; day < bar.start + bar.span; day++) hidden[day]++;\n  }\n  return { visible, moreLane, hidden };\n}\n",".agendaList {\n  position: relative;\n  flex: 1;\n  min-height: 0;\n  overflow-y: auto;\n  overscroll-behavior: contain;\n\n  &-empty {\n    padding: 2em 1em;\n  }\n\n  &-day {\n    padding: 0.25em 0.5em;\n\n    & + & {\n      border-top: 1px solid var(--border-color);\n    }\n\n    &-withHeader {\n      display: grid;\n      grid-template-columns: 3.5em minmax(0, 1fr);\n      padding-left: 0;\n    }\n\n    &-header {\n      position: sticky;\n      top: 0;\n      align-self: start;\n      display: flex;\n      flex-direction: column;\n      align-items: center;\n      gap: 0.125em;\n      padding: 0.5em 0;\n    }\n\n    &-number {\n      display: grid;\n      place-items: center;\n      width: 1.875em;\n      height: 1.875em;\n      border-radius: 50%;\n      font-size: 1.125em;\n      font-weight: 600;\n      font-variant-numeric: tabular-nums;\n\n      &-today {\n        background: var(--custom-calendar-today-color);\n        color: var(--text-on-primary);\n      }\n    }\n\n    &-name {\n      font-size: 0.6875em;\n      font-weight: 600;\n      letter-spacing: 0.04em;\n      text-transform: uppercase;\n      color: var(--text-dark);\n    }\n  }\n\n  &-events {\n    display: flex;\n    flex-direction: column;\n    gap: 0.125em;\n    margin: 0;\n    padding: 0.375em 0;\n    list-style: none;\n  }\n\n  &-event {\n    display: grid;\n    grid-template-columns: auto 3px minmax(0, 1fr);\n    align-items: center;\n    gap: 0.75em;\n    width: 100%;\n    min-height: 2.75em;\n    padding: 0.375em 0.625em;\n    border: 0;\n    border-radius: var(--custom-calendar-event-radius);\n    background: transparent;\n    color: inherit;\n    font: inherit;\n    text-align: left;\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n    transition: background-color 0.15s ease;\n\n    &:hover {\n      background: color-mix(in oklab, var(--event-color, var(--primary-color)) 12%, transparent);\n    }\n\n    &:focus-visible {\n      outline: 2px solid var(--focus-border-color, var(--primary-color));\n      outline-offset: -2px;\n    }\n  }\n\n  &-time {\n    display: flex;\n    flex-direction: column;\n    min-width: 4.5em;\n    font-size: 0.8125em;\n    font-variant-numeric: tabular-nums;\n    line-height: 1.35;\n\n    &-end {\n      color: var(--text-light);\n    }\n  }\n\n  &-accent {\n    align-self: stretch;\n    width: 3px;\n    border-radius: 3px;\n    background: var(--event-color, var(--primary-color));\n  }\n\n  &-title {\n    overflow: hidden;\n    font-weight: 500;\n    white-space: nowrap;\n    text-overflow: ellipsis;\n  }\n}\n","import { useId, useLayoutEffect, useRef, type CSSProperties, type ReactElement } from 'react';\nimport cx from 'classnames';\n\nimport EmptyState from 'components/common/emptyState';\n\nimport type { CalendarEvent } from '../../calendar.types';\nimport { addDays, dateToDay, isSameDay, shownDays } from '../../dates';\nimport { formatDayNumber, formatLongDate, formatTime, formatTimeRange, formatWeekday } from '../../format';\nimport { eventEnd, eventsByDay, isAllDayLike } from '../../layout';\nimport type { AgendaListProps } from './agendaList.types';\n\nimport styles from './agendaList.module.pcss';\nimport { cssColor } from '../../../styles/tone';\n\nfunction AgendaList<E extends CalendarEvent>({\n  events,\n  start,\n  days,\n  hidden,\n  scrollTo,\n  showDayHeaders,\n  locale,\n  today,\n  text,\n  onEventClick,\n  renderEvent,\n  eventClassName,\n  className,\n  classNames,\n  slotStyles,\n}: AgendaListProps<E>): ReactElement {\n  const id = useId();\n  const listRef = useRef<HTMLDivElement>(null);\n  const dates = shownDays(start, days, hidden);\n  const perDay = eventsByDay(events, dates);\n  const scrollKey = scrollTo ? dateToDay(scrollTo) : null;\n\n  useLayoutEffect(() => {\n    const list = listRef.current;\n    if (!list || scrollKey === null) return;\n    const groups = list.querySelectorAll<HTMLElement>('[data-cal-day]');\n    const target = [...groups].find((group) => (group.dataset.calDay ?? '') >= scrollKey);\n    list.scrollTop = target ? target.offsetTop : 0;\n  }, [scrollKey]);\n\n  const listed = perDay.flatMap((dayEvents, index) =>\n    dayEvents.length > 0 || !showDayHeaders ? [{ day: dates[index], dayEvents }] : [],\n  );\n\n  return (\n    <div ref={listRef} className={cx(styles.agendaList, className)}>\n      {listed.every(({ dayEvents }) => dayEvents.length === 0) && (\n        <EmptyState className={styles.agendaListEmpty}>{text.noEvents}</EmptyState>\n      )}\n      {listed.map(({ day, dayEvents }) => {\n        if (dayEvents.length === 0) return null;\n        const headerId = `${id}-${dateToDay(day)}`;\n        const isToday = isSameDay(day, today);\n        const dayEnd = addDays(day, 1);\n        return (\n          <div\n            key={dateToDay(day)}\n            role='group'\n            aria-labelledby={showDayHeaders ? headerId : undefined}\n            className={cx(styles.agendaListDay, showDayHeaders && styles.agendaListDayWithHeader)}\n            data-cal-day={dateToDay(day)}\n          >\n            {showDayHeaders && (\n              <div\n                id={headerId}\n                className={cx(styles.agendaListDayHeader, classNames?.dayHeader)}\n                style={slotStyles?.dayHeader}\n                aria-label={formatLongDate(day, locale)}\n              >\n                <span\n                  className={cx(styles.agendaListDayNumber, isToday && styles.agendaListDayNumberToday)}\n                  aria-hidden\n                >\n                  {formatDayNumber(day, locale)}\n                </span>\n                <span className={styles.agendaListDayName} aria-hidden>\n                  {formatWeekday(day, locale, 'short')}\n                </span>\n              </div>\n            )}\n            <ul className={styles.agendaListEvents}>\n              {dayEvents.map((event) => {\n                const end = eventEnd(event);\n                const from = event.start < day ? day : event.start;\n                const to = end > dayEnd ? dayEnd : end;\n                const allDay = isAllDayLike(event) || (event.start <= day && end >= dayEnd);\n                const when = allDay ? text.allDay : formatTimeRange(event.start, end, locale);\n                return (\n                  <li key={event.id}>\n                    <button\n                      type='button'\n                      className={cx(styles.agendaListEvent, classNames?.event, eventClassName?.(event))}\n                      style={\n                        { '--event-color': event.color && cssColor(event.color), ...slotStyles?.event } as CSSProperties\n                      }\n                      aria-label={`${event.title}, ${when}`}\n                      data-cal-event={String(event.id)}\n                      onClick={(clickEvent) => onEventClick?.(event, clickEvent.currentTarget)}\n                    >\n                      <span className={styles.agendaListTime} aria-hidden>\n                        {allDay ? (\n                          text.allDay\n                        ) : (\n                          <>\n                            <span>{formatTime(from, locale)}</span>\n                            <span className={styles.agendaListTimeEnd}>{formatTime(to, locale)}</span>\n                          </>\n                        )}\n                      </span>\n                      <span className={styles.agendaListAccent} aria-hidden />\n                      <span className={styles.agendaListTitle} aria-hidden>\n                        {renderEvent ? renderEvent(event, { singleLine: false }) : event.title}\n                      </span>\n                    </button>\n                  </li>\n                );\n              })}\n            </ul>\n          </div>\n        );\n      })}\n    </div>\n  );\n}\n\nexport default AgendaList;\n",".optionPicker {\n  display: flex;\n  width: 100%;\n  border-radius: var(--default-border-radius);\n  border: 1px solid var(--border-color);\n  transition: border-color 0.15s;\n  justify-content: space-around;\n  position: relative;\n  overflow: hidden;\n\n  /* :has(:focus-visible) rather than :focus-within, so this only lights up on\n       keyboard focus — same as the per-option ring below, not on a mouse click. */\n  &:has(:focus-visible) {\n    border-color: var(--focus-border-color, var(--primary-color));\n  }\n\n  &-chip {\n    position: absolute;\n    height: 100%;\n    top: 0;\n    left: 0;\n    background: var(--primary-color);\n\n    /* Must follow the shorthand above, which resets background-image. */\n    background-image: var(--primary-gradient, none);\n    border-radius: var(--default-border-radius);\n    transition:\n      left 0.25s ease-in-out,\n      width 0.25s ease-in-out;\n  }\n\n  &-option {\n    position: relative;\n    width: 100%;\n    height: 100%;\n    display: block;\n    align-content: center;\n    text-align: center;\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n    padding: var(--option-picker-padding, var(--spacing-m));\n    overflow: hidden;\n    text-overflow: ellipsis;\n    white-space: nowrap;\n    border-radius: var(--default-border-radius);\n    transition: color 0.25s;\n    box-sizing: border-box;\n\n    &:focus-visible {\n      outline: var(--focus-border-color, var(--primary-color)) solid 2px;\n      outline-offset: -2px;\n    }\n\n    &-active {\n      color: var(--text-on-primary);\n\n      /* The active option sits on the primary-color chip, so the default\n               primary-color outline above is invisible against it — use the\n               on-primary color instead, same as the option's own text/icon color. */\n      &:focus-visible {\n        outline-color: var(--text-on-primary);\n      }\n    }\n  }\n}\n","import { useRef, type ReactElement } from 'react';\nimport { OptionPickerProps } from './optionPicker.types';\nimport cx from 'classnames';\n\nimport styles from './optionPicker.module.pcss';\nimport InteractableDiv from '../interactableDiv';\nimport { useComponentDefaults } from '../configProvider';\n\nfunction OptionPicker(props: OptionPickerProps): ReactElement {\n  const {\n    className = undefined,\n    style,\n    classNames,\n    styles: slotStyles,\n    options,\n    onChange,\n    value,\n    ...rest\n  } = useComponentDefaults('OptionPicker', props);\n\n  const optionPickerLeft = (options.findIndex((option) => option.key === value) * 100) / options.length;\n\n  const itemRefs = useRef<(HTMLDivElement | null)[]>([]);\n  const selectedIndex = options.findIndex((option) => option.key === value);\n  const rovingIndex = selectedIndex >= 0 ? selectedIndex : 0;\n\n  function moveTo(step: 1 | -1, fromIndex: number, event: React.KeyboardEvent<HTMLDivElement>) {\n    if (options.length === 0) return;\n    const next = (fromIndex + step + options.length) % options.length;\n    itemRefs.current[next]?.focus();\n    onChange(options[next].key, event as unknown as React.MouseEvent<HTMLDivElement, MouseEvent>);\n  }\n\n  function handleKeyDown(event: React.KeyboardEvent<HTMLDivElement>, index: number) {\n    switch (event.key) {\n      case 'ArrowRight':\n      case 'ArrowDown':\n        event.preventDefault();\n        moveTo(1, index, event);\n        break;\n      case 'ArrowLeft':\n      case 'ArrowUp':\n        event.preventDefault();\n        moveTo(-1, index, event);\n        break;\n      default:\n        break;\n    }\n  }\n\n  return (\n    <div\n      {...rest}\n      role='radiogroup'\n      className={cx(styles.optionPicker, className, classNames?.root)}\n      style={{ ...style, ...slotStyles?.root }}\n    >\n      <div\n        className={cx(styles.optionPickerChip, classNames?.chip)}\n        style={{ left: `${optionPickerLeft}%`, width: `${100 / options.length}%`, ...slotStyles?.chip }}\n      />\n      {options.map((option, index) => {\n        const checked = option.key === value;\n        return (\n          <InteractableDiv\n            ref={(el) => {\n              itemRefs.current[index] = el;\n            }}\n            onClick={(e) => onChange(option.key, e)}\n            onKeyDown={(e) => handleKeyDown(e, index)}\n            key={option.key}\n            role='radio'\n            aria-checked={checked}\n            className={cx(styles.optionPickerOption, checked && styles.optionPickerOptionActive, classNames?.option)}\n            style={slotStyles?.option}\n            tabIndex={index === rovingIndex ? 0 : -1}\n          >\n            {option.label}\n          </InteractableDiv>\n        );\n      })}\n    </div>\n  );\n}\n\nexport default OptionPicker;\n",".calendarHeader {\n  display: flex;\n  flex-wrap: wrap;\n  align-items: center;\n  gap: 0.5em 0.75em;\n  padding: 0.625em 0.75em;\n  border-bottom: 1px solid var(--border-color);\n\n  &-title {\n    flex: 1 1 6em;\n    min-width: 0;\n    overflow: hidden;\n    font-size: 1.125em;\n    font-weight: 600;\n    white-space: nowrap;\n\n    &-text {\n      display: block;\n      overflow: hidden;\n      text-overflow: ellipsis;\n      animation: calendarTitleIn 0.25s ease-out;\n    }\n  }\n\n  &-nav {\n    display: flex;\n    align-items: center;\n    gap: 0.25em;\n  }\n\n  &-today {\n    margin-right: 0.25em;\n  }\n}\n\n/* Two classes, to outrank OptionPicker's own `width: 100%` whichever stylesheet loads first.\n   OptionPicker's chip assumes equal options. As a flex row sized to its content they were equal\n   at the average label's width, and Month and Agenda were cut; `1fr` columns take the widest. */\n.calendarHeader .calendarHeader-views {\n  --option-picker-padding: 0.3125em 0.75em;\n\n  display: grid;\n  grid-auto-columns: 1fr;\n  grid-auto-flow: column;\n  flex: none;\n  width: auto;\n  font-size: 0.875em;\n}\n\n/* After the rule above, which it has to override on a narrow calendar: a row of its own, and\n   less padding, so four view names still fit at phone widths. The row leaves room for the New\n   event button after it: an ActionIcon under 2em wide and the 0.75em gap, both in the header's\n   font, not this 0.875em, and 0.5em to spare against rounding; growing takes the spare back. */\n.calendarHeader-compact .calendarHeader-views {\n  --option-picker-padding: 0.3125em 0.25em;\n\n  flex: 1 1 calc(100% - 3.25em / 0.875);\n}\n\n@keyframes calendarTitleIn {\n  from {\n    opacity: 0;\n    transform: translateX(calc(var(--title-from, 1) * 0.75em));\n  }\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .calendarHeader-title-text {\n    animation: none;\n  }\n}\n","import type { CSSProperties, ReactElement } from 'react';\nimport cx from 'classnames';\nimport { faChevronLeft, faChevronRight, faPlus } from '@fortawesome/free-solid-svg-icons';\n\nimport ActionIcon from 'components/common/actionIcon';\nimport Button, { ButtonSize } from 'components/common/button';\nimport OptionPicker from 'components/common/optionPicker';\nimport type { CalendarView } from '../../calendar.types';\nimport type { CalendarHeaderProps } from './calendarHeader.types';\n\nimport styles from './calendarHeader.module.pcss';\n\nfunction CalendarHeader({\n  title,\n  compact,\n  direction,\n  view,\n  views,\n  text,\n  onStep,\n  onToday,\n  onViewChange,\n  onCreate,\n  className,\n  style,\n}: CalendarHeaderProps): ReactElement {\n  return (\n    <div className={cx(styles.calendarHeader, compact && styles.calendarHeaderCompact, className)} style={style}>\n      {/* A live region, so paging is announced; its first content is not, as it was there on mount. */}\n      <div className={styles.calendarHeaderTitle} aria-live='polite'>\n        <span\n          key={title}\n          className={styles.calendarHeaderTitleText}\n          style={{ '--title-from': direction } as CSSProperties}\n        >\n          {title}\n        </span>\n      </div>\n      <div className={styles.calendarHeaderNav}>\n        <Button className={styles.calendarHeaderToday} size={ButtonSize.Small} onClick={onToday}>\n          {text.today}\n        </Button>\n        <ActionIcon icon={faChevronLeft} title={text.previous} aria-label={text.previous} onClick={() => onStep(-1)} />\n        <ActionIcon icon={faChevronRight} title={text.next} aria-label={text.next} onClick={() => onStep(1)} />\n      </div>\n      {views.length > 1 && (\n        <OptionPicker\n          className={styles.calendarHeaderViews}\n          aria-label={text.viewSwitcher}\n          value={view}\n          onChange={(key) => onViewChange(key as CalendarView)}\n          options={views.map((option) => ({ key: option, label: text[option] }))}\n        />\n      )}\n      {onCreate && (\n        <ActionIcon\n          icon={faPlus}\n          title={text.newEvent}\n          aria-label={text.newEvent}\n          onClick={(clickEvent) => onCreate(clickEvent.currentTarget)}\n        />\n      )}\n    </div>\n  );\n}\n\nexport default CalendarHeader;\n","// Thresholds the library already settled on elsewhere, so a swipe or a long press feels the same\n// here as in Carousel, Toast and VirtualList.\n\n/** Touch movement before a press is read as a swipe or a scroll (carousel.tsx, useSwipeDismiss.ts). */\nexport const LOCK_DISTANCE = 10;\n/** Mouse movement before a press becomes a drag. */\nexport const MOUSE_DRAG_DISTANCE = 4;\n/** Hold before a touch picks something up (VirtualList's `longPressDelay`). */\nexport const LONG_PRESS_MS = 400;\n/** A touch this soon after a scroll is stopping momentum, not picking something up. */\nexport const RECENT_SCROLL_MS = 150;\n/** Share of the width a swipe must travel to page (carousel.tsx). */\nexport const COMMIT_RATIO = 0.2;\n/** px/ms a shorter flick must reach to page anyway (useSwipeDismiss.ts). */\nexport const COMMIT_VELOCITY = 0.5;\n/** How far back the flick velocity is measured. */\nexport const VELOCITY_WINDOW_MS = 100;\n/** Distance from a scroller's edge where a drag starts scrolling it (useRowDrag.ts). */\nexport const EDGE_ZONE = 48;\n/** px per frame at the very edge. */\nexport const EDGE_SPEED = 18;\n/** How long after a gesture the click it ends in is swallowed (useRowDrag.ts). */\nexport const CLICK_SUPPRESS_MS = 350;\n\n/** `1` to page forwards, `-1` back, `0` to return. A swipe to the left reveals the next period. */\nexport function pageStep(dx: number, width: number, velocity: number): -1 | 0 | 1 {\n  if (dx === 0) return 0;\n  const far = Math.abs(dx) >= width * COMMIT_RATIO;\n  const flicked = Math.abs(velocity) >= COMMIT_VELOCITY && Math.sign(velocity) === Math.sign(dx);\n  if (!far && !flicked) return 0;\n  return dx < 0 ? 1 : -1;\n}\n\n/** Scroll speed for a pointer at `y` in a scroller spanning `top` to `bottom`: negative scrolls up. */\nexport function edgeSpeed(y: number, top: number, bottom: number): number {\n  if (y < top + EDGE_ZONE) return -EDGE_SPEED * Math.min(1, (top + EDGE_ZONE - y) / EDGE_ZONE);\n  if (y > bottom - EDGE_ZONE) return EDGE_SPEED * Math.min(1, (y - (bottom - EDGE_ZONE)) / EDGE_ZONE);\n  return 0;\n}\n\nexport function snap(minutes: number, step: number): number {\n  return Math.round(minutes / step) * step;\n}\n\nexport function clamp(value: number, min: number, max: number): number {\n  return Math.min(Math.max(value, min), max);\n}\n\nexport interface VelocitySample {\n  time: number;\n  x: number;\n}\n\n/** px/ms over the samples of the last `VELOCITY_WINDOW_MS`. */\nexport function velocityOf(samples: readonly VelocitySample[], now: number): number {\n  const recent = samples.filter((sample) => now - sample.time <= VELOCITY_WINDOW_MS);\n  if (recent.length < 2) return 0;\n  const first = recent[0];\n  const last = recent[recent.length - 1];\n  const elapsed = last.time - first.time;\n  return elapsed > 0 ? (last.x - first.x) / elapsed : 0;\n}\n","import { useEffect, useLayoutEffect, useRef, type RefObject } from 'react';\n\nimport {\n  CLICK_SUPPRESS_MS,\n  edgeSpeed,\n  LOCK_DISTANCE,\n  LONG_PRESS_MS,\n  MOUSE_DRAG_DISTANCE,\n  RECENT_SCROLL_MS,\n  velocityOf,\n  type VelocitySample,\n} from './gesture';\n\nexport interface GesturePoint {\n  x: number;\n  y: number;\n}\n\nexport interface GestureHandlers<D> {\n  /** Every press that starts a gesture, before anything else: where a running settle is finished. */\n  onPress?: () => void;\n  /** Whether a horizontal swipe may page right now. */\n  canSwipe?: () => boolean;\n  onSwipeMove?: (dx: number) => void;\n  /** Also called with `(0, 0)` when the browser takes the gesture over. */\n  onSwipeEnd?: (dx: number, velocity: number) => void;\n  /** What a press on `target` would pick up, or `null` where nothing can be dragged. */\n  dragFrom?: (target: Element, point: GesturePoint) => D | null;\n  onDragStart?: (drag: D, point: GesturePoint) => void;\n  onDragMove?: (point: GesturePoint) => void;\n  onDragEnd?: (point: GesturePoint) => void;\n  onDragCancel?: () => void;\n  onPinchStart?: (center: GesturePoint, distance: number) => void;\n  onPinchMove?: (center: GesturePoint, distance: number) => void;\n  onPinchEnd?: () => void;\n  /** The element a drag scrolls near its edges, and the band of the viewport that counts as its inside. */\n  autoScroll?: () => { element: HTMLElement; top: number; bottom: number } | null;\n}\n\ntype Phase = 'idle' | 'pressed' | 'swiping' | 'dragging' | 'pinching' | 'passive';\n\n/**\n * One owner for every pointer gesture inside `ref`, so a swipe, a long-press drag, a pinch and\n * the browser's own scrolling can never both act on the same touch.\n *\n * Mouse or touch rules are chosen per event from `pointerType`, not once from a media query,\n * which answers wrongly on a laptop with a touch screen.\n */\nexport function useGestures<D>(ref: RefObject<HTMLElement | null>, handlers: GestureHandlers<D>): void {\n  const handlersRef = useRef(handlers);\n  useLayoutEffect(() => {\n    handlersRef.current = handlers;\n  });\n\n  useEffect(() => {\n    const root = ref.current;\n    if (!root) return;\n    const h = () => handlersRef.current;\n\n    let phase: Phase = 'idle';\n    let primary = -1;\n    let pointerType = '';\n    let origin: GesturePoint = { x: 0, y: 0 };\n    let last: GesturePoint = { x: 0, y: 0 };\n    let samples: VelocitySample[] = [];\n    let drag: D | null = null;\n    let timer: ReturnType<typeof setTimeout> | undefined;\n    let frame = 0;\n    let lastScroll = -Infinity;\n    let suppressUntil = 0;\n    const pointers = new Map<number, GesturePoint>();\n\n    function capture(pointerId: number) {\n      // Only once the gesture is ours: a captured pointer sends its click to the capture target,\n      // so capturing on press would swallow every click meant for an event inside the grid.\n      try {\n        root!.setPointerCapture(pointerId);\n      } catch {\n        // The pointer is already gone.\n      }\n    }\n\n    function clearTimer() {\n      clearTimeout(timer);\n      timer = undefined;\n    }\n\n    function finishGesture() {\n      clearTimer();\n      cancelAnimationFrame(frame);\n      frame = 0;\n      drag = null;\n      phase = 'passive';\n      suppressUntil = performance.now() + CLICK_SUPPRESS_MS;\n    }\n\n    function pinchGeometry() {\n      const [a, b] = [...pointers.values()];\n      return {\n        center: { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 },\n        distance: Math.hypot(a.x - b.x, a.y - b.y),\n      };\n    }\n\n    function scrollStep() {\n      if (phase !== 'dragging') return;\n      const area = h().autoScroll?.();\n      if (area) {\n        const speed = edgeSpeed(last.y, area.top, area.bottom);\n        const before = area.element.scrollTop;\n        if (speed !== 0) area.element.scrollTop += speed;\n        // The content moved under a finger that did not, so the drag target did.\n        if (area.element.scrollTop !== before) h().onDragMove?.(last);\n      }\n      frame = requestAnimationFrame(scrollStep);\n    }\n\n    function startDrag() {\n      timer = undefined;\n      if (drag === null || phase !== 'pressed') return;\n      phase = 'dragging';\n      capture(primary);\n      if (pointerType !== 'mouse' && 'vibrate' in navigator) navigator.vibrate(10);\n      h().onDragStart?.(drag, last);\n      frame = requestAnimationFrame(scrollStep);\n    }\n\n    function onPointerDown(event: PointerEvent) {\n      // A primary pointer means every earlier one is up, even where its pointerup never reached\n      // us (a mouse released outside the window before the drag captured it).\n      if (event.isPrimary && phase !== 'idle') {\n        if (phase === 'dragging') h().onDragCancel?.();\n        else if (phase === 'swiping') h().onSwipeEnd?.(0, 0);\n        else if (phase === 'pinching') h().onPinchEnd?.();\n        clearTimer();\n        cancelAnimationFrame(frame);\n        drag = null;\n        phase = 'idle';\n      }\n      if (phase === 'idle') {\n        if (event.pointerType === 'mouse' && event.button !== 0) return;\n        h().onPress?.();\n        primary = event.pointerId;\n        pointerType = event.pointerType;\n        origin = last = { x: event.clientX, y: event.clientY };\n        pointers.clear();\n        pointers.set(event.pointerId, origin);\n        samples = [{ time: event.timeStamp, x: event.clientX }];\n        drag = event.target instanceof Element ? (h().dragFrom?.(event.target, origin) ?? null) : null;\n        phase = 'pressed';\n        const stoppingMomentum = performance.now() - lastScroll < RECENT_SCROLL_MS;\n        if (drag !== null && pointerType !== 'mouse' && !stoppingMomentum) {\n          timer = setTimeout(startDrag, LONG_PRESS_MS);\n        }\n        return;\n      }\n      if (event.pointerType === 'mouse') return;\n      pointers.set(event.pointerId, { x: event.clientX, y: event.clientY });\n      if (phase === 'pressed' && pointers.size === 2 && h().onPinchStart) {\n        clearTimer();\n        phase = 'pinching';\n        capture(primary);\n        capture(event.pointerId);\n        const { center, distance } = pinchGeometry();\n        h().onPinchStart?.(center, distance);\n      }\n    }\n\n    function onPointerMove(event: PointerEvent) {\n      if (!pointers.has(event.pointerId)) return;\n      const point = { x: event.clientX, y: event.clientY };\n      pointers.set(event.pointerId, point);\n\n      if (phase === 'pinching') {\n        if (pointers.size < 2) return;\n        const { center, distance } = pinchGeometry();\n        h().onPinchMove?.(center, distance);\n        return;\n      }\n      if (event.pointerId !== primary) return;\n      last = point;\n      const dx = point.x - origin.x;\n      const dy = point.y - origin.y;\n\n      if (phase === 'pressed') {\n        const distance = Math.hypot(dx, dy);\n        if (pointerType === 'mouse') {\n          if (distance <= MOUSE_DRAG_DISTANCE) return;\n          if (drag === null) {\n            phase = 'passive';\n            return;\n          }\n          startDrag();\n        } else {\n          if (distance <= LOCK_DISTANCE) return;\n          clearTimer();\n          if (Math.abs(dx) <= Math.abs(dy) || !h().canSwipe?.()) {\n            phase = 'passive';\n            return;\n          }\n          phase = 'swiping';\n          capture(primary);\n        }\n      }\n\n      if (phase === 'swiping') {\n        samples.push({ time: event.timeStamp, x: point.x });\n        if (samples.length > 20) samples.shift();\n        h().onSwipeMove?.(dx);\n      } else if (phase === 'dragging') {\n        h().onDragMove?.(point);\n      }\n    }\n\n    function release(pointerId: number) {\n      pointers.delete(pointerId);\n      if (pointers.size === 0) {\n        clearTimer();\n        drag = null;\n        phase = 'idle';\n      }\n    }\n\n    function onPointerUp(event: PointerEvent) {\n      if (!pointers.has(event.pointerId)) return;\n      if (phase === 'pinching') {\n        h().onPinchEnd?.();\n        finishGesture();\n      } else if (event.pointerId === primary) {\n        if (phase === 'swiping') {\n          samples.push({ time: event.timeStamp, x: event.clientX });\n          h().onSwipeEnd?.(event.clientX - origin.x, velocityOf(samples, event.timeStamp));\n          finishGesture();\n        } else if (phase === 'dragging') {\n          last = { x: event.clientX, y: event.clientY };\n          h().onDragEnd?.(last);\n          finishGesture();\n        } else if (phase === 'pressed') {\n          // Left to the click that follows, so a tap, a mouse click and Enter share one path.\n          clearTimer();\n          phase = 'passive';\n        }\n      }\n      release(event.pointerId);\n    }\n\n    // The browser took the touch over, usually to scroll: whatever was in progress is undone.\n    function onPointerCancel(event: PointerEvent) {\n      if (!pointers.has(event.pointerId)) return;\n      if (phase === 'swiping') {\n        h().onSwipeEnd?.(0, 0);\n        finishGesture();\n      } else if (phase === 'dragging') {\n        h().onDragCancel?.();\n        finishGesture();\n      } else if (phase === 'pinching') {\n        h().onPinchEnd?.();\n        finishGesture();\n      } else {\n        clearTimer();\n        phase = 'passive';\n      }\n      release(event.pointerId);\n    }\n\n    // The only way to keep iOS Safari from scrolling under a drag that has already begun:\n    // `touch-action` is read once, when the touch starts, so changing it mid-gesture does\n    // nothing. Registered here at mount rather than on touchstart, so the area is already known\n    // to block scrolling before the first touch arrives.\n    function onTouchMove(event: TouchEvent) {\n      if ((phase === 'swiping' || phase === 'dragging' || phase === 'pinching') && event.cancelable) {\n        event.preventDefault();\n      }\n    }\n\n    // Android opens the context menu on a long press, iOS the callout, both around the time a\n    // drag arms.\n    function onContextMenu(event: Event) {\n      if (phase === 'dragging' || (phase === 'pressed' && pointerType !== 'mouse' && drag !== null)) {\n        event.preventDefault();\n      }\n    }\n\n    function onDragStartNative(event: Event) {\n      event.preventDefault();\n    }\n\n    function onClickCapture(event: MouseEvent) {\n      if (performance.now() < suppressUntil) {\n        event.stopPropagation();\n        event.preventDefault();\n        suppressUntil = 0;\n      }\n    }\n\n    function onScroll() {\n      lastScroll = performance.now();\n    }\n\n    function onKeyDown(event: KeyboardEvent) {\n      if (event.key !== 'Escape' || phase !== 'dragging') return;\n      event.preventDefault();\n      h().onDragCancel?.();\n      finishGesture();\n    }\n\n    root.addEventListener('pointerdown', onPointerDown);\n    root.addEventListener('pointermove', onPointerMove);\n    root.addEventListener('pointerup', onPointerUp);\n    root.addEventListener('pointercancel', onPointerCancel);\n    root.addEventListener('touchmove', onTouchMove, { passive: false });\n    root.addEventListener('contextmenu', onContextMenu);\n    root.addEventListener('dragstart', onDragStartNative);\n    root.addEventListener('click', onClickCapture, true);\n    // `scroll` does not bubble, but it does pass through capturing listeners on the way down.\n    root.addEventListener('scroll', onScroll, { capture: true, passive: true });\n    window.addEventListener('scroll', onScroll, { passive: true });\n    window.addEventListener('keydown', onKeyDown);\n    return () => {\n      clearTimer();\n      cancelAnimationFrame(frame);\n      root.removeEventListener('pointerdown', onPointerDown);\n      root.removeEventListener('pointermove', onPointerMove);\n      root.removeEventListener('pointerup', onPointerUp);\n      root.removeEventListener('pointercancel', onPointerCancel);\n      root.removeEventListener('touchmove', onTouchMove);\n      root.removeEventListener('contextmenu', onContextMenu);\n      root.removeEventListener('dragstart', onDragStartNative);\n      root.removeEventListener('click', onClickCapture, true);\n      root.removeEventListener('scroll', onScroll, { capture: true });\n      window.removeEventListener('scroll', onScroll);\n      window.removeEventListener('keydown', onKeyDown);\n    };\n  }, [ref]);\n}\n","import { useCallback, useEffect, useLayoutEffect, useRef } from 'react';\nimport { flushSync } from 'react-dom';\n\nimport { useMediaQuery } from '../hooks/useMediaQuery';\n\nconst SETTLE_MS = 240;\n\nexport interface Pager {\n  /** Ref for each element that slides. The time grid has two, its head and its body. */\n  trackRef: (element: HTMLElement | null) => void;\n  /** Width of one pane. */\n  width: () => number;\n  /** Offset every track by `dx` px, following the finger. */\n  move: (dx: number) => void;\n  /** Animate to the next (`1`) or previous (`-1`) pane and page, or back (`0`). */\n  settle: (step: -1 | 0 | 1) => void;\n  /** Jump to the end of a running settle: a new press arrived before it finished. */\n  finish: () => void;\n}\n\n/**\n * Drives the three panes a pageable view renders: previous, current and next, keyed by period, so\n * after paging the pane that slid in is the same DOM node as the one now current. The track is\n * reset in the same commit that swaps them, which is why nothing flashes.\n *\n * `focusFallback` receives focus when it would otherwise be left inside a pane that just went inert.\n */\nexport function usePager(\n  periodKey: string,\n  onPage: (step: 1 | -1) => void,\n  focusFallback: () => HTMLElement | null,\n): Pager {\n  const tracks = useRef(new Set<HTMLElement>());\n  const offset = useRef(0);\n  const animations = useRef<Animation[]>([]);\n  const pendingStep = useRef<-1 | 0 | 1>(0);\n  const keyRef = useRef(periodKey);\n  const reducedMotion = useMediaQuery('(prefers-reduced-motion: reduce)');\n\n  const latest = useRef({ onPage, focusFallback, reducedMotion });\n  useLayoutEffect(() => {\n    latest.current = { onPage, focusFallback, reducedMotion };\n  });\n\n  const write = useCallback((dx: number) => {\n    offset.current = dx;\n    for (const track of tracks.current) track.style.transform = dx === 0 ? '' : `translateX(${dx}px)`;\n  }, []);\n\n  useLayoutEffect(() => {\n    keyRef.current = periodKey;\n    write(0);\n    const active = document.activeElement;\n    if (active instanceof HTMLElement && active.closest('[inert]')) {\n      const inPager = [...tracks.current].some((track) => track.contains(active));\n      if (inPager) latest.current.focusFallback()?.focus();\n    }\n  }, [periodKey, write]);\n\n  const trackRef = useCallback((element: HTMLElement | null) => {\n    if (!element) return;\n    tracks.current.add(element);\n    return () => {\n      tracks.current.delete(element);\n    };\n  }, []);\n\n  const width = useCallback(() => {\n    const [first] = tracks.current;\n    return first?.parentElement?.clientWidth ?? 0;\n  }, []);\n\n  const complete = useCallback(\n    (step: -1 | 0 | 1) => {\n      animations.current = [];\n      if (step === 0) {\n        write(0);\n        return;\n      }\n      const before = keyRef.current;\n      flushSync(() => latest.current.onPage(step));\n      // A controlled parent that did not take the new date leaves the old panes in place.\n      if (keyRef.current === before) write(0);\n    },\n    [write],\n  );\n\n  const settle = useCallback(\n    (step: -1 | 0 | 1) => {\n      const paneWidth = width();\n      const from = offset.current;\n      const to = step === 0 ? 0 : -step * paneWidth;\n      const elements = [...tracks.current];\n      write(to);\n      const canAnimate = typeof elements[0]?.animate === 'function';\n      if (latest.current.reducedMotion || from === to || !canAnimate) {\n        complete(step);\n        return;\n      }\n      // Shorter the less of the way is left, so a flick that has almost arrived does not crawl.\n      const duration = Math.max(120, SETTLE_MS * Math.min(1, Math.abs(to - from) / Math.max(paneWidth, 1)));\n      const keyframes = [{ transform: `translateX(${from}px)` }, { transform: `translateX(${to}px)` }];\n      pendingStep.current = step;\n      animations.current = elements.map((element) =>\n        element.animate(keyframes, { duration, easing: 'cubic-bezier(0.25, 1, 0.5, 1)' }),\n      );\n      animations.current[0].onfinish = () => complete(step);\n    },\n    [complete, width, write],\n  );\n\n  const finish = useCallback(() => {\n    if (animations.current.length === 0) return;\n    // Not `Animation#finish()`: it queues the finish event for a later frame, and the new press\n    // has to start from the paged state now.\n    for (const animation of animations.current) {\n      animation.onfinish = null;\n      animation.cancel();\n    }\n    complete(pendingStep.current);\n  }, [complete]);\n\n  useEffect(() => {\n    const running = animations;\n    return () => {\n      for (const animation of running.current) {\n        animation.onfinish = null;\n        animation.cancel();\n      }\n    };\n  }, []);\n\n  const move = useCallback((dx: number) => write(dx), [write]);\n\n  return { trackRef, width, move, settle, finish };\n}\n","/* The three panes of a pageable view share one grid cell, so the track is as tall as its\n   tallest pane and does not change height mid-swipe. `clip` rather than `hidden` on the viewport:\n   `hidden` makes it a scroll container, which the neighbours would then extend sideways.\n   The neighbours' transforms are positions, not motion: the slide itself runs in usePager,\n   which skips it under reduced motion. */\n.pager {\n  &-viewport {\n    position: relative;\n    min-width: 0;\n    overflow: clip;\n  }\n\n  &-track {\n    display: grid;\n    height: 100%;\n  }\n\n  &-pane {\n    grid-area: 1 / 1;\n    min-width: 0;\n    min-height: 0;\n\n    &-prev {\n      transform: translateX(-100%);\n    }\n\n    &-next {\n      transform: translateX(100%);\n    }\n  }\n}\n","/* `--collapse` runs from 0 (the whole month) to 1 (only the selected week), written by the\n   component from the scroll position. Five rows fold away over five rows of scrolling, so the\n   clipped edge of the month and the top of the list below it stay level the whole way. */\n.compactMonth {\n  --row: 2.75em;\n  --weekdays: 1.75em;\n\n  position: relative;\n  flex: 1;\n  min-height: 0;\n  overflow-y: auto;\n  overscroll-behavior-y: contain;\n\n  &-sticky {\n    position: sticky;\n    top: 0;\n    z-index: 1;\n\n    /* The folded rows are clipped, not removed, so this box still spans all six: it must not\n       catch the taps meant for the list showing through below the fold. */\n    pointer-events: none;\n    user-select: none;\n    -webkit-touch-callout: none;\n  }\n\n  &-weekdays {\n    display: grid;\n    grid-template-columns: repeat(var(--days), minmax(0, 1fr));\n    align-items: center;\n    height: var(--weekdays);\n    background: var(--background);\n    pointer-events: auto;\n  }\n\n  &-weekday {\n    font-size: 0.6875em;\n    font-weight: 600;\n    text-align: center;\n    color: var(--text-dark);\n  }\n\n  &-viewport {\n    height: calc(6 * var(--row));\n    clip-path: inset(0 0 calc(5 * var(--row) * var(--collapse, 0)) 0);\n    pointer-events: auto;\n\n    /* Horizontal swipes page the month or week; vertical ones fold it. */\n    touch-action: pan-y;\n  }\n\n  &-pane {\n    background: var(--background);\n  }\n\n  /* Keeps the selected week in view while the rows around it fold away above and below it.\n     Reduced motion is handled where `--collapse` is written, which is then only 0 or 1: freezing\n     this transform instead would clip the selected week out of view mid-fold. */\n  &-rows {\n    transform: translateY(calc(var(--selected-row, 0) * var(--row) * var(--collapse, 0) * -1));\n  }\n\n  &-week {\n    display: grid;\n    grid-template-columns: repeat(var(--days), minmax(0, 1fr));\n    height: var(--row);\n\n    &-folding {\n      opacity: calc(1 - var(--collapse, 0));\n    }\n  }\n\n  &-cell {\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    justify-content: center;\n    gap: 0.1875em;\n    min-width: 0;\n    outline: none;\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n\n    &-outside {\n      color: var(--text-light);\n    }\n\n    &:focus-visible .compactMonth-number {\n      box-shadow: 0 0 0 2px var(--focus-border-color, var(--primary-color));\n    }\n  }\n\n  &-number {\n    display: grid;\n    place-items: center;\n    width: 1.875em;\n    height: 1.875em;\n    border-radius: 50%;\n    font-size: 0.9375em;\n    font-variant-numeric: tabular-nums;\n    transition:\n      background-color 0.15s ease,\n      color 0.15s ease;\n\n    &-today {\n      color: var(--custom-calendar-today-color);\n      font-weight: 700;\n    }\n\n    &-selected {\n      background: var(--text-color);\n      color: var(--background);\n      font-weight: 600;\n    }\n  }\n\n  &-dots {\n    display: flex;\n    gap: 2px;\n    height: 4px;\n  }\n\n  &-dot {\n    width: 4px;\n    height: 4px;\n    border-radius: 50%;\n    background: var(--event-color, var(--primary-color));\n  }\n\n  /* Tall enough that the month can always fold all the way, however few events the day has. */\n  &-list {\n    min-height: calc(100% - var(--weekdays) - var(--row));\n    border-top: 1px solid var(--border-color);\n\n    &-title {\n      padding: 0.9em 1.25em 0.15em;\n      font-size: 0.8125em;\n      font-weight: 600;\n      color: var(--text-dark);\n    }\n  }\n}\n\n/* Today stays marked when it is also the selected day. */\n.compactMonth-number-today.compactMonth-number-selected {\n  background: var(--custom-calendar-today-color);\n  color: var(--text-on-primary);\n}\n\n/* Scrolls with the page it sits in, rather than on its own. Two classes, to outrank AgendaList's\n   own rule whichever stylesheet loads first. */\n.compactMonth .compactMonth-agenda {\n  flex: none;\n  overflow: visible;\n}\n","import { useLayoutEffect, useRef, useState, type CSSProperties, type KeyboardEvent, type ReactElement } from 'react';\nimport cx from 'classnames';\n\nimport { useMediaQuery } from 'components/common/hooks/useMediaQuery';\nimport { CalendarView, type CalendarEvent } from '../../calendar.types';\nimport {\n  addDays,\n  addMonths,\n  dayDiff,\n  dateToDay,\n  gridKeyTarget,\n  isSameDay,\n  periodRange,\n  shownDays,\n  startOfMonth,\n  startOfWeek,\n} from '../../dates';\nimport { formatDayNumber, formatLongDate, formatMonthTitle, formatWeekday } from '../../format';\nimport { pageStep } from '../../gesture';\nimport { eventsByDay } from '../../layout';\nimport { useGestures } from '../../useGestures';\nimport { usePager } from '../../usePager';\nimport AgendaList from '../agendaList';\nimport type { CompactMonthProps } from './compactMonth.types';\n\nimport pagerStyles from '../../pager.module.pcss';\nimport styles from './compactMonth.module.pcss';\nimport { cssColor } from '../../../styles/tone';\n\nconst STEPS = [-1, 0, 1] as const;\nconst MAX_DOTS = 3;\n/** Quiet time after the last scroll before a half-collapsed month settles open or shut. */\nconst SETTLE_DELAY_MS = 150;\n\n/**\n * The month view of a narrow calendar: a grid of days with event dots over the selected day's\n * events. Scrolling that list folds the month up into the selected week, iOS-style.\n */\nfunction CompactMonth<E extends CalendarEvent>({\n  events,\n  date,\n  today,\n  locale,\n  weekStartsOn,\n  hidden,\n  text,\n  onDateChange,\n  onEventClick,\n  renderEvent,\n  eventClassName,\n  classNames,\n  slotStyles,\n}: CompactMonthProps<E>): ReactElement {\n  const scrollerRef = useRef<HTMLDivElement>(null);\n  const monthRef = useRef<HTMLDivElement>(null);\n  const viewportRef = useRef<HTMLDivElement>(null);\n  const currentRef = useRef<HTMLDivElement>(null);\n  const pendingFocus = useRef<string | null>(null);\n  const progress = useRef(0);\n  const [collapsed, setCollapsed] = useState(false);\n  const reducedMotion = useMediaQuery('(prefers-reduced-motion: reduce)');\n\n  const month = startOfMonth(date);\n  const week = startOfWeek(date, weekStartsOn);\n  const weekdays = shownDays(startOfWeek(today, weekStartsOn), 7, hidden);\n\n  const pager = usePager(\n    dateToDay(collapsed ? week : month),\n    (step) => onDateChange(collapsed ? addDays(date, 7 * step) : addMonths(date, step)),\n    () => currentRef.current?.querySelector<HTMLElement>('[tabindex=\"0\"]') ?? null,\n  );\n\n  // The fold follows the scroll position rather than animating, so it is written straight to a\n  // custom property on every scroll event and never re-renders. Only reaching or leaving fully\n  // folded re-renders, to swap the month panes for week strips that look identical at that point.\n  useLayoutEffect(() => {\n    const scroller = scrollerRef.current;\n    if (!scroller) return;\n    let settleTimer: ReturnType<typeof setTimeout> | undefined;\n    let touching = false;\n\n    function foldDistance() {\n      // Five of the six rows fold away.\n      return ((viewportRef.current?.clientHeight ?? 0) * 5) / 6;\n    }\n\n    function update() {\n      const distance = foldDistance();\n      let next = distance > 0 ? Math.min(scroller!.scrollTop / distance, 1) : 0;\n      // Following the scroll, the rows move at a rate of their own, as parallax does. Under\n      // reduced motion the month stays whole until it is scrolled all the way, then folds at once.\n      if (reducedMotion) next = next > 0.99 ? 1 : 0;\n      progress.current = next;\n      scroller!.style.setProperty('--collapse', String(next));\n      setCollapsed(next >= 1);\n    }\n\n    function settle() {\n      if (touching) return;\n      const distance = foldDistance();\n      const top = scroller!.scrollTop;\n      if (top <= 0 || top >= distance) return;\n      scroller!.scrollTo({ top: top < distance / 2 ? 0 : distance, behavior: reducedMotion ? 'auto' : 'smooth' });\n    }\n\n    function scheduleSettle() {\n      clearTimeout(settleTimer);\n      settleTimer = setTimeout(settle, SETTLE_DELAY_MS);\n    }\n\n    function onScroll() {\n      update();\n      scheduleSettle();\n    }\n    function onTouchStart() {\n      touching = true;\n    }\n    function onTouchEnd() {\n      touching = false;\n      scheduleSettle();\n    }\n\n    update();\n    scroller.addEventListener('scroll', onScroll, { passive: true });\n    scroller.addEventListener('touchstart', onTouchStart, { passive: true });\n    scroller.addEventListener('touchend', onTouchEnd, { passive: true });\n    scroller.addEventListener('touchcancel', onTouchEnd, { passive: true });\n    return () => {\n      clearTimeout(settleTimer);\n      scroller.removeEventListener('scroll', onScroll);\n      scroller.removeEventListener('touchstart', onTouchStart);\n      scroller.removeEventListener('touchend', onTouchEnd);\n      scroller.removeEventListener('touchcancel', onTouchEnd);\n    };\n  }, [reducedMotion]);\n\n  useLayoutEffect(() => {\n    const key = pendingFocus.current;\n    if (key === null) return;\n    pendingFocus.current = null;\n    currentRef.current?.querySelector<HTMLElement>(`[data-cal-day=\"${key}\"]`)?.focus();\n  });\n\n  useGestures(monthRef, {\n    onPress: pager.finish,\n    // Half folded, the neighbours would not line up with the current pane.\n    canSwipe: () => progress.current < 0.01 || progress.current > 0.99,\n    onSwipeMove: pager.move,\n    onSwipeEnd: (dx, velocity) => pager.settle(pageStep(dx, pager.width(), velocity)),\n  });\n\n  function handleKeyDown(keyEvent: KeyboardEvent<HTMLDivElement>, day: Date) {\n    const next = gridKeyTarget(keyEvent.key, day, weekStartsOn, hidden);\n    if (!next) return;\n    keyEvent.preventDefault();\n    pendingFocus.current = dateToDay(next);\n    onDateChange(next);\n  }\n\n  function renderDay(day: Date, outside: boolean, isCurrent: boolean, dayEvents: readonly E[]) {\n    const selected = isSameDay(day, date);\n    const isToday = isSameDay(day, today);\n    const name = formatLongDate(day, locale);\n    return (\n      <div\n        key={dateToDay(day)}\n        role='gridcell'\n        data-cal-day={dateToDay(day)}\n        tabIndex={isCurrent && selected ? 0 : -1}\n        aria-selected={selected}\n        aria-current={isToday ? 'date' : undefined}\n        aria-label={dayEvents.length > 0 ? `${name}, ${text.eventCount(dayEvents.length)}` : name}\n        className={cx(styles.compactMonthCell, outside && styles.compactMonthCellOutside, classNames?.cell)}\n        style={slotStyles?.cell}\n        onClick={() => onDateChange(day)}\n        onKeyDown={(keyEvent) => handleKeyDown(keyEvent, day)}\n      >\n        <span\n          aria-hidden\n          className={cx(\n            styles.compactMonthNumber,\n            isToday && styles.compactMonthNumberToday,\n            selected && styles.compactMonthNumberSelected,\n          )}\n        >\n          {formatDayNumber(day, locale)}\n        </span>\n        <span aria-hidden className={styles.compactMonthDots}>\n          {dayEvents.slice(0, MAX_DOTS).map((event) => (\n            <span\n              key={event.id}\n              className={styles.compactMonthDot}\n              style={{ '--event-color': event.color && cssColor(event.color) } as CSSProperties}\n            />\n          ))}\n        </span>\n      </div>\n    );\n  }\n\n  function renderPane(step: (typeof STEPS)[number]) {\n    const isCurrent = step === 0;\n    const className = cx(\n      pagerStyles.pagerPane,\n      step === -1 && pagerStyles.pagerPanePrev,\n      step === 1 && pagerStyles.pagerPaneNext,\n      styles.compactMonthPane,\n    );\n\n    if (collapsed) {\n      const paneWeek = addDays(week, 7 * step);\n      const dates = shownDays(paneWeek, 7, hidden);\n      const perDay = eventsByDay(events, dates);\n      return (\n        <div\n          key={dateToDay(paneWeek)}\n          ref={isCurrent ? currentRef : undefined}\n          className={className}\n          inert={!isCurrent}\n          aria-hidden={!isCurrent || undefined}\n        >\n          <div role='row' className={styles.compactMonthWeek}>\n            {dates.map((day, index) => renderDay(day, false, isCurrent, perDay[index]))}\n          </div>\n        </div>\n      );\n    }\n\n    const paneMonth = addMonths(month, step);\n    const gridStart = periodRange(CalendarView.Month, paneMonth, weekStartsOn).start;\n    const selectedRow = isCurrent ? Math.floor(dayDiff(gridStart, date) / 7) : 0;\n    return (\n      <div\n        key={dateToDay(paneMonth)}\n        ref={isCurrent ? currentRef : undefined}\n        className={className}\n        inert={!isCurrent}\n        aria-hidden={!isCurrent || undefined}\n        style={{ '--selected-row': selectedRow } as CSSProperties}\n      >\n        <div className={styles.compactMonthRows}>\n          {Array.from({ length: 6 }, (_, row) => {\n            const weekStart = addDays(gridStart, row * 7);\n            const dates = shownDays(weekStart, 7, hidden);\n            const perDay = eventsByDay(events, dates);\n            return (\n              <div\n                key={dateToDay(weekStart)}\n                role='row'\n                className={cx(styles.compactMonthWeek, row !== selectedRow && styles.compactMonthWeekFolding)}\n              >\n                {dates.map((day, column) =>\n                  renderDay(day, day.getMonth() !== paneMonth.getMonth(), isCurrent, perDay[column]),\n                )}\n              </div>\n            );\n          })}\n        </div>\n      </div>\n    );\n  }\n\n  return (\n    <div ref={scrollerRef} className={styles.compactMonth} style={{ '--days': weekdays.length } as CSSProperties}>\n      <div ref={monthRef} className={styles.compactMonthSticky}>\n        <div role='grid' aria-label={formatMonthTitle(date, locale)}>\n          <div role='row' className={styles.compactMonthWeekdays}>\n            {weekdays.map((day) => (\n              <div\n                key={day.getDay()}\n                role='columnheader'\n                aria-label={formatWeekday(day, locale, 'long')}\n                className={cx(styles.compactMonthWeekday, classNames?.dayHeader)}\n                style={slotStyles?.dayHeader}\n              >\n                {formatWeekday(day, locale, 'narrow')}\n              </div>\n            ))}\n          </div>\n          <div ref={viewportRef} className={cx(pagerStyles.pagerViewport, styles.compactMonthViewport)}>\n            <div ref={pager.trackRef} className={pagerStyles.pagerTrack}>\n              {STEPS.map(renderPane)}\n            </div>\n          </div>\n        </div>\n      </div>\n      <div className={styles.compactMonthList}>\n        <div className={styles.compactMonthListTitle}>{formatLongDate(date, locale)}</div>\n        <AgendaList\n          className={styles.compactMonthAgenda}\n          events={events}\n          start={date}\n          days={1}\n          showDayHeaders={false}\n          locale={locale}\n          today={today}\n          text={text}\n          onEventClick={onEventClick}\n          renderEvent={renderEvent}\n          eventClassName={eventClassName}\n          classNames={classNames}\n          slotStyles={slotStyles}\n        />\n      </div>\n    </div>\n  );\n}\n\nexport default CompactMonth;\n",".monthView {\n  display: flex;\n  flex: 1;\n  flex-direction: column;\n  min-height: 0;\n  user-select: none;\n  -webkit-touch-callout: none;\n\n  /* Horizontal swipes page the month; vertical ones still scroll the page. */\n  touch-action: pan-y;\n\n  &-grid {\n    display: flex;\n    flex: 1;\n    flex-direction: column;\n    min-height: 0;\n  }\n\n  &-weekdays {\n    display: grid;\n    grid-template-columns: repeat(var(--days), minmax(0, 1fr));\n    border-bottom: 1px solid var(--border-color);\n  }\n\n  &-weekday {\n    padding: 0.5em 0.625em;\n    overflow: hidden;\n    font-size: 0.6875em;\n    font-weight: 600;\n    letter-spacing: 0.06em;\n    text-overflow: ellipsis;\n    text-transform: uppercase;\n    white-space: nowrap;\n    color: var(--text-dark);\n  }\n\n  &-viewport {\n    flex: 1;\n    min-height: 0;\n  }\n\n  /* Six equal rows whatever the month, so every pane has the same height while swiping and a\n     row never grows to fit its events: that is what the `+N` buttons are for. */\n  &-pane {\n    display: grid;\n    grid-template-rows: repeat(6, minmax(0, 1fr));\n    background: var(--background);\n  }\n\n  &-week {\n    position: relative;\n    display: grid;\n    grid-template-columns: repeat(var(--days), minmax(0, 1fr));\n    min-height: 0;\n\n    & + & {\n      border-top: 1px solid var(--border-color);\n    }\n  }\n\n  &-cell {\n    position: relative;\n    min-width: 0;\n    padding: 0.25em;\n    outline: none;\n\n    & + & {\n      border-left: 1px solid var(--border-color);\n    }\n\n    &:focus-visible {\n      box-shadow: inset 0 0 0 2px var(--focus-border-color, var(--primary-color));\n    }\n\n    &-outside {\n      background: color-mix(in oklab, var(--background-accent) 40%, transparent);\n      color: var(--text-light);\n    }\n\n    &-creatable {\n      cursor: pointer;\n      -webkit-tap-highlight-color: transparent;\n\n      &:hover {\n        background: color-mix(in oklab, var(--primary-color) 5%, transparent);\n      }\n    }\n\n    &-target {\n      background: color-mix(in oklab, var(--primary-color) 14%, transparent);\n    }\n  }\n\n  &-dayNumber {\n    display: inline-grid;\n    place-items: center;\n    min-width: 1.75em;\n    height: 1.75em;\n    padding: 0 0.25em;\n    border-radius: 0.875em;\n    font-size: 0.8125em;\n    font-weight: 500;\n    font-variant-numeric: tabular-nums;\n    transition: background-color 0.15s ease;\n\n    &-today {\n      background: var(--custom-calendar-today-color);\n      color: var(--text-on-primary);\n      font-weight: 700;\n    }\n\n    &-link {\n      cursor: pointer;\n      -webkit-tap-highlight-color: transparent;\n\n      &:not(.monthView-dayNumber-today):hover {\n        background: var(--background-accent);\n      }\n    }\n  }\n\n  /* Top right of the row's first cell, in the band above the bars and level with the day number:\n     the cell's 0.25em padding and the number's 1.75em at 0.8125em, in this label's 0.6875em. */\n  &-weekNumber {\n    position: absolute;\n    top: calc(0.25em / 0.6875);\n    right: 0.5em;\n\n    /* Clear of the day number, in a narrow cell or a long translation. */\n    max-width: calc(100% - 3.5em);\n    overflow: hidden;\n    font-size: 0.6875em;\n    font-weight: 600;\n    line-height: calc(1.75em * 0.8125 / 0.6875);\n    white-space: nowrap;\n    text-overflow: ellipsis;\n    color: var(--text-light);\n  }\n\n  /* Laid over the week's cells and starting below the day numbers. Its `grid-auto-rows`,\n     `row-gap` and `padding-top` are read back to work out how many lanes fit, so change them\n     here rather than overriding them on a bar. */\n  &-bars {\n    position: absolute;\n    inset: 0;\n    display: grid;\n    grid-template-columns: repeat(var(--days), minmax(0, 1fr));\n    grid-auto-rows: 1.375em;\n    row-gap: 2px;\n    align-content: start;\n    padding-top: 1.75em;\n    overflow: hidden;\n    pointer-events: none;\n  }\n\n  &-bar {\n    --event: var(--event-color, var(--primary-color));\n\n    display: flex;\n    align-items: center;\n    gap: 0.3em;\n    min-width: 0;\n    margin: 0 0.25em;\n    padding: 0 0.4em;\n    overflow: hidden;\n    border-radius: var(--custom-calendar-event-radius);\n    font-size: 0.75em;\n    white-space: nowrap;\n    pointer-events: auto;\n    transition:\n      background-color 0.15s ease,\n      opacity 0.15s ease;\n\n    &-filled {\n      background: color-mix(in oklab, var(--event) 22%, var(--background));\n      box-shadow: inset 3px 0 0 var(--event);\n      font-weight: 500;\n\n      &:hover {\n        background: color-mix(in oklab, var(--event) 32%, var(--background));\n      }\n    }\n\n    &-timed:hover {\n      background: color-mix(in oklab, var(--event) 12%, transparent);\n    }\n\n    &-continuesBefore {\n      margin-left: 0;\n      border-top-left-radius: 0;\n      border-bottom-left-radius: 0;\n      box-shadow: none;\n    }\n\n    &-continuesAfter {\n      margin-right: 0;\n      border-top-right-radius: 0;\n      border-bottom-right-radius: 0;\n    }\n\n    &-interactive {\n      cursor: pointer;\n      -webkit-tap-highlight-color: transparent;\n    }\n\n    &-dragging {\n      opacity: 0.45;\n    }\n\n    &-title {\n      min-width: 0;\n      overflow: hidden;\n      text-overflow: ellipsis;\n    }\n  }\n\n  &-dot {\n    flex: none;\n    width: 0.5em;\n    height: 0.5em;\n    border-radius: 50%;\n    background: var(--event);\n  }\n\n  &-time {\n    flex: none;\n    font-variant-numeric: tabular-nums;\n    color: var(--text-dark);\n  }\n\n  &-more {\n    align-self: center;\n    margin: 0 0.25em;\n    padding: 0 0.4em;\n    border-radius: var(--custom-calendar-event-radius);\n    font-size: 0.75em;\n    font-weight: 600;\n    white-space: nowrap;\n    color: var(--text-dark);\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n    pointer-events: auto;\n\n    &:hover {\n      background: var(--background-accent-light);\n    }\n  }\n\n  &-list {\n    display: flex;\n    flex-direction: column;\n    gap: 0.25em;\n    width: 18em;\n    max-width: 100%;\n\n    &-title {\n      padding: 0.25em 0.625em;\n      font-size: 0.8125em;\n      font-weight: 600;\n      color: var(--text-dark);\n    }\n\n    /* Scrolls inside the popover rather than growing it past the screen. */\n    &-events {\n      max-height: 18em;\n    }\n\n    &-create {\n      align-self: flex-start;\n      margin: 0.25em 0.625em 0.125em;\n    }\n  }\n}\n","import {\n  useLayoutEffect,\n  useRef,\n  useState,\n  type CSSProperties,\n  type KeyboardEvent,\n  type MouseEvent,\n  type ReactElement,\n} from 'react';\nimport cx from 'classnames';\n\nimport Button from 'components/common/button';\nimport Popover from 'components/common/popover';\nimport { PopoverAlign, PopoverPlacement } from 'components/common/popover/popover.types';\nimport { isSelfActivation } from 'components/common/utils/activationKey';\nimport { isDismissClick } from 'components/common/utils/dismissClick';\nimport { CalendarView, type CalendarEvent } from '../../calendar.types';\nimport {\n  addDays,\n  addMonths,\n  allDaySlot,\n  dayDiff,\n  dateToDay,\n  gridKeyTarget,\n  isSameDay,\n  periodRange,\n  shownDays,\n  startOfMonth,\n  startOfWeek,\n  weekNumber,\n} from '../../dates';\nimport { formatDayNumber, formatLongDate, formatMonthTitle, formatTime, formatWeekday } from '../../format';\nimport { clamp, pageStep } from '../../gesture';\nimport { eventDays, eventsByDay, isAllDayLike, limitLanes, moveByDays, packLanes, type LaneBar } from '../../layout';\nimport { useGestures, type GesturePoint } from '../../useGestures';\nimport { usePager } from '../../usePager';\nimport AgendaList from '../agendaList';\nimport type { MonthDrag, MonthViewProps } from './monthView.types';\n\nimport pagerStyles from '../../pager.module.pcss';\nimport styles from './monthView.module.pcss';\nimport { cssColor } from '../../../styles/tone';\n\nconst STEPS = [-1, 0, 1] as const;\n\nfunction MonthView<E extends CalendarEvent>({\n  events,\n  date,\n  today,\n  locale,\n  weekStartsOn,\n  hidden,\n  showWeekNumbers,\n  text,\n  onDateChange,\n  onOpenDay,\n  onEventClick,\n  onEventChange,\n  onSlotCreate,\n  renderEvent,\n  eventClassName,\n  classNames,\n  slotStyles,\n}: MonthViewProps<E>): ReactElement {\n  const rootRef = useRef<HTMLDivElement>(null);\n  const viewportRef = useRef<HTMLDivElement>(null);\n  const currentRef = useRef<HTMLDivElement>(null);\n  const pendingFocus = useRef<string | null>(null);\n  const dragRef = useRef<MonthDrag<E> | null>(null);\n  const [capacity, setCapacity] = useState(Infinity);\n  const [list, setList] = useState<{ day: Date; anchor: HTMLElement; fromKeyboard: boolean } | null>(null);\n  const [drag, setDrag] = useState<MonthDrag<E> | null>(null);\n\n  const month = startOfMonth(date);\n  const weekdays = shownDays(startOfWeek(today, weekStartsOn), 7, hidden);\n  const gridStart = periodRange(CalendarView.Month, month, weekStartsOn).start;\n  const rowDates = (paneStart: Date, row: number) => shownDays(addDays(paneStart, row * 7), 7, hidden);\n\n  const pager = usePager(\n    dateToDay(month),\n    (step) => onDateChange(addMonths(date, step)),\n    () => currentRef.current?.querySelector<HTMLElement>('[tabindex=\"0\"]') ?? null,\n  );\n\n  // Lanes that fit a cell, from the real geometry, so `+N` appears exactly when bars would clip.\n  // Without layout (jsdom) there is nothing to measure, and every event is shown.\n  useLayoutEffect(() => {\n    const viewport = viewportRef.current;\n    if (!viewport) return;\n    function measure() {\n      const bars = viewport!.querySelector<HTMLElement>('[data-cal-bars]');\n      if (!bars || bars.clientHeight === 0) {\n        setCapacity(Infinity);\n        return;\n      }\n      const style = getComputedStyle(bars);\n      const lane = parseFloat(style.gridAutoRows);\n      const gap = parseFloat(style.rowGap) || 0;\n      const top = parseFloat(style.paddingTop) || 0;\n      setCapacity(lane > 0 ? Math.max(1, Math.floor((bars.clientHeight - top + gap) / (lane + gap))) : Infinity);\n    }\n    measure();\n    const observer = new ResizeObserver(measure);\n    observer.observe(viewport);\n    return () => observer.disconnect();\n  }, []);\n\n  // Arrowing onto a day of the next month pages first, so the cell to focus only exists after\n  // the render that follows.\n  useLayoutEffect(() => {\n    const key = pendingFocus.current;\n    if (key === null) return;\n    pendingFocus.current = null;\n    currentRef.current?.querySelector<HTMLElement>(`[data-cal-day=\"${key}\"]`)?.focus();\n  });\n\n  function dayAt(point: GesturePoint): Date | null {\n    const pane = currentRef.current;\n    if (!pane) return null;\n    const rect = pane.getBoundingClientRect();\n    if (rect.width === 0 || rect.height === 0) return null;\n    const count = weekdays.length;\n    const column = clamp(Math.floor(((point.x - rect.left) / rect.width) * count), 0, count - 1);\n    const row = clamp(Math.floor(((point.y - rect.top) / rect.height) * 6), 0, 5);\n    return rowDates(gridStart, row)[column];\n  }\n\n  function cellFor(day: Date): HTMLElement | null {\n    return currentRef.current?.querySelector<HTMLElement>(`[data-cal-day=\"${dateToDay(day)}\"]`) ?? null;\n  }\n\n  // Mirrored in a ref, as in the time grid: a release can arrive before the render of the move\n  // before it, and has to drop where that move was.\n  function setDragState(next: MonthDrag<E> | null) {\n    dragRef.current = next;\n    setDrag(next);\n  }\n\n  useGestures<MonthDrag<E>>(rootRef, {\n    onPress: pager.finish,\n    canSwipe: () => true,\n    onSwipeMove: pager.move,\n    onSwipeEnd: (dx, velocity) => pager.settle(pageStep(dx, pager.width(), velocity)),\n    dragFrom: (target, point) => {\n      if (!currentRef.current?.contains(target)) return null;\n      const grabbed = dayAt(point);\n      if (grabbed === null) return null;\n      const element = target.closest<HTMLElement>('[data-cal-event]');\n      if (element) {\n        const event = onEventChange && events.find((candidate) => String(candidate.id) === element.dataset.calEvent);\n        return event && !event.readOnly ? { event, grabbed, offset: 0 } : null;\n      }\n      // A day number that opens its day is a link, and a press on it is a click however it moves.\n      if (!onSlotCreate || target.closest('[data-cal-more]')) return null;\n      if (onOpenDay && target.closest('[data-cal-day-number]')) return null;\n      return { event: null, grabbed, offset: 0 };\n    },\n    onDragStart: setDragState,\n    onDragMove: (point) => {\n      const current = dragRef.current;\n      const day = dayAt(point);\n      if (!current || day === null) return;\n      const offset = dayDiff(current.grabbed, day);\n      if (offset !== current.offset) setDragState({ ...current, offset });\n    },\n    onDragEnd: (point) => {\n      const current = dragRef.current;\n      setDragState(null);\n      const day = dayAt(point);\n      if (!current || day === null) return;\n      const { event, grabbed } = current;\n      if (!event) {\n        const slot = allDaySlot(grabbed, day);\n        onSlotCreate?.(slot, cellFor(slot.start) ?? rootRef.current!);\n        return;\n      }\n      const offset = dayDiff(grabbed, day);\n      if (offset !== 0) onEventChange?.({ event, ...moveByDays(event, offset) });\n    },\n    onDragCancel: () => setDragState(null),\n  });\n\n  function openList(day: Date, fromKeyboard: boolean) {\n    const anchor = cellFor(day);\n    if (anchor) setList({ day, anchor, fromKeyboard });\n  }\n\n  function handleCellClick(clickEvent: MouseEvent<HTMLDivElement>, day: Date) {\n    if (onOpenDay && (clickEvent.target as Element).closest('[data-cal-day-number]')) {\n      onOpenDay(day);\n      return;\n    }\n    if (isDismissClick(clickEvent.nativeEvent)) return;\n    onSlotCreate?.(allDaySlot(day), clickEvent.currentTarget);\n  }\n\n  function handleKeyDown(keyEvent: KeyboardEvent<HTMLDivElement>, day: Date) {\n    const next = gridKeyTarget(keyEvent.key, day, weekStartsOn, hidden);\n    if (!next) {\n      if (isSelfActivation(keyEvent)) {\n        keyEvent.preventDefault();\n        openList(day, true);\n      }\n      return;\n    }\n    keyEvent.preventDefault();\n    pendingFocus.current = dateToDay(next);\n    onDateChange(next);\n  }\n\n  function renderBar(bar: LaneBar<E>) {\n    const { event } = bar;\n    const filled = isAllDayLike(event) || bar.span > 1 || bar.continuesBefore || bar.continuesAfter;\n    const interactive = Boolean(onEventClick) || (Boolean(onEventChange) && !event.readOnly);\n    return (\n      <div\n        key={event.id}\n        data-cal-event={String(event.id)}\n        className={cx(\n          styles.monthViewBar,\n          filled ? styles.monthViewBarFilled : styles.monthViewBarTimed,\n          bar.continuesBefore && styles.monthViewBarContinuesBefore,\n          bar.continuesAfter && styles.monthViewBarContinuesAfter,\n          interactive && styles.monthViewBarInteractive,\n          drag?.event === event && styles.monthViewBarDragging,\n          classNames?.event,\n          eventClassName?.(event),\n        )}\n        style={\n          {\n            '--event-color': event.color && cssColor(event.color),\n            gridColumn: `${bar.start + 1} / span ${bar.span}`,\n            gridRow: bar.lane + 1,\n            ...slotStyles?.event,\n          } as CSSProperties\n        }\n        onClick={(clickEvent) => onEventClick?.(event, clickEvent.currentTarget)}\n      >\n        {!filled && <span className={styles.monthViewDot} />}\n        {!filled && <span className={styles.monthViewTime}>{formatTime(event.start, locale)}</span>}\n        <span className={styles.monthViewBarTitle}>\n          {renderEvent ? renderEvent(event, { singleLine: true }) : event.title}\n        </span>\n      </div>\n    );\n  }\n\n  function renderPane(step: (typeof STEPS)[number]) {\n    const paneMonth = addMonths(month, step);\n    const isCurrent = step === 0;\n    const paneStart = periodRange(CalendarView.Month, paneMonth, weekStartsOn).start;\n\n    let target: { from: Date; to: Date } | null = null;\n    if (isCurrent && drag?.event && drag.offset !== 0) {\n      const { first, last } = eventDays(drag.event);\n      target = { from: addDays(first, drag.offset), to: addDays(last, drag.offset) };\n    } else if (isCurrent && drag && !drag.event) {\n      const over = addDays(drag.grabbed, drag.offset);\n      target = drag.offset < 0 ? { from: over, to: drag.grabbed } : { from: drag.grabbed, to: over };\n    }\n\n    return (\n      <div\n        key={dateToDay(paneMonth)}\n        ref={isCurrent ? currentRef : undefined}\n        className={cx(\n          pagerStyles.pagerPane,\n          step === -1 && pagerStyles.pagerPanePrev,\n          step === 1 && pagerStyles.pagerPaneNext,\n          styles.monthViewPane,\n        )}\n        inert={!isCurrent}\n        aria-hidden={!isCurrent || undefined}\n      >\n        {Array.from({ length: 6 }, (_, row) => {\n          const dates = rowDates(paneStart, row);\n          const perDay = eventsByDay(events, dates);\n          const lanes = limitLanes(packLanes(events, dates), dates.length, capacity);\n          // Numbered by the ISO week holding most of its days, which the middle one is in.\n          const week = showWeekNumbers ? text.weekNumber(weekNumber(dates[Math.floor(dates.length / 2)])) : undefined;\n          return (\n            <div key={dateToDay(dates[0])} role='row' aria-label={week} className={styles.monthViewWeek}>\n              {dates.map((day, column) => {\n                const count = perDay[column].length;\n                const isToday = isSameDay(day, today);\n                const name = formatLongDate(day, locale);\n                return (\n                  <div\n                    key={dateToDay(day)}\n                    role='gridcell'\n                    data-cal-day={dateToDay(day)}\n                    tabIndex={isCurrent && isSameDay(day, date) ? 0 : -1}\n                    aria-current={isToday ? 'date' : undefined}\n                    aria-label={count > 0 ? `${name}, ${text.eventCount(count)}` : name}\n                    className={cx(\n                      styles.monthViewCell,\n                      day.getMonth() !== paneMonth.getMonth() && styles.monthViewCellOutside,\n                      onSlotCreate && styles.monthViewCellCreatable,\n                      target &&\n                        dayDiff(target.from, day) >= 0 &&\n                        dayDiff(day, target.to) >= 0 &&\n                        styles.monthViewCellTarget,\n                      classNames?.cell,\n                    )}\n                    style={slotStyles?.cell}\n                    onClick={(clickEvent) => handleCellClick(clickEvent, day)}\n                    onKeyDown={(keyEvent) => handleKeyDown(keyEvent, day)}\n                  >\n                    <span\n                      data-cal-day-number\n                      aria-hidden\n                      className={cx(\n                        styles.monthViewDayNumber,\n                        isToday && styles.monthViewDayNumberToday,\n                        onOpenDay && styles.monthViewDayNumberLink,\n                      )}\n                    >\n                      {formatDayNumber(day, locale)}\n                    </span>\n                    {week && column === 0 && (\n                      <span aria-hidden className={styles.monthViewWeekNumber}>\n                        {week}\n                      </span>\n                    )}\n                  </div>\n                );\n              })}\n              {/* Pointer only: a keyboard reaches a day's events through Enter on its cell. */}\n              <div data-cal-bars aria-hidden className={styles.monthViewBars}>\n                {lanes.visible.map(renderBar)}\n                {lanes.hidden.map(\n                  (count, column) =>\n                    count > 0 && (\n                      <div\n                        key={`more-${column}`}\n                        data-cal-more\n                        className={styles.monthViewMore}\n                        style={{ gridColumn: column + 1, gridRow: lanes.moreLane + 1 }}\n                        onClick={() => openList(dates[column], false)}\n                      >\n                        {text.more(count)}\n                      </div>\n                    ),\n                )}\n              </div>\n            </div>\n          );\n        })}\n      </div>\n    );\n  }\n\n  return (\n    <div ref={rootRef} className={styles.monthView} style={{ '--days': weekdays.length } as CSSProperties}>\n      <div role='grid' aria-label={formatMonthTitle(date, locale)} className={styles.monthViewGrid}>\n        <div role='row' className={styles.monthViewWeekdays}>\n          {weekdays.map((day) => (\n            <div\n              key={day.getDay()}\n              role='columnheader'\n              aria-label={formatWeekday(day, locale, 'long')}\n              className={cx(styles.monthViewWeekday, classNames?.dayHeader)}\n              style={slotStyles?.dayHeader}\n            >\n              {formatWeekday(day, locale, 'short')}\n            </div>\n          ))}\n        </div>\n        <div ref={viewportRef} className={cx(pagerStyles.pagerViewport, styles.monthViewViewport)}>\n          <div ref={pager.trackRef} className={pagerStyles.pagerTrack}>\n            {STEPS.map(renderPane)}\n          </div>\n        </div>\n      </div>\n      <Popover\n        anchor={list?.anchor ?? null}\n        isOpen={list !== null}\n        onOpenChange={(open) => !open && setList(null)}\n        // Enter on a cell is a key event of ours, which Popover cannot see, so it is told.\n        focusOnOpen={list?.fromKeyboard ?? false}\n        placement={PopoverPlacement.Bottom}\n        align={PopoverAlign.Start}\n        content={\n          list && (\n            <div className={styles.monthViewList}>\n              <div className={styles.monthViewListTitle}>{formatLongDate(list.day, locale)}</div>\n              <AgendaList\n                className={styles.monthViewListEvents}\n                events={events}\n                start={list.day}\n                days={1}\n                showDayHeaders={false}\n                locale={locale}\n                today={today}\n                text={text}\n                onEventClick={(event) => {\n                  setList(null);\n                  // The list's own row unmounts with the popover, so the day's cell anchors it.\n                  onEventClick?.(event, list.anchor);\n                }}\n                renderEvent={renderEvent}\n                eventClassName={eventClassName}\n                classNames={classNames}\n                slotStyles={slotStyles}\n              />\n              {onSlotCreate && (\n                <Button\n                  className={styles.monthViewListCreate}\n                  onClick={() => {\n                    setList(null);\n                    onSlotCreate(allDaySlot(list.day), list.anchor);\n                  }}\n                >\n                  {text.newEvent}\n                </Button>\n              )}\n            </div>\n          )\n        }\n      />\n    </div>\n  );\n}\n\nexport default MonthView;\n","/* One vertical scroller holds the sticky head and the body, so the head's day columns line up\n   with the body's by construction, scrollbar or not. Only the two tracks inside move sideways.\n   Everything positioned in time uses percentages of the day, so zooming rewrites one custom\n   property and nothing else. */\n.timeGrid {\n  --gutter: 3.5em;\n  --lane: 1.375em;\n\n  position: relative;\n  flex: 1;\n  min-height: 0;\n  overflow-y: auto;\n  overscroll-behavior: contain;\n  outline: none;\n  user-select: none;\n  -webkit-touch-callout: none;\n\n  /* Horizontal swipes page, pinches zoom, vertical drags still scroll. */\n  touch-action: pan-y;\n\n  &-head {\n    position: sticky;\n    top: 0;\n    z-index: 1;\n    display: grid;\n    grid-template-columns: var(--gutter) minmax(0, 1fr);\n    border-bottom: 1px solid var(--border-color);\n    background: var(--background);\n  }\n\n  &-corner {\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    justify-content: flex-end;\n    padding-bottom: 0.25em;\n  }\n\n  /* At the top, level with the weekday names, while the toggle stays at the bottom. Wraps rather\n     than spill over the first day: a translation such as `Semaine 40` is wider than the gutter. */\n  &-weekNumber {\n    max-width: 100%;\n    margin-bottom: auto;\n    padding-top: 0.55em;\n    overflow-wrap: anywhere;\n    font-size: 0.6875em;\n    font-weight: 600;\n    text-align: center;\n    color: var(--text-light);\n  }\n\n  /* Points down to expand and up to collapse: the rotation says which, so it stays. Two classes,\n     to outrank Button's own font-size whichever stylesheet loads first. */\n  & &-toggle {\n    font-size: 0.75em;\n    transition: transform 0.2s ease;\n\n    &-open {\n      transform: rotate(180deg);\n    }\n  }\n\n  &-headPane {\n    display: grid;\n    grid-template-columns: repeat(var(--days), minmax(0, 1fr));\n    align-content: start;\n    background: var(--background);\n  }\n\n  &-dayHeader {\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    gap: 0.125em;\n    padding: 0.375em 0 0.25em;\n    border: 0;\n    background: transparent;\n    color: inherit;\n    font: inherit;\n\n    &-link {\n      cursor: pointer;\n      -webkit-tap-highlight-color: transparent;\n\n      &:hover .timeGrid-dayNumber:not(.timeGrid-dayNumber-today) {\n        background: var(--background-accent-light);\n      }\n\n      &:focus-visible {\n        outline: 2px solid var(--focus-border-color, var(--primary-color));\n        outline-offset: -2px;\n        border-radius: var(--default-border-radius);\n      }\n    }\n  }\n\n  &-weekday {\n    font-size: 0.6875em;\n    font-weight: 600;\n    letter-spacing: 0.06em;\n    text-transform: uppercase;\n    color: var(--text-dark);\n  }\n\n  &-dayNumber {\n    display: grid;\n    place-items: center;\n    width: 1.875em;\n    height: 1.875em;\n    border-radius: 50%;\n    font-size: 1.125em;\n    font-weight: 500;\n    font-variant-numeric: tabular-nums;\n    transition: background-color 0.15s ease;\n\n    &-today {\n      background: var(--custom-calendar-today-color);\n      color: var(--text-on-primary);\n      font-weight: 700;\n    }\n  }\n\n  &-allDay {\n    grid-column: 1 / -1;\n    display: grid;\n    grid-template-columns: repeat(var(--days), minmax(0, 1fr));\n    grid-auto-rows: var(--lane);\n    row-gap: 2px;\n    min-height: 0.5em;\n    padding: 2px 0 4px;\n\n    /* A lane's height even when empty, so there is something to click to create. */\n    &-creatable {\n      min-height: var(--lane);\n      cursor: pointer;\n      -webkit-tap-highlight-color: transparent;\n    }\n  }\n\n  &-bar {\n    --event: var(--event-color, var(--primary-color));\n\n    min-width: 0;\n    margin: 0 2px;\n    padding: 0 0.5em;\n    overflow: hidden;\n    border: 0;\n    border-radius: var(--custom-calendar-event-radius);\n    background: color-mix(in oklab, var(--event) 22%, var(--background));\n    box-shadow: inset 3px 0 0 var(--event);\n    color: var(--text-color);\n    font: inherit;\n    font-size: 0.75em;\n    font-weight: 500;\n    text-align: left;\n    white-space: nowrap;\n    text-overflow: ellipsis;\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n\n    &:hover {\n      background: color-mix(in oklab, var(--event) 32%, var(--background));\n    }\n\n    &:focus-visible {\n      outline: 2px solid var(--focus-border-color, var(--primary-color));\n    }\n\n    &-continuesBefore {\n      margin-left: 0;\n      border-top-left-radius: 0;\n      border-bottom-left-radius: 0;\n      box-shadow: none;\n    }\n\n    &-continuesAfter {\n      margin-right: 0;\n      border-top-right-radius: 0;\n      border-bottom-right-radius: 0;\n    }\n\n    &-title {\n      display: block;\n      overflow: hidden;\n      text-overflow: ellipsis;\n    }\n\n    &-dragged {\n      opacity: 0.4;\n    }\n\n    &-preview {\n      background: color-mix(in oklab, var(--event) 32%, var(--background));\n      box-shadow:\n        inset 3px 0 0 var(--event),\n        0 0.25em 0.75em color-mix(in oklab, var(--text-color) 22%, transparent);\n      pointer-events: none;\n    }\n  }\n\n  &-more {\n    align-self: center;\n    margin: 0 2px;\n    padding: 0 0.5em;\n    border-radius: var(--custom-calendar-event-radius);\n    font-size: 0.75em;\n    font-weight: 600;\n    white-space: nowrap;\n    color: var(--text-dark);\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n\n    &:hover {\n      background: var(--background-accent-light);\n    }\n  }\n\n  /* Its own stacking context, which keeps everything drawn in the hours under the sticky head\n     above. Inside it, the now-line and the drag preview come after the events in the markup,\n     and so paint over them without a z-index. */\n  &-body {\n    isolation: isolate;\n    display: grid;\n    grid-template-columns: var(--gutter) minmax(0, 1fr);\n    height: calc(24 * var(--custom-calendar-hour-height));\n  }\n\n  &-gutter {\n    position: relative;\n  }\n\n  &-hour {\n    position: absolute;\n    right: 0.625em;\n    font-size: 0.6875em;\n    font-variant-numeric: tabular-nums;\n    white-space: nowrap;\n    color: var(--text-light);\n    transform: translateY(-50%);\n  }\n\n  &-bodyPane {\n    position: relative;\n    display: grid;\n    grid-template-columns: repeat(var(--days), minmax(0, 1fr));\n    height: 100%;\n    background: var(--background);\n  }\n\n  /* Hour lines, and fainter half-hour lines between them. */\n  &-column {\n    /* Events leave a strip on the right free, so there is always somewhere to press to create. */\n    --column-width: calc(100% - 0.75em);\n    --hour-line: linear-gradient(var(--border-color) 1px, transparent 1px);\n    --half-hour-line: linear-gradient(color-mix(in oklab, var(--border-color) 45%, transparent) 1px, transparent 1px);\n\n    position: relative;\n    min-width: 0;\n    border-left: 1px solid var(--border-color);\n    background-image: var(--hour-line), var(--half-hour-line);\n    background-position:\n      0 0,\n      0 calc(var(--custom-calendar-hour-height) / 2);\n    background-size: 100% var(--custom-calendar-hour-height);\n\n    &-today {\n      background-color: color-mix(in oklab, var(--custom-calendar-today-color) 4%, transparent);\n    }\n\n    /* A layer under the hour lines rather than over them, so it can be opaque and still let them\n       show, and over today's tint, which is the background colour. */\n    &-working {\n      --outside: var(--custom-calendar-off-hours-color);\n      --inside: transparent;\n\n      background-image:\n        var(--hour-line), var(--half-hour-line),\n        linear-gradient(\n          var(--outside) var(--work-start),\n          var(--inside) var(--work-start) var(--work-end),\n          var(--outside) var(--work-end)\n        );\n      background-position:\n        0 0,\n        0 calc(var(--custom-calendar-hour-height) / 2),\n        0 0;\n      background-size:\n        100% var(--custom-calendar-hour-height),\n        100% var(--custom-calendar-hour-height),\n        100% 100%;\n\n      /* `--work-start` is the earlier hour, so an overnight shift is the part outside the band. */\n      &-overnight {\n        --outside: transparent;\n        --inside: var(--custom-calendar-off-hours-color);\n      }\n    }\n\n    &-creatable {\n      cursor: pointer;\n      -webkit-tap-highlight-color: transparent;\n    }\n  }\n\n  &-event {\n    --event: var(--event-color, var(--primary-color));\n\n    position: absolute;\n    box-sizing: border-box;\n    display: flex;\n    flex-direction: column;\n    align-items: stretch;\n    min-width: 0;\n    height: var(--span);\n    margin: 0;\n    padding: 0.2em 0.45em 0.2em 0.6em;\n    overflow: hidden;\n    container-type: size;\n    border: 0;\n    border-radius: var(--custom-calendar-event-radius);\n    background: color-mix(in oklab, var(--event) 22%, var(--background));\n\n    /* The outline in the page colour keeps side-by-side events apart. */\n    box-shadow:\n      inset 3px 0 0 var(--event),\n      0 0 0 1px var(--background);\n    color: var(--text-color);\n    font: inherit;\n    font-size: 0.75em;\n    line-height: 1.3;\n    text-align: left;\n    transition:\n      background-color 0.15s ease,\n      opacity 0.15s ease;\n\n    &:hover {\n      background: color-mix(in oklab, var(--event) 32%, var(--background));\n    }\n\n    &:focus-visible {\n      z-index: 1;\n      outline: 2px solid var(--focus-border-color, var(--primary-color));\n    }\n\n    &-short {\n      flex-direction: row;\n\n      /* Not `baseline`: a 15-minute event is shorter than its line, and centring clips it evenly\n         rather than cutting off the bottom. Title and time share a font size, so they still line up. */\n      align-items: center;\n      gap: 0.4em;\n      padding-block: 0;\n    }\n\n    /* Too short for its label, which would be clipped to nothing: a bar of its real length, and\n       the label on a line of its own beneath it, in the room packDay leaves for it. */\n    &-brief {\n      /* One line, with room for the descenders that a line height of 1 clips in Firefox. Keep\n         BRIEF_LABEL_MINUTES in layout.ts at least this tall at the default hour height. */\n      --label: 1.25em;\n\n      isolation: isolate;\n      height: calc(var(--span) + var(--label));\n      align-items: flex-end;\n      line-height: var(--label);\n      background: none;\n      box-shadow: none;\n\n      /* Under the label, which it covers once zoomed in: see the @container rule below. */\n      &::before {\n        content: '';\n        position: absolute;\n        z-index: -1;\n        top: 0;\n        right: 0;\n        left: 0;\n        height: max(2px, 100% - var(--label));\n        border-radius: inherit;\n        background: var(--event);\n        transition:\n          background-color 0.15s ease,\n          opacity 0.15s ease;\n      }\n\n      &:hover {\n        background: none;\n\n        &::before {\n          opacity: 0.75;\n        }\n      }\n\n      /* On the bar's end, which is the event's, rather than under the label. */\n      .timeGrid-resize {\n        bottom: calc(var(--label) - 0.3em);\n        height: 0.6em;\n        max-height: none;\n      }\n    }\n\n    &-continuesBefore {\n      border-top-left-radius: 0;\n      border-top-right-radius: 0;\n    }\n\n    &-continuesAfter {\n      border-bottom-left-radius: 0;\n      border-bottom-right-radius: 0;\n    }\n\n    &-interactive {\n      cursor: pointer;\n      -webkit-tap-highlight-color: transparent;\n    }\n\n    &-dragged {\n      opacity: 0.4;\n    }\n\n    &-title {\n      flex: none;\n      overflow: hidden;\n      font-weight: 600;\n      white-space: nowrap;\n      text-overflow: ellipsis;\n    }\n\n    &-time {\n      overflow: hidden;\n      font-variant-numeric: tabular-nums;\n      white-space: nowrap;\n      text-overflow: ellipsis;\n      color: var(--text-dark);\n    }\n  }\n\n  &-resize {\n    position: absolute;\n    right: 0;\n    bottom: 0;\n    left: 0;\n    height: 0.625em;\n\n    /* Half at most, so a short event keeps a part that moves it: a finger's grip is taller. */\n    max-height: 50%;\n    cursor: ns-resize;\n\n    /* A visible grip where a finger is the pointer, since there is no hover to reveal it. */\n    @media (pointer: coarse) {\n      height: 1.5em;\n\n      &::after {\n        content: '';\n        position: absolute;\n        bottom: 0.3em;\n        left: 50%;\n        width: 1.5em;\n        height: 0.25em;\n        border-radius: 0.125em;\n        background: color-mix(in oklab, var(--event) 70%, var(--background));\n        translate: -50% 0;\n      }\n    }\n  }\n\n  &-preview {\n    --event: var(--event-color, var(--primary-color));\n\n    position: absolute;\n    left: 0;\n    width: var(--column-width);\n    box-sizing: border-box;\n    display: flex;\n    flex-direction: column;\n    padding: 0.2em 0.45em 0.2em 0.6em;\n    overflow: hidden;\n    border-radius: var(--custom-calendar-event-radius);\n    background: color-mix(in oklab, var(--event) 32%, var(--background));\n    box-shadow:\n      inset 3px 0 0 var(--event),\n      0 0.5em 1.25em color-mix(in oklab, var(--text-color) 22%, transparent);\n    color: var(--text-color);\n    font-size: 0.75em;\n    line-height: 1.3;\n    pointer-events: none;\n\n    &-time {\n      font-weight: 600;\n      font-variant-numeric: tabular-nums;\n      white-space: nowrap;\n    }\n  }\n\n  /* The anchor `onSlotCreate` hands out, moved over each new slot. Transparent rather than\n     hidden, so it keeps the box a popover measures and anchors to. */\n  &-slot {\n    position: absolute;\n    opacity: 0;\n    pointer-events: none;\n  }\n\n  &-now {\n    position: absolute;\n    right: 0;\n    left: 0;\n    height: 2px;\n    margin-top: -1px;\n    background: var(--custom-calendar-now-color);\n    pointer-events: none;\n\n    &::before {\n      content: '';\n      position: absolute;\n      top: 50%;\n      left: 0;\n      width: 0.625em;\n      height: 0.625em;\n      border-radius: 50%;\n      background: var(--custom-calendar-now-color);\n      translate: -50% -50%;\n      animation: calendarNowPulse 2.4s ease-in-out infinite;\n    }\n  }\n}\n\n/* Zoomed in until its bar holds its label, a brief event is drawn like any other, its label\n   centred in the bar. 2.5em is twice the label's line: a condition cannot read `--label`. */\n@container (height >= 2.5em) {\n  .timeGrid-event-brief::before {\n    background: color-mix(in oklab, var(--event) 22%, var(--background));\n    box-shadow: inset 3px 0 0 var(--event);\n  }\n\n  .timeGrid-event-brief:hover::before {\n    background: color-mix(in oklab, var(--event) 32%, var(--background));\n    opacity: 1;\n  }\n\n  .timeGrid-event-brief .timeGrid-event-title,\n  .timeGrid-event-brief .timeGrid-event-time {\n    align-self: flex-start;\n    margin-top: calc((100cqh - 2 * var(--label)) / 2);\n  }\n}\n\n/* An event shorter than its text shows its colour alone, not a slice of clipped letters. The `em`\n   is the event's own font size, and zooming in brings the text back. */\n@container (height < 0.8em) {\n  .timeGrid-event-title,\n  .timeGrid-event-time {\n    display: none;\n  }\n}\n\n@keyframes calendarNowPulse {\n  50% {\n    box-shadow: 0 0 0 0.3em color-mix(in oklab, var(--custom-calendar-now-color) 25%, transparent);\n  }\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .timeGrid .timeGrid-toggle {\n    transition: none;\n  }\n\n  .timeGrid-now::before {\n    animation: none;\n  }\n}\n","import {\n  useEffect,\n  useLayoutEffect,\n  useRef,\n  useState,\n  type CSSProperties,\n  type MouseEvent,\n  type ReactElement,\n  type RefObject,\n} from 'react';\nimport cx from 'classnames';\nimport { faChevronDown } from '@fortawesome/free-solid-svg-icons';\n\nimport ActionIcon from 'components/common/actionIcon';\nimport { isDismissClick } from 'components/common/utils/dismissClick';\nimport type { CalendarEvent } from '../../calendar.types';\nimport {\n  addDays,\n  allDaySlot,\n  atMinutes,\n  dayDiff,\n  dateToDay,\n  endAfter,\n  isSameDay,\n  MINUTES_PER_DAY,\n  minutesOfDay,\n  periodRange,\n  shownDays,\n  stepDate,\n  weekNumber,\n} from '../../dates';\nimport {\n  formatClockRange,\n  formatDayNumber,\n  formatHour,\n  formatLongDate,\n  formatTimeRange,\n  formatWeekday,\n} from '../../format';\nimport { clamp, pageStep, snap } from '../../gesture';\nimport {\n  BRIEF_EVENT_MINUTES,\n  eventEnd,\n  isAllDayLike,\n  limitLanes,\n  moveByDays,\n  packDay,\n  packLanes,\n  type LaneBar,\n  type LimitedLanes,\n  type TimedBox,\n} from '../../layout';\nimport { useGestures, type GesturePoint } from '../../useGestures';\nimport { useNow } from '../../useNow';\nimport { usePager } from '../../usePager';\nimport type { AllDayDrag, TimeDrag, TimeGridProps, TimeGridZoom } from './timeGrid.types';\n\nimport pagerStyles from '../../pager.module.pcss';\nimport styles from './timeGrid.module.pcss';\nimport { cssColor } from '../../../styles/tone';\n\nconst STEPS = [-1, 0, 1] as const;\nconst HOURS = Array.from({ length: 23 }, (_, i) => i + 1);\nconst ALL_DAY_LANES = 3;\n/** Below this an event is drawn on one line, its time after its title. */\nconst SHORT_EVENT_MINUTES = 45;\n/** Hour first scrolled to when today is not in view and no working hours are set. */\nconst DEFAULT_SCROLL_HOUR = 7;\n/** Deepest zoom, as a multiple of the theme's hour height. */\nconst MAX_ZOOM = 4;\n\ninterface ZoomElements {\n  scroller: HTMLElement;\n  head: HTMLElement;\n  body: HTMLElement;\n}\n\n/**\n * Sets the hour height, and scrolls so `anchorHour` stays under `clientY`: zooming pivots on the\n * fingers or the cursor instead of the top of the day.\n */\nfunction zoomTo(\n  { scroller, head, body }: ZoomElements,\n  zoomRef: RefObject<TimeGridZoom | null>,\n  hour: number,\n  anchorHour: number,\n  clientY: number,\n) {\n  const base = zoomRef.current?.base ?? body.getBoundingClientRect().height / 24;\n  if (!base) return;\n  // Zoomed out, the whole day fits exactly.\n  const min = Math.max((scroller.clientHeight - head.offsetHeight) / 24, 1);\n  const next = clamp(hour, Math.min(min, base), base * MAX_ZOOM);\n  zoomRef.current = { hour: next, base };\n  scroller.style.setProperty('--custom-calendar-hour-height', `${next}px`);\n  const top = scroller.getBoundingClientRect().top;\n  scroller.scrollTop = body.offsetTop + anchorHour * next - (clientY - top);\n}\n\nfunction TimeGrid<E extends CalendarEvent>({\n  events,\n  date,\n  period,\n  locale,\n  weekStartsOn,\n  slotMinutes,\n  hidden,\n  scrollToHour,\n  workingHours,\n  showWeekNumbers,\n  text,\n  onDateChange,\n  onOpenDay,\n  onEventClick,\n  onEventChange,\n  onSlotCreate,\n  renderEvent,\n  eventClassName,\n  classNames,\n  slotStyles,\n  zoomRef,\n}: TimeGridProps<E>): ReactElement {\n  const scrollerRef = useRef<HTMLDivElement>(null);\n  const headRef = useRef<HTMLDivElement>(null);\n  const bodyRef = useRef<HTMLDivElement>(null);\n  const currentRef = useRef<HTMLDivElement>(null);\n  const allDayRef = useRef<HTMLDivElement>(null);\n  const dragRef = useRef<TimeDrag<E> | null>(null);\n  const slotRef = useRef<HTMLDivElement>(null);\n  const snapshot = useRef(events);\n  const pinchRef = useRef<{ hour: number; anchorHour: number; distance: number } | null>(null);\n  const [drag, setDrag] = useState<TimeDrag<E> | null>(null);\n  const [allDayOpen, setAllDayOpen] = useState(false);\n  const now = useNow();\n\n  const range = periodRange(period, date, weekStartsOn, hidden);\n  const rangeStart = range.start;\n  /** Calendar days the period spans, hidden ones included. */\n  const periodDays = dayDiff(range.start, range.end);\n  const columns = shownDays(rangeStart, periodDays, hidden);\n  // While a drag runs, the grid renders the events as they were when it began: the touch that\n  // started it keeps targeting the element it landed on, and iOS stops delivering its moves if\n  // a re-render replaces that element.\n  const shown = drag ? snapshot.current : events;\n\n  const pager = usePager(\n    dateToDay(rangeStart),\n    (step) => onDateChange(stepDate(period, date, step, hidden)),\n    () => scrollerRef.current,\n  );\n\n  function zoomElements(): ZoomElements | null {\n    const scroller = scrollerRef.current;\n    const head = headRef.current;\n    const body = bodyRef.current;\n    return scroller && head && body ? { scroller, head, body } : null;\n  }\n\n  // Once, on mount: paging keeps the scroll position, so the same hours stay under the eye.\n  useLayoutEffect(() => {\n    const scroller = scrollerRef.current;\n    const body = bodyRef.current;\n    if (!scroller || !body) return;\n    const hour = body.offsetHeight / 24;\n    if (!hour) return;\n    const todayShown = columns.some((day) => isSameDay(day, now));\n    const target =\n      scrollToHour ??\n      (todayShown ? Math.max(0, minutesOfDay(now) / 60 - 1) : (workingHours?.[0] ?? DEFAULT_SCROLL_HOUR));\n    scroller.scrollTop = clamp(target, 0, 24) * hour;\n    // oxlint-disable-next-line react-hooks/exhaustive-deps\n  }, []);\n\n  useEffect(() => {\n    const scroller = scrollerRef.current;\n    if (!scroller) return;\n    // Trackpad pinches arrive as a wheel with Ctrl held (Chromium, Firefox); Safari sends\n    // `gesturechange` instead, and there the trackpad zooms the page as usual.\n    function onWheel(event: WheelEvent) {\n      if (!event.ctrlKey) return;\n      const elements = zoomElements();\n      if (!elements) return;\n      event.preventDefault();\n      const rect = elements.body.getBoundingClientRect();\n      const hour = rect.height / 24;\n      if (!hour) return;\n      zoomTo(\n        elements,\n        zoomRef,\n        hour * Math.exp(-event.deltaY * 0.01),\n        (event.clientY - rect.top) / hour,\n        event.clientY,\n      );\n    }\n    scroller.addEventListener('wheel', onWheel, { passive: false });\n    return () => scroller.removeEventListener('wheel', onWheel);\n    // zoomElements only reads refs.\n    // oxlint-disable-next-line react-hooks/exhaustive-deps\n  }, [zoomRef]);\n\n  /** Days from the period's first day, hidden ones included, to the column under `x` in `rect`. */\n  function dayAt(x: number, rect: DOMRect): number {\n    const column = clamp(Math.floor(((x - rect.left) / rect.width) * columns.length), 0, columns.length - 1);\n    return dayDiff(rangeStart, columns[column]);\n  }\n\n  /** `day` counts from the period's first day, hidden ones included, so it adds up to a date. */\n  function pointAt(point: GesturePoint): { day: number; minutes: number } | null {\n    const pane = currentRef.current;\n    if (!pane) return null;\n    const rect = pane.getBoundingClientRect();\n    if (!rect.width || !rect.height) return null;\n    return {\n      day: dayAt(point.x, rect),\n      minutes: clamp(((point.y - rect.top) / rect.height) * MINUTES_PER_DAY, 0, MINUTES_PER_DAY),\n    };\n  }\n\n  function allDayAt(point: GesturePoint): number | null {\n    const rect = allDayRef.current?.getBoundingClientRect();\n    return rect?.width ? dayAt(point.x, rect) : null;\n  }\n\n  /** Whole days: the event moved by as many as the pointer has, or the days a new slot covers. */\n  function allDayPreview({ event, grabbed }: AllDayDrag<E>, over: number): { start: Date; end: Date } {\n    if (event) return moveByDays(event, over - grabbed);\n    const { start, end } = allDaySlot(addDays(rangeStart, grabbed), addDays(rangeStart, over));\n    return { start, end };\n  }\n\n  /** Wall-clock minutes from the first shown midnight: negative before it. */\n  function minutesFromStart(value: Date): number {\n    return dayDiff(rangeStart, value) * MINUTES_PER_DAY + minutesOfDay(value);\n  }\n\n  function preview(current: TimeDrag<E>, point: GesturePoint): { start: Date; end: Date } | null {\n    if (current.kind === 'allDay') {\n      const over = allDayAt(point);\n      return over === null ? null : allDayPreview(current, over);\n    }\n    const at = pointAt(point);\n    if (!at) return null;\n    const limit = periodDays * MINUTES_PER_DAY;\n\n    if (current.kind === 'create') {\n      const anchor = Math.floor(current.anchor / slotMinutes) * slotMinutes;\n      const [from, to] =\n        at.minutes >= anchor\n          ? [anchor, Math.max(Math.ceil(at.minutes / slotMinutes) * slotMinutes, anchor + slotMinutes)]\n          : [Math.floor(at.minutes / slotMinutes) * slotMinutes, anchor + slotMinutes];\n      const day = addDays(rangeStart, current.day);\n      const start = atMinutes(day, from);\n      return { start, end: endAfter(start, atMinutes(day, Math.min(to, MINUTES_PER_DAY)), to - from) };\n    }\n\n    const pointer = at.day * MINUTES_PER_DAY + at.minutes;\n    const eventStart = minutesFromStart(current.event.start);\n    if (current.kind === 'move') {\n      const from = clamp(snap(pointer - current.grab, slotMinutes), Math.min(eventStart, 0), limit - slotMinutes);\n      const start = atMinutes(rangeStart, from);\n      // The real length, not the wall-clock one, so a move across a clock change keeps it.\n      const length = eventEnd(current.event).getTime() - current.event.start.getTime();\n      return { start, end: new Date(start.getTime() + length) };\n    }\n    const to = clamp(snap(pointer, slotMinutes), eventStart + slotMinutes, Math.max(limit, eventStart + slotMinutes));\n    const start = current.event.start;\n    return { start, end: endAfter(start, atMinutes(rangeStart, to), to - eventStart) };\n  }\n\n  function setDragState(next: TimeDrag<E> | null) {\n    dragRef.current = next;\n    setDrag(next);\n  }\n\n  function findEvent(element: HTMLElement): E | undefined {\n    return events.find((candidate) => String(candidate.id) === element.dataset.calEvent);\n  }\n\n  useGestures<TimeDrag<E>>(scrollerRef, {\n    onPress: pager.finish,\n    canSwipe: () => true,\n    onSwipeMove: pager.move,\n    onSwipeEnd: (dx, velocity) => pager.settle(pageStep(dx, pager.width(), velocity)),\n    dragFrom: (target, point) => {\n      if (allDayRef.current?.contains(target)) return allDayDragFrom(target, point);\n      if (!currentRef.current?.contains(target)) return null;\n      const at = pointAt(point);\n      if (!at) return null;\n      const element = target.closest<HTMLElement>('[data-cal-event]');\n      if (element) {\n        const event = onEventChange ? findEvent(element) : undefined;\n        if (!event || event.readOnly) return null;\n        const current = { start: event.start, end: eventEnd(event) };\n        if (target.closest('[data-cal-resize]')) return { kind: 'resize', event, ...current };\n        const grab = at.day * MINUTES_PER_DAY + at.minutes - minutesFromStart(event.start);\n        return { kind: 'move', event, grab, ...current };\n      }\n      if (!onSlotCreate) return null;\n      const day = addDays(rangeStart, at.day);\n      return { kind: 'create', day: at.day, anchor: at.minutes, start: day, end: day };\n    },\n    onDragStart: (started, point) => {\n      snapshot.current = events;\n      setDragState({ ...started, ...preview(started, point) });\n    },\n    onDragMove: (point) => {\n      const current = dragRef.current;\n      const next = current && preview(current, point);\n      if (!current || !next) return;\n      if (next.start.getTime() === current.start.getTime() && next.end.getTime() === current.end.getTime()) return;\n      setDragState({ ...current, ...next });\n    },\n    onDragEnd: (point) => {\n      const current = dragRef.current;\n      setDragState(null);\n      if (!current) return;\n      const { start, end } = preview(current, point) ?? current;\n      if (current.kind === 'create') {\n        onSlotCreate?.({ start, end, allDay: false }, markSlot(current.day, start, end));\n        return;\n      }\n      const { event } = current;\n      if (!event) {\n        onSlotCreate?.({ start, end, allDay: true }, dayHeader(start));\n        return;\n      }\n      const unchanged = start.getTime() === event.start.getTime() && end.getTime() === eventEnd(event).getTime();\n      if (!unchanged) onEventChange?.({ event, start, end });\n    },\n    onDragCancel: () => setDragState(null),\n    onPinchStart: (center, distance) => {\n      const body = bodyRef.current;\n      if (!body) return;\n      const rect = body.getBoundingClientRect();\n      const hour = rect.height / 24;\n      if (!hour) return;\n      pinchRef.current = { hour, anchorHour: (center.y - rect.top) / hour, distance };\n    },\n    onPinchMove: (center, distance) => {\n      const pinch = pinchRef.current;\n      const elements = zoomElements();\n      if (!pinch || !elements || !pinch.distance) return;\n      zoomTo(elements, zoomRef, (pinch.hour * distance) / pinch.distance, pinch.anchorHour, center.y);\n    },\n    onPinchEnd: () => {\n      pinchRef.current = null;\n    },\n    autoScroll: () => {\n      const scroller = scrollerRef.current;\n      const head = headRef.current;\n      // A drag in the all-day row is above the hours, where the zone would scroll them up.\n      if (!scroller || !head || dragRef.current?.kind === 'allDay') return null;\n      const rect = scroller.getBoundingClientRect();\n      // The sticky head covers the top of the scroller, so the zone starts below it.\n      return { element: scroller, top: rect.top + head.offsetHeight, bottom: rect.bottom };\n    },\n  });\n\n  function allDayDragFrom(target: Element, point: GesturePoint): AllDayDrag<E> | null {\n    const grabbed = allDayAt(point);\n    if (grabbed === null || target.closest('[data-cal-more]')) return null;\n    const element = target.closest<HTMLElement>('[data-cal-event]');\n    if (element) {\n      const event = onEventChange ? findEvent(element) : undefined;\n      if (!event || event.readOnly) return null;\n      return { kind: 'allDay', event, grabbed, start: event.start, end: eventEnd(event) };\n    }\n    if (!onSlotCreate) return null;\n    const { start, end } = allDaySlot(addDays(rangeStart, grabbed));\n    return { kind: 'allDay', event: null, grabbed, start, end };\n  }\n\n  /** The header of `day`'s column: what a new all-day slot is anchored to. */\n  function dayHeader(day: Date): HTMLElement {\n    const header = headRef.current?.querySelector<HTMLElement>(`[data-cal-head-day=\"${dateToDay(day)}\"]`);\n    return header ?? scrollerRef.current!;\n  }\n\n  function handleAllDayClick(clickEvent: MouseEvent<HTMLDivElement>) {\n    if (!onSlotCreate || isDismissClick(clickEvent.nativeEvent)) return;\n    if ((clickEvent.target as Element).closest('[data-cal-event], [data-cal-more]')) return;\n    const rect = clickEvent.currentTarget.getBoundingClientRect();\n    if (!rect.width) return;\n    const day = addDays(rangeStart, dayAt(clickEvent.clientX, rect));\n    onSlotCreate(allDaySlot(day), dayHeader(day));\n  }\n\n  function handleColumnClick(clickEvent: MouseEvent<HTMLDivElement>, day: Date) {\n    if (!onSlotCreate || isDismissClick(clickEvent.nativeEvent)) return;\n    if ((clickEvent.target as Element).closest('[data-cal-event]')) return;\n    const rect = clickEvent.currentTarget.getBoundingClientRect();\n    if (!rect.height) return;\n    const minutes = ((clickEvent.clientY - rect.top) / rect.height) * MINUTES_PER_DAY;\n    const from = clamp(Math.floor(minutes / slotMinutes) * slotMinutes, 0, MINUTES_PER_DAY - slotMinutes);\n    const start = atMinutes(day, from);\n    // Real time, not the clock: across a clock change an hour on the clock is none or two.\n    const end = new Date(start.getTime() + Math.max(60, slotMinutes) * 60_000);\n    const midnight = atMinutes(day, MINUTES_PER_DAY);\n    const slot = { start, end: end < midnight ? end : midnight, allDay: false };\n    onSlotCreate(slot, markSlot(dayDiff(rangeStart, day), slot.start, slot.end));\n  }\n\n  /**\n   * Moves the invisible box over a new slot and returns it: the anchor `onSlotCreate` hands out,\n   * so the app can open its editor right next to the slot, as `onEventClick` lets it for an event.\n   */\n  function markSlot(dayIndex: number, start: Date, end: Date): HTMLElement {\n    const marker = slotRef.current;\n    if (!marker) return scrollerRef.current!;\n    const next = addDays(rangeStart, dayIndex + 1);\n    const top = minutesOfDay(start);\n    const bottom = end >= next ? MINUTES_PER_DAY : minutesOfDay(end);\n    const column = Math.max(\n      columns.findIndex((day) => dayDiff(rangeStart, day) === dayIndex),\n      0,\n    );\n    marker.style.left = `${(column / columns.length) * 100}%`;\n    marker.style.width = `${100 / columns.length}%`;\n    marker.style.top = `${(top / MINUTES_PER_DAY) * 100}%`;\n    // Never upside down: on the day the clocks go back, a slot can end on an earlier clock time.\n    marker.style.height = `${(Math.max(bottom - top, 0) / MINUTES_PER_DAY) * 100}%`;\n    return marker;\n  }\n\n  function renderAllDayBar(bar: LaneBar<E>) {\n    const { event } = bar;\n    return (\n      <button\n        key={event.id}\n        type='button'\n        data-cal-event={String(event.id)}\n        className={cx(\n          styles.timeGridBar,\n          bar.continuesBefore && styles.timeGridBarContinuesBefore,\n          bar.continuesAfter && styles.timeGridBarContinuesAfter,\n          drag?.kind === 'allDay' && drag.event === event && styles.timeGridBarDragged,\n          classNames?.event,\n          eventClassName?.(event),\n        )}\n        style={\n          {\n            '--event-color': event.color && cssColor(event.color),\n            gridColumn: `${bar.start + 1} / span ${bar.span}`,\n            gridRow: bar.lane + 1,\n            ...slotStyles?.event,\n          } as CSSProperties\n        }\n        aria-label={`${event.title}, ${text.allDay}`}\n        onClick={(clickEvent) => onEventClick?.(event, clickEvent.currentTarget)}\n      >\n        <span className={styles.timeGridBarTitle} aria-hidden>\n          {renderEvent ? renderEvent(event, { singleLine: true }) : event.title}\n        </span>\n      </button>\n    );\n  }\n\n  function renderBox(box: TimedBox<E>) {\n    const { event } = box;\n    const minutes = box.bottom - box.top;\n    const short = minutes < SHORT_EVENT_MINUTES;\n    const when = formatTimeRange(event.start, eventEnd(event), locale);\n    const resizable = Boolean(onEventChange) && !event.readOnly && !box.continuesAfter;\n    const dragged = drag && drag.kind !== 'create' && drag.event === event;\n    return (\n      <button\n        key={event.id}\n        type='button'\n        data-cal-event={String(event.id)}\n        aria-label={`${event.title}, ${when}`}\n        className={cx(\n          styles.timeGridEvent,\n          short && styles.timeGridEventShort,\n          minutes < BRIEF_EVENT_MINUTES && styles.timeGridEventBrief,\n          box.continuesBefore && styles.timeGridEventContinuesBefore,\n          box.continuesAfter && styles.timeGridEventContinuesAfter,\n          (onEventClick || resizable) && styles.timeGridEventInteractive,\n          dragged && styles.timeGridEventDragged,\n          classNames?.event,\n          eventClassName?.(event),\n        )}\n        style={\n          {\n            '--event-color': event.color && cssColor(event.color),\n            top: `${(box.top / MINUTES_PER_DAY) * 100}%`,\n            '--span': `${(minutes / MINUTES_PER_DAY) * 100}%`,\n            left: `calc(var(--column-width) * ${box.column / box.columns})`,\n            width: `calc(var(--column-width) * ${box.span / box.columns})`,\n            ...slotStyles?.event,\n          } as CSSProperties\n        }\n        onClick={(clickEvent) => onEventClick?.(event, clickEvent.currentTarget)}\n      >\n        <span className={styles.timeGridEventTitle} aria-hidden>\n          {renderEvent ? renderEvent(event, { singleLine: short }) : event.title}\n        </span>\n        <span className={styles.timeGridEventTime} aria-hidden>\n          {formatClockRange(event.start, eventEnd(event), locale)}\n        </span>\n        {resizable && <span data-cal-resize className={styles.timeGridResize} aria-hidden />}\n      </button>\n    );\n  }\n\n  function renderPreview(day: Date) {\n    if (!drag || drag.kind === 'allDay') return null;\n    const dayStart = day;\n    const next = addDays(day, 1);\n    if (drag.end <= dayStart && drag.start < dayStart) return null;\n    if (drag.start >= next) return null;\n    const top = drag.start < dayStart ? 0 : minutesOfDay(drag.start);\n    const bottom = drag.end >= next ? MINUTES_PER_DAY : minutesOfDay(drag.end);\n    const color = drag.kind === 'create' ? undefined : drag.event.color;\n    const title = drag.kind === 'create' ? null : drag.event.title;\n    return (\n      <div\n        className={styles.timeGridPreview}\n        style={\n          {\n            '--event-color': color && cssColor(color),\n            top: `${(top / MINUTES_PER_DAY) * 100}%`,\n            height: `${(Math.max(bottom - top, 15) / MINUTES_PER_DAY) * 100}%`,\n          } as CSSProperties\n        }\n        aria-hidden\n      >\n        {title && <span className={styles.timeGridEventTitle}>{title}</span>}\n        <span className={styles.timeGridPreviewTime}>{formatClockRange(drag.start, drag.end, locale)}</span>\n      </div>\n    );\n  }\n\n  /** Where an all-day drag would put its days, drawn over the bars. */\n  function renderAllDayPreview(dates: readonly Date[]) {\n    if (drag?.kind !== 'allDay') return null;\n    const target = { id: '', title: '', allDay: true, ...drag.event, start: drag.start, end: drag.end };\n    const [bar] = packLanes([target], dates);\n    if (!bar) return null;\n    return (\n      <div\n        className={cx(styles.timeGridBar, styles.timeGridBarPreview)}\n        style={\n          {\n            '--event-color': drag.event?.color && cssColor(drag.event.color),\n            gridColumn: `${bar.start + 1} / span ${bar.span}`,\n            gridRow: 1,\n          } as CSSProperties\n        }\n        aria-hidden\n      >\n        <span className={styles.timeGridBarTitle}>{drag.event?.title}</span>\n      </div>\n    );\n  }\n\n  const panes = STEPS.map((step) => {\n    const paneRange = periodRange(period, stepDate(period, date, step, hidden), weekStartsOn, hidden);\n    const start = paneRange.start;\n    const dates = shownDays(start, dayDiff(start, paneRange.end), hidden);\n    const allDay = packLanes(shown, dates, isAllDayLike);\n    const lanes: LimitedLanes<E> = allDayOpen\n      ? { visible: allDay, moreLane: 0, hidden: dates.map(() => 0) }\n      : limitLanes(allDay, dates.length, ALL_DAY_LANES);\n    return { step, start, dates, lanes };\n  });\n  const hasHidden = panes[1].lanes.hidden.some((count) => count > 0);\n  const work = workingHours?.map((hour) => (clamp(hour, 0, 24) / 24) * 100);\n  // Working from the evening into the morning, such as `[22, 6]`: shaded between its end and start.\n  const overnight = work !== undefined && work[0] > work[1];\n  const paneClass = (step: number) =>\n    cx(pagerStyles.pagerPane, step === -1 && pagerStyles.pagerPanePrev, step === 1 && pagerStyles.pagerPaneNext);\n\n  return (\n    <div\n      ref={scrollerRef}\n      tabIndex={-1}\n      className={styles.timeGrid}\n      style={\n        {\n          '--days': columns.length,\n          '--custom-calendar-hour-height': zoomRef.current ? `${zoomRef.current.hour}px` : undefined,\n          '--work-start': work && `${Math.min(...work)}%`,\n          '--work-end': work && `${Math.max(...work)}%`,\n        } as CSSProperties\n      }\n    >\n      <div ref={headRef} className={styles.timeGridHead}>\n        <div className={styles.timeGridCorner}>\n          {showWeekNumbers && (\n            // Numbered by the ISO week holding most of the shown days, which the middle one is in.\n            <span className={styles.timeGridWeekNumber}>\n              {text.weekNumber(weekNumber(columns[Math.floor(columns.length / 2)]))}\n            </span>\n          )}\n          {(hasHidden || allDayOpen) && (\n            <ActionIcon\n              className={cx(styles.timeGridToggle, allDayOpen && styles.timeGridToggleOpen)}\n              icon={faChevronDown}\n              title={text.allDay}\n              aria-label={text.allDay}\n              aria-expanded={allDayOpen}\n              onClick={() => setAllDayOpen((open) => !open)}\n            />\n          )}\n        </div>\n        <div className={pagerStyles.pagerViewport}>\n          <div ref={pager.trackRef} className={pagerStyles.pagerTrack}>\n            {panes.map(({ step, start, dates, lanes }) => (\n              <div\n                key={dateToDay(start)}\n                className={cx(paneClass(step), styles.timeGridHeadPane)}\n                inert={step !== 0}\n                aria-hidden={step !== 0 || undefined}\n              >\n                {dates.map((day) => {\n                  const isToday = isSameDay(day, now);\n                  const content = (\n                    <>\n                      <span className={styles.timeGridWeekday}>{formatWeekday(day, locale, 'short')}</span>\n                      <span className={cx(styles.timeGridDayNumber, isToday && styles.timeGridDayNumberToday)}>\n                        {formatDayNumber(day, locale)}\n                      </span>\n                    </>\n                  );\n                  const shared = {\n                    className: cx(styles.timeGridDayHeader, classNames?.dayHeader),\n                    style: slotStyles?.dayHeader,\n                    'aria-current': isToday ? ('date' as const) : undefined,\n                    'data-cal-head-day': dateToDay(day),\n                  };\n                  return columns.length > 1 && onOpenDay ? (\n                    <button\n                      key={dateToDay(day)}\n                      type='button'\n                      {...shared}\n                      className={cx(shared.className, styles.timeGridDayHeaderLink)}\n                      aria-label={formatLongDate(day, locale)}\n                      onClick={() => onOpenDay(day)}\n                    >\n                      {content}\n                    </button>\n                  ) : (\n                    <div key={dateToDay(day)} {...shared}>\n                      {content}\n                    </div>\n                  );\n                })}\n                <div\n                  ref={step === 0 ? allDayRef : undefined}\n                  className={cx(styles.timeGridAllDay, onSlotCreate && styles.timeGridAllDayCreatable)}\n                  aria-label={text.allDay}\n                  role='group'\n                  onClick={handleAllDayClick}\n                >\n                  {lanes.visible.map(renderAllDayBar)}\n                  {lanes.hidden.map(\n                    (count, column) =>\n                      count > 0 && (\n                        <div\n                          key={`more-${column}`}\n                          aria-hidden\n                          data-cal-more\n                          className={styles.timeGridMore}\n                          style={{ gridColumn: column + 1, gridRow: lanes.moreLane + 1 }}\n                          onClick={() => setAllDayOpen(true)}\n                        >\n                          {text.more(count)}\n                        </div>\n                      ),\n                  )}\n                  {step === 0 && renderAllDayPreview(dates)}\n                </div>\n              </div>\n            ))}\n          </div>\n        </div>\n      </div>\n\n      <div ref={bodyRef} className={styles.timeGridBody}>\n        <div className={styles.timeGridGutter} aria-hidden>\n          {HOURS.map((hour) => (\n            <span key={hour} className={styles.timeGridHour} style={{ top: `${(hour / 24) * 100}%` }}>\n              {formatHour(hour, locale)}\n            </span>\n          ))}\n        </div>\n        <div className={pagerStyles.pagerViewport}>\n          <div ref={pager.trackRef} className={pagerStyles.pagerTrack}>\n            {panes.map(({ step, start, dates }) => (\n              <div\n                key={dateToDay(start)}\n                ref={step === 0 ? currentRef : undefined}\n                className={cx(paneClass(step), styles.timeGridBodyPane)}\n                inert={step !== 0}\n                aria-hidden={step !== 0 || undefined}\n              >\n                {dates.map((day) => {\n                  const isToday = isSameDay(day, now);\n                  return (\n                    <div\n                      key={dateToDay(day)}\n                      role='group'\n                      aria-label={formatLongDate(day, locale)}\n                      data-cal-column={dateToDay(day)}\n                      className={cx(\n                        styles.timeGridColumn,\n                        isToday && styles.timeGridColumnToday,\n                        work && styles.timeGridColumnWorking,\n                        overnight && styles.timeGridColumnWorkingOvernight,\n                        onSlotCreate && styles.timeGridColumnCreatable,\n                        classNames?.cell,\n                      )}\n                      style={slotStyles?.cell}\n                      onClick={(clickEvent) => handleColumnClick(clickEvent, day)}\n                    >\n                      {packDay(shown, day).map(renderBox)}\n                      {isToday && (\n                        <div\n                          className={styles.timeGridNow}\n                          style={{ top: `${(minutesOfDay(now) / MINUTES_PER_DAY) * 100}%` }}\n                          aria-hidden\n                        />\n                      )}\n                      {step === 0 && renderPreview(day)}\n                    </div>\n                  );\n                })}\n                {step === 0 && <div ref={slotRef} className={styles.timeGridSlot} aria-hidden />}\n              </div>\n            ))}\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n}\n\nexport default TimeGrid;\n",".calendar {\n  /* Theme tokens resolved once, each with a built-in fallback. Set any of the public\n     `--calendar-*` variables through ThemeProvider (theme-wide) or `style` (per instance). */\n  --custom-calendar-hour-height: var(--calendar-hour-height, 3em);\n  --custom-calendar-today-color: var(--calendar-today-color, var(--primary-color));\n  --custom-calendar-now-color: var(--calendar-now-color, var(--error-color));\n  --custom-calendar-event-radius: var(--calendar-event-radius, 6px);\n  --custom-calendar-off-hours-color: var(--calendar-off-hours-color, var(--background-accent));\n\n  isolation: isolate;\n  box-sizing: border-box;\n  display: flex;\n  flex-direction: column;\n  height: 40em;\n  min-height: 0;\n  overflow: hidden;\n  border: 1px solid var(--border-color);\n  border-radius: var(--default-border-radius);\n  background: var(--background);\n  color: var(--text-color);\n}\n","import { useEffect, useLayoutEffect, useRef, useState, type ReactElement } from 'react';\nimport cx from 'classnames';\n\nimport { useComponentDefaults, useLabels, useLocale } from '../configProvider';\nimport { validLocale } from '../configProvider/validLocale';\nimport { mergeRefs } from '../utils/mergeRefs';\nimport { asDay, dayToDate } from '../utils/day';\nimport {\n  CalendarView,\n  type CalendarEvent,\n  type CalendarEventChange,\n  type CalendarLabels,\n  type CalendarProps,\n} from './calendar.types';\nimport {\n  dayDiff,\n  dateToDay,\n  addDays,\n  hiddenDaySet,\n  NARROW_WEEK,\n  newEventSlot,\n  periodRange,\n  rangeWithNeighbours,\n  shownDay,\n  shownDays,\n  startOfDay,\n  stepDate,\n  type CalendarPeriod,\n} from './dates';\nimport { formatPeriodTitle } from './format';\nimport { localeWeekStart } from '../utils/weekStart';\nimport { useToday } from './useNow';\nimport AgendaList from './components/agendaList';\nimport CalendarHeader from './components/calendarHeader';\nimport CompactMonth from './components/compactMonth';\nimport MonthView from './components/monthView';\nimport TimeGrid from './components/timeGrid';\nimport type { TimeGridZoom } from './components/timeGrid/timeGrid.types';\nimport type { CalendarViewProps } from './view.types';\n\nimport styles from './calendar.module.pcss';\nimport { useToneCheck } from '../styles/tone';\n\nconst ALL_VIEWS = [CalendarView.Month, CalendarView.Week, CalendarView.Day, CalendarView.Agenda];\n\n/** Below this width the month view folds into the compact month with a day list, and a week shows three days. */\nconst COMPACT_WIDTH = 600;\n\nconst DEFAULT_LABELS: Required<CalendarLabels> = {\n  previous: 'Previous',\n  next: 'Next',\n  today: 'Today',\n  viewSwitcher: 'Calendar view',\n  month: 'Month',\n  week: 'Week',\n  day: 'Day',\n  agenda: 'Agenda',\n  allDay: 'All day',\n  more: (count) => `+${count} more`,\n  eventCount: (count) => (count === 1 ? '1 event' : `${count} events`),\n  noEvents: 'No events',\n  newEvent: 'New event',\n  weekNumber: (week) => `Week ${week}`,\n};\n\nfunction Calendar<E extends CalendarEvent = CalendarEvent>(props: CalendarProps<E>): ReactElement {\n  const {\n    events,\n    date: controlledDate,\n    defaultDate,\n    onDateChange,\n    view: controlledView,\n    defaultView,\n    onViewChange,\n    views = ALL_VIEWS,\n    locale: localeProp,\n    weekStartsOn: weekStartsOnProp,\n    slotMinutes = 15,\n    hiddenDays,\n    scrollToHour,\n    workingHours,\n    showWeekNumbers = false,\n    onRangeChange,\n    onEventClick,\n    onEventChange,\n    onSlotCreate,\n    renderEvent,\n    eventClassName,\n    labels,\n    ref,\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    ...rest\n  } = useComponentDefaults('Calendar', props);\n  const text = useLabels('Calendar', labels, DEFAULT_LABELS);\n  const appLocale = useLocale();\n  const locale = validLocale(localeProp, 'Calendar') ?? appLocale;\n  useToneCheck(\n    'Calendar',\n    events.map((event) => event.color),\n  );\n  const weekStartsOn = weekStartsOnProp ?? localeWeekStart(locale);\n  const hidden = hiddenDaySet(hiddenDays);\n\n  const rootRef = useRef<HTMLDivElement>(null);\n  const zoomRef = useRef<TimeGridZoom | null>(null);\n  // `null` until measured, so that the first range reported is the one the layout shows.\n  const [width, setWidth] = useState<number | null>(null);\n  const [direction, setDirection] = useState<1 | -1>(1);\n  const today = useToday();\n\n  // `date` is a day, the same wherever the calendar is opened, and read as its first local moment.\n  const [internalDate, setInternalDate] = useState(() => {\n    const initial = asDay(defaultDate);\n    return initial ? dayToDate(initial) : startOfDay(new Date());\n  });\n  const controlledDay = asDay(controlledDate);\n  // Only ever a day the views show. The selection itself is left alone: a hidden day in `date` is\n  // the parent's, and stepping on from it lands where the parent would expect.\n  const date = shownDay(controlledDay ? dayToDate(controlledDay) : internalDate, hidden);\n  const [internalView, setInternalView] = useState(() => defaultView ?? views[0] ?? CalendarView.Month);\n  const view = controlledView ?? internalView;\n\n  // The layout switch follows the calendar's own width rather than the viewport's, so it also\n  // happens in a sidebar on a wide screen. Width 0 means no layout (server, jsdom): stay wide.\n  useLayoutEffect(() => {\n    const root = rootRef.current;\n    if (!root) return;\n    // The border box, both times: an observer's `contentRect` leaves the border out, and 600px\n    // wide would then read as 598.\n    const measure = () => setWidth(root.getBoundingClientRect().width);\n    measure();\n    const observer = new ResizeObserver(measure);\n    observer.observe(root);\n    return () => observer.disconnect();\n  }, []);\n  const compact = width !== null && width > 0 && width < COMPACT_WIDTH;\n  const period: CalendarPeriod = compact && view === CalendarView.Week ? NARROW_WEEK : view;\n\n  // Reported per range, not per render: `date` is a new object on every render of a controlled\n  // parent. Not `useEffectEvent` for the latest callback, which React only has from 19.2.\n  // An effect, since controlled props move the range too; `reported` keeps StrictMode's re-run\n  // from reporting a range twice, which a consumer fetching in the callback pays for.\n  const range = rangeWithNeighbours(period, date, weekStartsOn, hidden);\n  const rangeKey = width === null ? null : `${dateToDay(range.start)}/${dateToDay(range.end)}`;\n  const latest = useRef({ range, onRangeChange });\n  const reported = useRef<string | null>(null);\n  useLayoutEffect(() => {\n    latest.current = { range, onRangeChange };\n  });\n  useEffect(() => {\n    if (rangeKey === null || reported.current === rangeKey) return;\n    reported.current = rangeKey;\n    latest.current.onRangeChange?.(latest.current.range);\n  }, [rangeKey]);\n\n  function changeDate(next: Date) {\n    const day = startOfDay(next);\n    if (dateToDay(day) === dateToDay(date)) return;\n    setDirection(day > date ? 1 : -1);\n    if (controlledDate === undefined) setInternalDate(day);\n    onDateChange?.(dateToDay(day));\n  }\n\n  function changeView(next: CalendarView) {\n    if (next === view) return;\n    if (controlledView === undefined) setInternalView(next);\n    onViewChange?.(next);\n  }\n\n  // Moves whose promise has not settled yet, drawn where they were dropped meanwhile.\n  const [pending, setPending] = useState<ReadonlyMap<string, { start: Date; end: Date }>>(() => new Map());\n  function changeEvent(change: CalendarEventChange<E>) {\n    const result = onEventChange?.(change);\n    if (typeof (result as Promise<unknown> | undefined)?.then !== 'function') return;\n    const key = String(change.event.id);\n    const entry = { start: change.start, end: change.end };\n    setPending((current) => new Map(current).set(key, entry));\n    // A later move of the same event owns the entry by then, and keeps it.\n    const settle = () =>\n      setPending((current) => {\n        if (current.get(key) !== entry) return current;\n        const next = new Map(current);\n        next.delete(key);\n        return next;\n      });\n    (result as Promise<unknown>).then(settle, settle);\n  }\n  const shownEvents =\n    pending.size === 0\n      ? events\n      : events.map((event) => {\n          const moved = pending.get(String(event.id));\n          return moved ? { ...event, ...moved } : event;\n        });\n\n  const viewProps: CalendarViewProps<E> = {\n    events: shownEvents,\n    date,\n    period,\n    today,\n    locale,\n    weekStartsOn,\n    slotMinutes,\n    hidden,\n    scrollToHour,\n    workingHours,\n    showWeekNumbers,\n    text,\n    onDateChange: changeDate,\n    onOpenDay: views.includes(CalendarView.Day)\n      ? (day) => {\n          changeDate(day);\n          changeView(CalendarView.Day);\n        }\n      : undefined,\n    onEventClick,\n    onEventChange: onEventChange && changeEvent,\n    onSlotCreate,\n    renderEvent,\n    eventClassName,\n    classNames,\n    slotStyles,\n  };\n\n  // The week's title names the days it shows, not the ones it hides at either end.\n  const shown = periodRange(period, date, weekStartsOn, hidden);\n  const columns = shownDays(shown.start, dayDiff(shown.start, shown.end), hidden);\n  const titleRange = { start: columns[0], end: addDays(columns[columns.length - 1], 1) };\n\n  function renderView() {\n    switch (view) {\n      case CalendarView.Week:\n      case CalendarView.Day:\n        // Remounted across the narrow width: it decides where to open the hours on mount, from the\n        // days it shows, and the first render, before the width is measured, is the wide one.\n        return <TimeGrid key={compact ? 'narrow' : 'wide'} {...viewProps} zoomRef={zoomRef} />;\n      case CalendarView.Agenda: {\n        return (\n          <AgendaList\n            events={shownEvents}\n            start={shown.start}\n            days={dayDiff(shown.start, shown.end)}\n            hidden={hidden}\n            scrollTo={date}\n            showDayHeaders\n            locale={locale}\n            today={today}\n            text={text}\n            onEventClick={onEventClick}\n            renderEvent={renderEvent}\n            eventClassName={eventClassName}\n            classNames={classNames}\n            slotStyles={slotStyles}\n          />\n        );\n      }\n      default:\n        return compact ? <CompactMonth {...viewProps} /> : <MonthView {...viewProps} />;\n    }\n  }\n\n  return (\n    <div\n      {...rest}\n      ref={mergeRefs(rootRef, ref)}\n      className={cx(styles.calendar, className, classNames?.root)}\n      style={{ ...style, ...slotStyles?.root }}\n    >\n      <CalendarHeader\n        className={classNames?.header}\n        style={slotStyles?.header}\n        compact={compact}\n        title={formatPeriodTitle(period, date, titleRange, locale)}\n        direction={direction}\n        view={view}\n        views={views}\n        text={text}\n        onStep={(step) => changeDate(stepDate(period, date, step, hidden))}\n        onToday={() => changeDate(new Date())}\n        onViewChange={changeView}\n        onCreate={\n          onSlotCreate && ((anchor) => onSlotCreate(newEventSlot(period, date, new Date(), slotMinutes), anchor))\n        }\n      />\n      {renderView()}\n    </div>\n  );\n}\n\nexport default Calendar;\n","import type React from 'react';\nimport type { ReactNode } from 'react';\nimport type { ButtonProps, ButtonStyleType } from '../button/button.types';\nimport type { HtmlProps, SlotClassNames, SlotStyles } from '../types/slots.types';\nimport type { IconAction } from '../types/actions.types';\nimport type { ColorValue } from '../themeProvider/color.types';\nimport type { ImageProps } from '../image/image.types';\n\n/**\n * The card's surface. `Outlined` and `Light` mean what they do on Button, Badge and Chip; `Elevated`\n * is a card's own. There is no `Filled`: body text on a solid colour reads badly.\n */\nexport enum CardStyleType {\n  /** A raised surface with a shadow, always neutral. The default. */\n  Elevated = 'elevated',\n  /** A border in the colour, no fill. */\n  Outlined = 'outlined',\n  /** A flat tint of the colour, no border. */\n  Light = 'light',\n}\n\n// `tabIndex` is omitted only because this component's version also accepts `null`.\nexport type CardSlots = 'root' | 'content';\n\nexport interface CardProps extends Omit<HtmlProps, 'tabIndex'> {\n  className?: string | undefined;\n  children?: ReactNode;\n  styleType?: CardStyleType;\n  /**\n   * The border of an `Outlined` card and the tint of a `Light` one: a `ThemeColor` (default\n   * `Neutral`) or any CSS colour. An `Elevated` card stays neutral.\n   */\n  color?: ColorValue;\n  onClick?: () => void;\n  tabIndex?: number | null;\n  hoverEffect?: boolean;\n  style?: React.CSSProperties;\n  autoGap?: boolean;\n  clip?: boolean;\n  /** Ref to the root element. */\n  ref?: React.Ref<HTMLDivElement>;\n  /** Classes for the inner elements. See `CardSlots`. */\n  classNames?: SlotClassNames<CardSlots>;\n  /** Inline styles for the inner elements. See `CardSlots`. */\n  styles?: SlotStyles<CardSlots>;\n}\n\nexport type CardHeaderAction = IconAction;\n\nexport interface CardHeaderProps {\n  className?: string;\n  title?: ReactNode;\n  subTitle?: ReactNode;\n  /** URL string → rendered as background-image. Any other ReactNode → rendered inside the thumbnail div. */\n  thumbnail?: string | ReactNode;\n  actions?: CardHeaderAction[] | ReactNode;\n}\n\n/** An `Image`, bled to the card's edges: a skeleton while it loads, a fallback if it fails. */\nexport interface CardMediaProps extends Omit<ImageProps, 'src' | 'alt'> {\n  src?: string;\n  alt?: string;\n}\n\nexport interface CardContentProps {\n  children?: ReactNode;\n}\n\nexport interface CardAction {\n  id?: string;\n  label?: string;\n  styleType?: ButtonStyleType;\n  color?: ButtonProps['color'];\n  onClick?: () => void;\n}\n\nexport interface CardActionsProps {\n  className?: string;\n  /** Renders a Button for each action object. Pass `children` for custom content instead. */\n  actions?: CardAction[];\n  children?: ReactNode;\n}\n",".card {\n  border-radius: var(--default-border-radius);\n  color: var(--text-color);\n  border: 1px solid transparent;\n  box-sizing: border-box;\n\n  &-clip {\n    overflow: hidden;\n  }\n\n  &-padding {\n    padding: 1em;\n  }\n\n  &-gaps {\n    display: flex;\n    flex-direction: column;\n    gap: var(--spacing-s);\n  }\n\n  &-style {\n    &-elevated {\n      background: var(--background-card-elevated, var(--background-accent-light));\n      box-shadow: var(--card-shadow);\n    }\n\n    &-outlined {\n      background: var(--background-card-outlined, transparent);\n      border-color: var(--tone-outline);\n    }\n\n    &-light {\n      background: var(--tone-light);\n    }\n  }\n\n  /* A theme may give the Neutral card a surface of its own; any other colour's tint is the point. */\n  &-neutral&-style-light {\n    background: var(--background-card-filled, var(--tone-light));\n  }\n\n  &-interactable {\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n  }\n\n  &-hover {\n    position: relative;\n    transition:\n      transform 0.15s ease,\n      box-shadow 0.15s ease,\n      filter 0.3s ease;\n\n    /* A layer over the card's own surface, so a coloured card keeps its colour. Opacity, because\n       Chromium does not interpolate a currentcolor mix. Paints under the later Ripple wrapper. */\n    &::before {\n      content: '';\n      position: absolute;\n      inset: 0;\n      border-radius: inherit;\n      background: currentcolor;\n      opacity: 0;\n      transition: opacity 0.15s ease;\n      pointer-events: none;\n    }\n\n    &:hover {\n      transform: translateY(-2px);\n\n      &::before {\n        opacity: 0.05;\n      }\n    }\n  }\n}\n\n.cardHeader {\n  display: flex;\n  gap: 1em;\n  align-items: center;\n\n  &-thumbnail {\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    width: 2.5em;\n    height: 2.5em;\n    background: rgb(0 0 0 / 8%);\n    border-radius: 50%;\n    overflow: hidden;\n    background-size: cover;\n    background-position: center;\n    flex-shrink: 0;\n  }\n\n  &-actions {\n    display: flex;\n    gap: var(--spacing-s);\n    flex-wrap: wrap;\n    flex-shrink: 1;\n    align-items: center;\n    justify-content: flex-end;\n  }\n\n  &-titles {\n    display: flex;\n    flex-flow: column;\n    flex: 1;\n    gap: 0.25em;\n    overflow: hidden;\n\n    &-main {\n      font-size: 1.125em;\n      font-weight: 600;\n      min-width: 3ch;\n      flex-shrink: 100000;\n      overflow: hidden;\n      text-overflow: ellipsis;\n\n      &-container {\n        display: flex;\n        align-items: center;\n        gap: var(--spacing-m);\n        justify-content: space-between;\n      }\n    }\n\n    &-sub {\n      font-weight: 300;\n      overflow: hidden;\n      text-overflow: ellipsis;\n    }\n  }\n}\n\n/* Pulled out to the card's own edges, so these must stay twice and once .card-padding above. */\n.cardMedia {\n  width: calc(100% + 2em);\n\n  /* With `width` and `height` given, the stretched width keeps their aspect ratio, and the skeleton\n     takes the same shape. */\n  height: auto;\n  margin: 0 -1em;\n}\n\n.cardActions {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 0.5em;\n}\n\n/* The lift on hover is the only motion here; the shadow and border it transitions alongside stay. */\n@media (prefers-reduced-motion: reduce) {\n  .card-hover:hover {\n    transform: none;\n  }\n}\n","import cx from 'classnames';\nimport React, { useRef } from 'react';\n\nimport { CardStyleType, type CardProps } from './card.types';\nimport styles from './card.module.pcss';\nimport InteractableDiv from '../interactableDiv';\nimport Ripple from '../ripple';\nimport type { RippleHandle } from '../ripple';\nimport { useComponentDefaults } from '../configProvider';\nimport { isSelfActivation } from '../utils/activationKey';\nimport { ThemeColor } from '../themeProvider/color.types';\nimport { toneProps, useToneCheck } from '../styles/tone';\n\nconst CLASS_BY_STYLE: Record<CardStyleType, string> = {\n  elevated: styles.cardStyleElevated,\n  outlined: styles.cardStyleOutlined,\n  light: styles.cardStyleLight,\n};\n\nfunction Card(props: CardProps) {\n  const {\n    className = undefined,\n    children = null,\n    styleType = CardStyleType.Elevated,\n    color = ThemeColor.Neutral,\n    onClick = undefined,\n    tabIndex = null,\n    hoverEffect = undefined,\n    style = undefined,\n    autoGap = true,\n    clip = false,\n    onKeyDown: onKeyDownProp,\n    classNames,\n    styles: slotStyles,\n    ...rest\n  } = useComponentDefaults('Card', props);\n  const isInteractable = !!onClick;\n  const tone = toneProps(color);\n  useToneCheck('Card', [color]);\n\n  const rippleRef = useRef<RippleHandle>(null);\n\n  // Keyboard activation calls onClick directly without dispatching a DOM click, so Ripple's own\n  // listener never sees it.\n  function handleKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {\n    if (isInteractable && isSelfActivation(event)) rippleRef.current?.addRipple();\n    onKeyDownProp?.(event);\n  }\n\n  return (\n    <InteractableDiv\n      {...rest}\n      className={cx(\n        styles.card,\n        tone.className,\n        color === ThemeColor.Neutral && styles.cardNeutral,\n        CLASS_BY_STYLE[styleType],\n        clip && styles.cardClip,\n        isInteractable && styles.cardInteractable,\n        ((isInteractable && hoverEffect !== false) || hoverEffect) && styles.cardHover,\n        className,\n        classNames?.root,\n      )}\n      onClick={onClick}\n      onKeyDown={handleKeyDown}\n      tabIndex={tabIndex ?? (isInteractable ? 0 : null)}\n      style={{ ...tone.style, ...style, ...slotStyles?.root }}\n    >\n      <Ripple\n        ref={rippleRef}\n        disabled={!isInteractable}\n        className={cx(styles.cardPadding, autoGap && styles.cardGaps, classNames?.content)}\n        style={{ borderRadius: 'inherit', ...slotStyles?.content }}\n      >\n        {children}\n      </Ripple>\n    </InteractableDiv>\n  );\n}\n\nexport default Card;\n","import React from 'react';\nimport cx from 'classnames';\n\nimport type { CardHeaderProps } from './card.types';\nimport styles from './card.module.pcss';\nimport ActionIcon from '../actionIcon';\n\nfunction CardHeader({\n  className = '',\n  title = '',\n  subTitle = '',\n  thumbnail = '',\n  actions = undefined,\n}: CardHeaderProps) {\n  const thumbnailUrl = typeof thumbnail === 'string' && thumbnail.startsWith('http') ? thumbnail : undefined;\n\n  return (\n    <div className={cx(styles.cardHeader, className)}>\n      {thumbnail && (\n        <div\n          className={styles.cardHeaderThumbnail}\n          style={thumbnailUrl ? { backgroundImage: `url(${thumbnailUrl})` } : {}}\n        >\n          {!thumbnailUrl && thumbnail}\n        </div>\n      )}\n      <div className={styles.cardHeaderTitles}>\n        <div className={styles.cardHeaderTitlesMainContainer}>\n          <div className={styles.cardHeaderTitlesMain}>{title}</div>\n          <div className={styles.cardHeaderActions}>\n            {Array.isArray(actions)\n              ? actions.map((action, index) => (\n                  <ActionIcon\n                    key={index}\n                    title={action.title}\n                    onClick={\n                      action.onClick\n                        ? (e: React.MouseEvent<HTMLButtonElement>) => {\n                            e.stopPropagation();\n                            action.onClick?.(e);\n                          }\n                        : undefined\n                    }\n                    icon={action.icon}\n                  />\n                ))\n              : actions}\n          </div>\n        </div>\n        <div className={styles.cardHeaderTitlesSub}>{subTitle}</div>\n      </div>\n    </div>\n  );\n}\n\nexport default CardHeader;\n","import type { CSSProperties } from 'react';\nimport type { SlotClassNames, SlotStyles, HtmlProps } from '../types/slots.types';\n\nexport enum SkeletonVariant {\n  Text = 'text',\n  Circle = 'circle',\n  Rectangular = 'rectangular',\n}\n\nexport enum SkeletonAnimation {\n  Shimmer = 'shimmer',\n  Pulse = 'pulse',\n  None = 'none',\n}\n\nexport type SkeletonSlots = 'line';\n\nexport interface SkeletonProps extends HtmlProps {\n  variant?: SkeletonVariant;\n  animation?: SkeletonAnimation;\n  lines?: number;\n  className?: string;\n  style?: CSSProperties;\n  classNames?: SlotClassNames<SkeletonSlots>;\n  styles?: SlotStyles<SkeletonSlots>;\n}\n",".skeleton {\n  display: block;\n  background-color: var(--background-accent);\n  border-radius: var(--default-border-radius);\n\n  &-lines {\n    display: flex;\n    flex-direction: column;\n    gap: 0.5em;\n  }\n\n  &-line-last {\n    width: 60%;\n  }\n\n  &-text {\n    width: 100%;\n    height: 1em;\n  }\n\n  &-circle {\n    border-radius: 50%;\n  }\n\n  &-rectangular {\n    border-radius: var(--default-border-radius);\n  }\n\n  &-animation {\n    &-pulse {\n      animation: skeletonPulse 1.5s ease-in-out infinite;\n    }\n\n    &-shimmer {\n      background-image: linear-gradient(\n        90deg,\n        var(--background-accent) 25%,\n        var(--background-accent-light) 50%,\n        var(--background-accent) 75%\n      );\n      background-size: 200% 100%;\n      animation: skeletonShimmer 1.5s ease-in-out infinite;\n    }\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    &-animation-pulse,\n    &-animation-shimmer {\n      animation: none;\n    }\n  }\n}\n\n@keyframes skeletonPulse {\n  0%,\n  100% {\n    opacity: 1;\n  }\n\n  50% {\n    opacity: 0.4;\n  }\n}\n\n@keyframes skeletonShimmer {\n  0% {\n    background-position: 200% 0;\n  }\n\n  100% {\n    background-position: -200% 0;\n  }\n}\n","import type { ReactElement } from 'react';\nimport cx from 'classnames';\n\nimport { SkeletonVariant, SkeletonAnimation } from './skeleton.types';\nimport type { SkeletonProps } from './skeleton.types';\nimport styles from './skeleton.module.pcss';\nimport { useComponentDefaults } from '../configProvider';\n\nconst CLASS_BY_VARIANT: Record<SkeletonVariant, string> = {\n  [SkeletonVariant.Text]: styles.skeletonText,\n  [SkeletonVariant.Circle]: styles.skeletonCircle,\n  [SkeletonVariant.Rectangular]: styles.skeletonRectangular,\n};\n\nconst CLASS_BY_ANIMATION: Record<SkeletonAnimation, string | undefined> = {\n  [SkeletonAnimation.Shimmer]: styles.skeletonAnimationShimmer,\n  [SkeletonAnimation.Pulse]: styles.skeletonAnimationPulse,\n  [SkeletonAnimation.None]: undefined,\n};\n\nfunction Skeleton(props: SkeletonProps): ReactElement {\n  const {\n    variant = SkeletonVariant.Text,\n    animation = SkeletonAnimation.Shimmer,\n    lines = 1,\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    ...rest\n  } = useComponentDefaults('Skeleton', props);\n  const variantClass = CLASS_BY_VARIANT[variant];\n  const animationClass = CLASS_BY_ANIMATION[animation];\n\n  if (variant !== SkeletonVariant.Text || lines <= 1) {\n    return <span {...rest} className={cx(styles.skeleton, variantClass, animationClass, className)} style={style} />;\n  }\n\n  return (\n    <span {...rest} className={cx(styles.skeletonLines, className)} style={style}>\n      {Array.from({ length: lines }, (_, index) => (\n        <span\n          key={index}\n          className={cx(\n            styles.skeleton,\n            variantClass,\n            animationClass,\n            index === lines - 1 && styles.skeletonLineLast,\n            classNames?.line,\n          )}\n          style={slotStyles?.line}\n        />\n      ))}\n    </span>\n  );\n}\n\nexport default Skeleton;\n","/* Zero specificity, so a class that sizes the image wins here too, as CSS beats an img's attributes. */\n:where(.image-standIn) {\n  display: inline-block;\n  width: var(--custom-image-width, auto);\n  height: var(--custom-image-height, auto);\n  aspect-ratio: var(--custom-image-ratio, auto);\n  vertical-align: middle;\n}\n\n.image {\n  /* Out of the layout and of no size, so it holds no grid place and PanZoom does not measure it.\n     `!important` against the class that sizes the image. */\n  &-loading {\n    position: absolute !important;\n    width: 0 !important;\n    height: 0 !important;\n    opacity: 0;\n    pointer-events: none;\n  }\n\n  &-arrived {\n    animation: imageFadeIn 0.2s ease-out;\n  }\n\n  /* A small box of its own when nothing gives it a size, so a failed image still shows. */\n  &-fallback {\n    position: relative;\n    min-width: 2.5em;\n    min-height: 2.5em;\n    border-radius: var(--default-border-radius);\n    background: var(--background-accent);\n    color: var(--text-dark);\n  }\n\n  &-fallbackIcon {\n    position: absolute;\n    inset: 0;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    font-size: 1.5em;\n  }\n}\n\n@keyframes imageFadeIn {\n  from {\n    opacity: 0;\n  }\n}\n","import { useEffect, useLayoutEffect, useRef, useState, type CSSProperties, type ReactNode } from 'react';\nimport cx from 'classnames';\nimport { faImage } from '@fortawesome/free-solid-svg-icons';\n\nimport Skeleton from '../skeleton';\nimport { SkeletonVariant } from '../skeleton/skeleton.types';\nimport { useComponentDefaults } from '../configProvider';\nimport { mergeRefs } from '../utils/mergeRefs';\nimport { renderIcon } from '../utils/renderIcon';\nimport type { ImageProps } from './image.types';\nimport styles from './image.module.pcss';\n\n/** `cached` was complete before the first paint, so it neither stands in nor fades. */\ntype Status = 'loading' | 'cached' | 'arrived' | 'failed';\n\n/** Never wraps the `<img>`: PanZoom fits and measures its direct children, and a wrapper would hide the image. */\nfunction Image(props: ImageProps) {\n  const {\n    src,\n    alt,\n    thumbnail,\n    placeholder,\n    fallback,\n    width,\n    height,\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    onLoad,\n    onError,\n    ref,\n    ...rest\n  } = useComponentDefaults('Image', props);\n\n  const imageRef = useRef<HTMLImageElement>(null);\n  const thumbnailRef = useRef<HTMLImageElement>(null);\n  const [status, setStatus] = useState<Status>('loading');\n  const [hasThumbnail, setHasThumbnail] = useState(false);\n  const [ownSize, setOwnSize] = useState<{ width: number; height: number } | null>(null);\n  const [statusOf, setStatusOf] = useState(`${src} ${thumbnail}`);\n  if (`${src} ${thumbnail}` !== statusOf) {\n    setStatusOf(`${src} ${thumbnail}`);\n    setStatus('loading');\n    setHasThumbnail(false);\n    setOwnSize(null);\n  }\n\n  useLayoutEffect(() => {\n    const image = imageRef.current;\n    const small = thumbnailRef.current;\n    if (image?.complete && image.naturalWidth) setStatus('cached');\n    if (small?.complete && small.naturalWidth) setHasThumbnail(true);\n  }, [src, thumbnail]);\n\n  const hasGivenSize = width !== undefined && height !== undefined;\n  const isLoading = status === 'loading';\n\n  // Polled: the browser knows the size from the first bytes but fires no event for it. Only near the\n  // screen, so a lazy image further down is not polled for nothing.\n  useEffect(() => {\n    const image = imageRef.current;\n    if (!isLoading || hasGivenSize || ownSize || !image || typeof IntersectionObserver === 'undefined') return;\n    let frame = 0;\n    const check = () => {\n      // A new source on the same element reports the old image's size until it has loaded.\n      if (image.naturalWidth && (image.currentSrc === image.src || image.srcset)) {\n        setOwnSize({ width: image.naturalWidth, height: image.naturalHeight });\n      } else {\n        frame = requestAnimationFrame(check);\n      }\n    };\n    const observer = new IntersectionObserver(\n      ([entry]) => {\n        cancelAnimationFrame(frame);\n        if (entry.isIntersecting) frame = requestAnimationFrame(check);\n      },\n      { rootMargin: '200px' },\n    );\n    observer.observe(image);\n    return () => {\n      observer.disconnect();\n      cancelAnimationFrame(frame);\n    };\n  }, [isLoading, hasGivenSize, ownSize, src]);\n\n  const size = hasGivenSize || !ownSize ? intrinsicSize(width, height) : ownSizeOf(ownSize);\n\n  if (status === 'failed') {\n    if (fallback !== undefined) return fallback;\n    return (\n      <span\n        {...(alt ? { role: 'img', 'aria-label': alt } : { 'aria-hidden': true })}\n        className={cx(styles.imageStandIn, styles.imageFallback, className, classNames?.fallback)}\n        style={{ ...size, ...style, ...slotStyles?.fallback }}\n      >\n        {/* A box of its own to centre in: the image's class sets the fallback's `display`. */}\n        <span className={styles.imageFallbackIcon}>{renderIcon(faImage)}</span>\n      </span>\n    );\n  }\n\n  const showsThumbnail = isLoading && hasThumbnail;\n  let earlyStandIn: ReactNode = null;\n  if (isLoading && !hasThumbnail) {\n    earlyStandIn =\n      placeholder !== undefined ? (\n        placeholder\n      ) : (\n        <Skeleton\n          variant={SkeletonVariant.Rectangular}\n          className={cx(styles.imageStandIn, className, classNames?.placeholder)}\n          style={{ ...size, ...style, ...slotStyles?.placeholder }}\n        />\n      );\n  }\n  const holdsBack = isLoading && (earlyStandIn !== null || !!thumbnail);\n\n  return (\n    <>\n      {earlyStandIn}\n      {isLoading && thumbnail && (\n        <img\n          ref={thumbnailRef}\n          src={thumbnail}\n          alt=''\n          width={width}\n          height={height}\n          className={cx(!showsThumbnail && styles.imageLoading, className, classNames?.placeholder)}\n          style={{ ...size, ...style, ...slotStyles?.placeholder }}\n          onLoad={() => setHasThumbnail(true)}\n        />\n      )}\n      <img\n        {...rest}\n        ref={mergeRefs(imageRef, ref)}\n        src={src}\n        alt={alt}\n        width={width}\n        height={height}\n        // For PanZoom: a new `src` on the same element reports the old size until it has loaded.\n        aria-busy={isLoading || undefined}\n        className={cx(\n          holdsBack && styles.imageLoading,\n          // Not over its own thumbnail, which already shows the picture and would flash.\n          status === 'arrived' && !hasThumbnail && styles.imageArrived,\n          className,\n        )}\n        style={style}\n        onLoad={(event) => {\n          onLoad?.(event);\n          setStatus((current) => (current === 'loading' ? 'arrived' : current));\n        }}\n        onError={(event) => {\n          onError?.(event);\n          setStatus('failed');\n        }}\n      />\n    </>\n  );\n}\n\n/** No height: under CSS that sets only a width the image keeps its proportions, which a height would override. */\nfunction ownSizeOf({ width, height }: { width: number; height: number }): CSSProperties {\n  return { '--custom-image-width': `${width}px`, '--custom-image-ratio': `${width} / ${height}` } as CSSProperties;\n}\n\nfunction intrinsicSize(width: ImageProps['width'], height: ImageProps['height']): CSSProperties {\n  const length = (value: ImageProps['width']) =>\n    value === undefined ? undefined : /^\\d+(\\.\\d+)?$/.test(String(value)) ? `${value}px` : String(value);\n  const size: Record<string, string | undefined> = {\n    '--custom-image-width': length(width),\n    '--custom-image-height': length(height),\n  };\n  if (width !== undefined && height !== undefined && !Number.isNaN(Number(width)) && !Number.isNaN(Number(height))) {\n    size['--custom-image-ratio'] = `${width} / ${height}`;\n  }\n  return size as CSSProperties;\n}\n\nexport default Image;\n","import cx from 'classnames';\n\nimport Image from '../image';\nimport type { CardMediaProps } from './card.types';\nimport styles from './card.module.pcss';\n\nfunction CardMedia({ src = '', alt = '', className, ...rest }: CardMediaProps) {\n  return <Image {...rest} src={src} alt={alt} className={cx(styles.cardMedia, className)} />;\n}\n\nexport default CardMedia;\n","import React from 'react';\n\nimport type { CardContentProps } from './card.types';\n\nfunction CardContent({ children = null }: CardContentProps) {\n  return <>{children}</>;\n}\n\nexport default CardContent;\n","import React from 'react';\nimport cx from 'classnames';\nimport Button from '../button';\n\nimport type { CardActionsProps } from './card.types';\nimport styles from './card.module.pcss';\n\nfunction CardActions({ className = '', actions = [], children = null }: CardActionsProps) {\n  return (\n    <div className={cx(styles.cardActions, className)}>\n      {actions.map(({ id, onClick, label, styleType, color }) => (\n        <div key={id ?? label}>\n          <Button onClick={onClick} styleType={styleType} color={color}>\n            {label}\n          </Button>\n        </div>\n      ))}\n      {children}\n    </div>\n  );\n}\n\nexport default CardActions;\n","import { createContext } from 'react';\n\n/**\n * Whether a slide is the one on show, and not one of the inert copies a looping Carousel renders\n * of the same element. For components built on Carousel; not exported.\n */\nexport const CarouselSlideContext = createContext(false);\n",".carousel {\n  position: relative;\n  overflow: hidden;\n  border-radius: var(--default-border-radius);\n\n  &-adaptive {\n    transition: height 0.4s ease;\n  }\n\n  /* One slide wide, with every slide in the same cell, moved along by its own position. Side by side\n     at 1/n of a track n slides wide, each width was rounded to the layout's 1/64px, and the error\n     added up to an image 15px off centre by the thousandth slide. The cell still takes the tallest\n     slide's height. */\n  &-track {\n    display: grid;\n    grid-template-columns: 100%;\n    align-items: stretch;\n    transition: transform 0.4s ease;\n    touch-action: pan-y;\n    user-select: none;\n  }\n\n  &-trackAdaptive {\n    align-items: start;\n  }\n\n  &-slide {\n    grid-area: 1 / 1;\n    box-sizing: border-box;\n  }\n\n  /* Two classes, to outrank Button's radius and ActionIcon's box whichever stylesheet loads\n     first. */\n  & &-prevButton,\n  & &-nextButton {\n    position: absolute;\n    top: 50%;\n    transform: translateY(-50%);\n    z-index: 1;\n    width: 36px;\n    height: 36px;\n    border-radius: 50%;\n    box-shadow: var(--card-shadow);\n    transition: opacity 0.15s;\n  }\n\n  &-buttonDisabled {\n    opacity: 0.35;\n    pointer-events: none;\n  }\n\n  &-prevButton {\n    left: 12px;\n  }\n\n  &-nextButton {\n    right: 12px;\n  }\n\n  /* A scroll strip rather than a row that shrinks, so the dots keep their size at any slide count.\n     The padding holds the outer dots clear of the fade until the strip has somewhere to scroll. */\n  &-dots {\n    position: absolute;\n    bottom: 4px;\n    left: 50%;\n    transform: translateX(-50%);\n    z-index: 1;\n    display: flex;\n    box-sizing: border-box;\n    width: max-content;\n    max-width: 100%;\n    padding-inline: 16px;\n    overflow-x: auto;\n    overscroll-behavior-x: contain;\n    scroll-behavior: smooth;\n\n    /* Matches the strip's own `padding-inline`, so the first and last dot start out clear of the\n       fade. The shared sheet's private length, not `--scroll-fade-length`, which would inherit. */\n    --custom-scroll-fade-length: 16px;\n\n    scrollbar-width: none;\n\n    &::-webkit-scrollbar {\n      display: none;\n    }\n\n    @media (prefers-reduced-motion: reduce) {\n      scroll-behavior: auto;\n    }\n  }\n\n  /* The dot stays 8px, its padding carries the hit area out to the 24px minimum. */\n  &-dot {\n    box-sizing: content-box;\n\n    /* The strip is width-capped, and shrinking would pull the content box, the only part\n       `background-clip` paints, down to nothing. */\n    flex-shrink: 0;\n    width: 8px;\n    height: 8px;\n    padding: 8px;\n    border-radius: 2000px;\n    background: var(--background-accent);\n    background-clip: content-box;\n    opacity: 0.6;\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n    transition:\n      opacity 0.15s,\n      background 0.15s,\n      width 0.15s;\n\n    &:hover {\n      opacity: 0.85;\n    }\n\n    &:focus-visible {\n      outline: none;\n      box-shadow: 0 0 0 2px var(--primary-lighter);\n    }\n\n    &-active {\n      background: var(--primary-color);\n      background-clip: content-box;\n      opacity: 1;\n      width: 20px;\n    }\n  }\n}\n","/*\n * The scroll-fade mask, shared by `ScrollFade` and by anything with a scroll port of its own\n * (Modal's body, Carousel's dot strip). Why a mask rather than an overlay, and what that costs:\n * `docs/ScrollFade.md`.\n */\n\n/* Registered so the two alphas can be transitioned: an unregistered custom property has no type\n   for the browser to interpolate, so the fade would snap in and out at each edge. A browser\n   without `@property` keeps them working and simply lands on the old instant switch. */\n@property --custom-scroll-fade-start {\n  syntax: '<number>';\n  inherits: false;\n  initial-value: 1;\n}\n\n@property --custom-scroll-fade-end {\n  syntax: '<number>';\n  inherits: false;\n  initial-value: 1;\n}\n\n/* `inherits: false` so a host setting a length of its own doesn't hand it to a nested\n   `ScrollFade`, and typed so a bare `0` is read as a length instead of voiding the `calc()`\n   below, which would drop the whole mask rather than shorten the fade. */\n@property --custom-scroll-fade-length {\n  syntax: '<length>';\n  inherits: false;\n  initial-value: 24px;\n}\n\n/* Zero specificity, so a host that sets its own length on the same element wins whichever order\n   the bundler emits the two modules in. The `24px` repeats the initial value above for a browser\n   without `@property`, where there is none. */\n:where(.scroll-fade) {\n  --custom-scroll-fade-length: var(--scroll-fade-length, 24px);\n}\n\n.scroll-fade {\n  --custom-scroll-fade-start: 1;\n  --custom-scroll-fade-end: 1;\n\n  /* A fade is not motion, so this stays under `prefers-reduced-motion`. */\n  transition:\n    --custom-scroll-fade-start 0.2s ease,\n    --custom-scroll-fade-end 0.2s ease;\n}\n\n/* One gradient covering the whole port with only its end stops changing, so the four states are\n   two alpha switches rather than four gradients. */\n.scroll-fade-y {\n  -webkit-mask-image: linear-gradient(\n    to bottom,\n    rgb(0 0 0 / var(--custom-scroll-fade-start)) 0,\n    rgb(0 0 0 / 100%) var(--custom-scroll-fade-length),\n    rgb(0 0 0 / 100%) calc(100% - var(--custom-scroll-fade-length)),\n    rgb(0 0 0 / var(--custom-scroll-fade-end)) 100%\n  );\n  mask-image: linear-gradient(\n    to bottom,\n    rgb(0 0 0 / var(--custom-scroll-fade-start)) 0,\n    rgb(0 0 0 / 100%) var(--custom-scroll-fade-length),\n    rgb(0 0 0 / 100%) calc(100% - var(--custom-scroll-fade-length)),\n    rgb(0 0 0 / var(--custom-scroll-fade-end)) 100%\n  );\n}\n\n.scroll-fade-x {\n  -webkit-mask-image: linear-gradient(\n    to right,\n    rgb(0 0 0 / var(--custom-scroll-fade-start)) 0,\n    rgb(0 0 0 / 100%) var(--custom-scroll-fade-length),\n    rgb(0 0 0 / 100%) calc(100% - var(--custom-scroll-fade-length)),\n    rgb(0 0 0 / var(--custom-scroll-fade-end)) 100%\n  );\n  mask-image: linear-gradient(\n    to right,\n    rgb(0 0 0 / var(--custom-scroll-fade-start)) 0,\n    rgb(0 0 0 / 100%) var(--custom-scroll-fade-length),\n    rgb(0 0 0 / 100%) calc(100% - var(--custom-scroll-fade-length)),\n    rgb(0 0 0 / var(--custom-scroll-fade-end)) 100%\n  );\n}\n\n.scroll-fade-faded-start {\n  --custom-scroll-fade-start: 0;\n}\n\n.scroll-fade-faded-end {\n  --custom-scroll-fade-end: 0;\n}\n\n/* Fading text costs contrast at exactly the edge someone may be reading. Anyone who has asked for\n   more of it gets the hard edge back instead — legibility beats the nicer boundary. */\n@media (prefers-contrast: more) {\n  .scroll-fade {\n    --custom-scroll-fade-start: 1;\n    --custom-scroll-fade-end: 1;\n  }\n}\n","import React, { useEffect, useLayoutEffect, useRef, useState } from 'react';\nimport cx from 'classnames';\nimport { faChevronLeft, faChevronRight } from '@fortawesome/free-solid-svg-icons';\n\nimport ActionIcon from '../actionIcon';\nimport InteractableDiv from '../interactableDiv';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport type { CarouselLabels, CarouselProps } from './carousel.types';\nimport { CarouselSlideContext } from './carouselSlideContext';\n\nimport styles from './carousel.module.pcss';\nimport fadeStyles from 'components/common/styles/scrollFade.module.pcss';\nimport { ButtonStyleType } from '../button/button.types';\n\n// Opaque, because the arrows sit over the slide's own image; Neutral's fill is see-through.\nconst ARROW_COLOR = 'var(--background-accent)';\n\nconst AXIS_LOCK_THRESHOLD = 10;\nconst DRAG_COMMIT_RATIO = 0.2;\n/** With `lazyRender`, further than this a jump cuts straight to the slide: sliding there would mount every slide in between. */\nconst MAX_ANIMATED_JUMP = 3;\n\nconst DEFAULT_LABELS: Required<CarouselLabels> = {\n  previous: 'Previous slide',\n  next: 'Next slide',\n  slides: 'Slides',\n  goToSlide: (slide) => `Go to slide ${slide}`,\n};\n\nfunction Carousel(props: CarouselProps) {\n  const {\n    children,\n    currentIndex,\n    defaultIndex = 0,\n    onChange = () => {},\n    loop = true,\n    autoPlay = false,\n    autoPlayInterval = 4000,\n    pauseOnHover = true,\n    showArrows = true,\n    showDots = true,\n    swipeable = true,\n    lazyRender = false,\n    adaptiveHeight = false,\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    labels,\n    ...rest\n  } = useComponentDefaults('Carousel', props);\n  const text = useLabels('Carousel', labels, DEFAULT_LABELS);\n\n  const instanceId = React.useId();\n  const slides = React.Children.toArray(children);\n  const count = slides.length;\n  const canLoop = loop && count > 1;\n  const offset = canLoop ? 1 : 0;\n  const extendedSlides = canLoop ? [slides[count - 1], ...slides, slides[0]] : slides;\n\n  const isControlled = currentIndex !== undefined;\n  const [internalIndex, setInternalIndex] = useState(() => clampIndex(defaultIndex, count, canLoop));\n  const realIndex = clampIndex(isControlled ? currentIndex! : internalIndex, count, canLoop);\n\n  const [displayIndex, setDisplayIndex] = useState(realIndex + offset);\n  const [transitionEnabled, setTransitionEnabled] = useState(true);\n  const prevRealIndexRef = useRef(realIndex);\n\n  const [hovered, setHovered] = useState(false);\n  const [focused, setFocused] = useState(false);\n  const [dragging, setDragging] = useState(false);\n\n  const containerRef = useRef<HTMLDivElement>(null);\n  const trackRef = useRef<HTMLDivElement>(null);\n  const dotsRef = useRef<HTMLDivElement>(null);\n\n  // The window of slides `lazyRender` keeps mounted, as of the last settled position. Slides are\n  // rendered for the union of this and the current window, so the ones a multi-step jump slides\n  // past stay mounted until the transform transition ends instead of blanking out mid-flight.\n  const [settledMountRange, setSettledMountRange] = useState(() => ({\n    min: realIndex + offset - 1,\n    max: realIndex + offset + 1,\n  }));\n\n  useLayoutEffect(() => {\n    const prevReal = prevRealIndexRef.current;\n    prevRealIndexRef.current = realIndex;\n    if (prevReal === realIndex) return;\n\n    // A wrap parks the track on a clone until transitionEnd swaps it back, so a change arriving\n    // before that has to animate from the normalised position rather than the clone.\n    const lastExtendedIndex = extendedSlides.length - 1;\n    let fromIndex = displayIndex;\n    if (canLoop && displayIndex === 0) fromIndex = count;\n    else if (canLoop && displayIndex === lastExtendedIndex) fromIndex = 1;\n\n    const forwardWrap = canLoop && prevReal === count - 1 && realIndex === 0;\n    const backwardWrap = canLoop && prevReal === 0 && realIndex === count - 1;\n\n    let target: number;\n    if (forwardWrap) target = fromIndex + 1;\n    else if (backwardWrap) target = fromIndex - 1;\n    else target = realIndex + offset;\n\n    if (lazyRender && Math.abs(target - fromIndex) > MAX_ANIMATED_JUMP) {\n      setTransitionEnabled(false);\n      setDisplayIndex(target);\n      setSettledMountRange({ min: target - 1, max: target + 1 });\n      return;\n    }\n\n    // Slides the track has passed are off screen. Kept until a transition ended, which quick steps\n    // keep restarting, the window grew by a slide each step, and after a wrap spanned the whole set.\n    // Off a wrap's clone, the track is put back on `fromIndex` below.\n    const shown = lazyRender && fromIndex === displayIndex ? (shownPosition(trackRef.current) ?? fromIndex) : fromIndex;\n    setSettledMountRange({ min: Math.floor(shown) - 1, max: Math.ceil(shown) + 1 });\n\n    // Written straight to the node: the normalised position has to reach style recalculation on its\n    // own, or it and the animated position collapse into one jump with no transition.\n    const track = trackRef.current;\n    if (fromIndex !== displayIndex && track) {\n      track.style.transition = 'none';\n      track.style.transform = buildTrackTransform(fromIndex, 0);\n      void track.offsetWidth;\n      track.style.transition = '';\n    }\n\n    setTransitionEnabled(true);\n    setDisplayIndex(target);\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [realIndex]);\n\n  const mountMin = Math.min(settledMountRange.min, displayIndex - 1);\n  const mountMax = Math.max(settledMountRange.max, displayIndex + 1);\n  // Without `lazyRender`, every slide. With it, the others get no element at all: a placeholder per\n  // slide made every render of a large set cost as much as the set.\n  const lastPosition = extendedSlides.length - 1;\n  const firstRendered = lazyRender ? Math.max(0, mountMin) : 0;\n  const lastRendered = lazyRender ? Math.min(lastPosition, mountMax) : lastPosition;\n  const isRendered = (position: number) => position >= firstRendered && position <= lastRendered;\n\n  const [contentHeight, setContentHeight] = useState<number | undefined>(undefined);\n\n  useLayoutEffect(() => {\n    if (!adaptiveHeight) return;\n    const slideElement = trackRef.current?.children[displayIndex - firstRendered] as HTMLElement | undefined;\n    if (!slideElement) return;\n\n    const update = () => setContentHeight(slideElement.offsetHeight);\n    update();\n\n    const observer = new ResizeObserver(update);\n    observer.observe(slideElement);\n    return () => observer.disconnect();\n  }, [adaptiveHeight, displayIndex, firstRendered, count]);\n\n  // Keeps the selected dot in view. Written as `scrollLeft` because `scrollIntoView` is free to\n  // scroll the page along with it.\n  useLayoutEffect(() => {\n    const strip = dotsRef.current;\n    const dot = strip?.children[realIndex] as HTMLElement | undefined;\n    if (!strip || !dot) return;\n    strip.scrollLeft = dot.offsetLeft - (strip.clientWidth - dot.offsetWidth) / 2;\n  }, [realIndex, count]);\n\n  function goToIndex(nextIndex: number) {\n    const clamped = clampIndex(nextIndex, count, canLoop);\n    if (!isControlled) setInternalIndex(clamped);\n    onChange(clamped);\n  }\n\n  function goNext() {\n    if (!canLoop && realIndex >= count - 1) return;\n    goToIndex(realIndex + 1);\n  }\n\n  function goPrev() {\n    if (!canLoop && realIndex <= 0) return;\n    goToIndex(realIndex - 1);\n  }\n\n  function handleTrackTransitionEnd(event: React.TransitionEvent<HTMLDivElement>) {\n    if (event.target !== event.currentTarget) return;\n\n    // A wrap ends on a clone, and the swap to the real slide has no transition of its own: settled\n    // around the clone, the window spanned the whole set.\n    let settled = displayIndex;\n    if (canLoop && displayIndex === 0) settled = count;\n    else if (canLoop && displayIndex === extendedSlides.length - 1) settled = 1;\n    setSettledMountRange({ min: settled - 1, max: settled + 1 });\n\n    if (settled !== displayIndex) {\n      setTransitionEnabled(false);\n      setDisplayIndex(settled);\n    }\n  }\n\n  // Hover and focus only ever pause the timer, so without one they are not worth tracking.\n  const tracksPointerPause = autoPlay && pauseOnHover;\n  const isPaused = hovered || focused || dragging;\n  useEffect(() => {\n    if (!autoPlay || count <= 1 || isPaused) return;\n    const timer = setInterval(goNext, autoPlayInterval);\n    return () => clearInterval(timer);\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [autoPlay, autoPlayInterval, isPaused, realIndex, count]);\n\n  // A drag follows the pointer through the node, not through state: a render per move event would\n  // rebuild every slide and dot for something only the track's transform sees.\n  const dragRef = useRef<{\n    startX: number;\n    startY: number;\n    width: number;\n    axisLocked: boolean;\n    active: boolean;\n    offsetPercent: number;\n  } | null>(null);\n\n  function handlePointerDown(event: React.PointerEvent<HTMLDivElement>) {\n    if (!swipeable || count <= 1) return;\n    // Another mouse button opens a menu, which can keep its release from the page.\n    if (event.button !== 0) return;\n    dragRef.current = {\n      startX: event.clientX,\n      startY: event.clientY,\n      // Measured once: measuring per move would force a layout between the transform writes.\n      width: containerRef.current?.getBoundingClientRect().width || 1,\n      axisLocked: false,\n      active: true,\n      offsetPercent: 0,\n    };\n  }\n\n  function handlePointerMove(event: React.PointerEvent<HTMLDivElement>) {\n    const drag = dragRef.current;\n    if (!drag) return;\n    // No button held, so its release went somewhere the track never heard of: over the dots, an\n    // arrow, outside the page, or into a context menu. Without this, every hover after it dragged.\n    if (event.buttons === 0) {\n      handlePointerUp();\n      return;\n    }\n    if (!drag.active) return;\n\n    const deltaX = event.clientX - drag.startX;\n    const deltaY = event.clientY - drag.startY;\n\n    if (!drag.axisLocked) {\n      if (Math.abs(deltaX) < AXIS_LOCK_THRESHOLD && Math.abs(deltaY) < AXIS_LOCK_THRESHOLD) return;\n      if (Math.abs(deltaY) > Math.abs(deltaX)) {\n        drag.active = false;\n        return;\n      }\n      drag.axisLocked = true;\n      // Never on pointerdown: a captured pointer sends its `click` to the capture target too, so\n      // capturing earlier would swallow every click meant for a button inside a slide.\n      event.currentTarget.setPointerCapture?.(event.pointerId);\n      setDragging(true);\n      setTransitionEnabled(false);\n    }\n\n    drag.offsetPercent = (deltaX / drag.width) * 100;\n    if (trackRef.current) {\n      trackRef.current.style.transform = buildTrackTransform(displayIndex, drag.offsetPercent);\n    }\n  }\n\n  function handlePointerUp() {\n    const drag = dragRef.current;\n    if (!drag) return;\n    dragRef.current = null;\n    if (!drag.axisLocked) {\n      setDragging(false);\n      return;\n    }\n\n    setTransitionEnabled(true);\n    setDragging(false);\n\n    // React never saw the dragged transform, so it has to be handed back by hand, and always: a\n    // drag that changes no index would otherwise strand the track off-screen. One that does change\n    // it overwrites this in the same tick, and still animates on from where the pointer left off.\n    if (trackRef.current) {\n      trackRef.current.style.transition = '';\n      trackRef.current.style.transform = buildTrackTransform(displayIndex, 0);\n    }\n\n    if (Math.abs(drag.offsetPercent) >= DRAG_COMMIT_RATIO * 100) {\n      if (drag.offsetPercent < 0) goNext();\n      else goPrev();\n    }\n  }\n\n  // Arrow keys belong to the carousel's own controls, never to the slides: a text field or a\n  // scrollable region in a slide has to keep its own. `defaultPrevented` leaves the dots to their\n  // own handling below.\n  function handleKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {\n    if (event.defaultPrevented || trackRef.current?.contains(event.target as Node)) return;\n\n    if (event.key === 'ArrowLeft') {\n      event.preventDefault();\n      goPrev();\n    } else if (event.key === 'ArrowRight') {\n      event.preventDefault();\n      goNext();\n    }\n  }\n\n  function handleDotsKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {\n    let next: number | null = null;\n    if (event.key === 'ArrowLeft') next = realIndex - 1;\n    else if (event.key === 'ArrowRight') next = realIndex + 1;\n    else if (event.key === 'Home') next = 0;\n    else if (event.key === 'End') next = count - 1;\n    if (next === null) return;\n\n    event.preventDefault();\n    const clamped = clampIndex(next, count, canLoop);\n    goToIndex(clamped);\n    // The selected dot is the one holding the tab stop, so focus has to travel with the selection.\n    (dotsRef.current?.children[clamped] as HTMLElement | undefined)?.focus();\n  }\n\n  // The dots are the APG tablist over the slides. Without them the slides describe themselves as\n  // plain slides rather than as panels.\n  const hasTabs = showDots && count > 1;\n  const dotId = (index: number) => `${instanceId}-dot-${index}`;\n  const slideId = (index: number) => `${instanceId}-slide-${index}`;\n\n  // An unnamed landmark is worse than none, so the carousel is only a region once it has a name.\n  const isNamed = rest['aria-label'] !== undefined || rest['aria-labelledby'] !== undefined;\n\n  function slideSemantics(index: number) {\n    if (hasTabs) {\n      return { id: slideId(index), role: 'tabpanel', 'aria-labelledby': dotId(index) };\n    }\n    return {\n      id: slideId(index),\n      role: 'group',\n      'aria-roledescription': 'slide',\n      'aria-label': `${index + 1} of ${count}`,\n    };\n  }\n\n  const isPrevDisabled = !canLoop && realIndex <= 0;\n  const isNextDisabled = !canLoop && realIndex >= count - 1;\n\n  const trackTransform = buildTrackTransform(displayIndex, 0);\n\n  return (\n    <div\n      {...rest}\n      ref={containerRef}\n      role={isNamed ? 'region' : 'group'}\n      aria-roledescription='carousel'\n      className={cx(styles.carousel, adaptiveHeight && styles.carouselAdaptive, classNames?.root, className)}\n      style={{ height: adaptiveHeight ? contentHeight : undefined, ...style, ...slotStyles?.root }}\n      onMouseEnter={tracksPointerPause ? () => setHovered(true) : undefined}\n      onMouseLeave={tracksPointerPause ? () => setHovered(false) : undefined}\n      onFocus={tracksPointerPause ? () => setFocused(true) : undefined}\n      onBlur={tracksPointerPause ? () => setFocused(false) : undefined}\n      onKeyDown={handleKeyDown}\n    >\n      <div\n        ref={trackRef}\n        className={cx(styles.carouselTrack, adaptiveHeight && styles.carouselTrackAdaptive, classNames?.track)}\n        style={{\n          transform: trackTransform,\n          transition: transitionEnabled ? undefined : 'none',\n          ...slotStyles?.track,\n        }}\n        aria-live={autoPlay ? 'off' : 'polite'}\n        onTransitionEnd={handleTrackTransitionEnd}\n        onPointerDown={handlePointerDown}\n        onPointerMove={handlePointerMove}\n        onPointerUp={handlePointerUp}\n        onPointerCancel={handlePointerUp}\n      >\n        {extendedSlides.slice(firstRendered, lastRendered + 1).map((slide, i) => {\n          const idx = firstRendered + i;\n          const isClonePosition = canLoop && (idx === 0 || idx === lastPosition);\n          const logicalIndex = idx - offset;\n          const isActive = !isClonePosition && logicalIndex === realIndex;\n\n          return (\n            <div\n              key={`carousel-slide-${idx}`}\n              {...(isClonePosition ? {} : slideSemantics(logicalIndex))}\n              className={cx(styles.carouselSlide, classNames?.slide)}\n              style={{ transform: `translateX(${idx * 100}%)`, ...slotStyles?.slide }}\n              inert={isActive ? undefined : true}\n            >\n              <CarouselSlideContext.Provider value={isActive}>{slide}</CarouselSlideContext.Provider>\n            </div>\n          );\n        })}\n      </div>\n\n      {showArrows && count > 1 && (\n        <>\n          <ActionIcon\n            styleType={ButtonStyleType.Filled}\n            color={ARROW_COLOR}\n            icon={faChevronLeft}\n            title={text.previous}\n            aria-label={text.previous}\n            aria-disabled={isPrevDisabled || undefined}\n            onClick={() => {\n              if (!isPrevDisabled) goPrev();\n            }}\n            className={cx(\n              styles.carouselPrevButton,\n              isPrevDisabled && styles.carouselButtonDisabled,\n              classNames?.prevButton,\n            )}\n            style={slotStyles?.prevButton}\n          />\n          <ActionIcon\n            styleType={ButtonStyleType.Filled}\n            color={ARROW_COLOR}\n            icon={faChevronRight}\n            title={text.next}\n            aria-label={text.next}\n            aria-disabled={isNextDisabled || undefined}\n            onClick={() => {\n              if (!isNextDisabled) goNext();\n            }}\n            className={cx(\n              styles.carouselNextButton,\n              isNextDisabled && styles.carouselButtonDisabled,\n              classNames?.nextButton,\n            )}\n            style={slotStyles?.nextButton}\n          />\n        </>\n      )}\n\n      {hasTabs && (\n        <div\n          ref={dotsRef}\n          role='tablist'\n          aria-label={text.slides}\n          className={cx(\n            styles.carouselDots,\n            // Both ends always: the strip's own padding holds the outer dots clear of the fade\n            // until there is something to scroll, so this needs no measuring.\n            fadeStyles.scrollFade,\n            fadeStyles.scrollFadeX,\n            fadeStyles.scrollFadeFadedStart,\n            fadeStyles.scrollFadeFadedEnd,\n            classNames?.dots,\n          )}\n          style={slotStyles?.dots}\n          onKeyDown={handleDotsKeyDown}\n        >\n          {slides.map((_, index) => (\n            <InteractableDiv\n              key={`carousel-dot-${index}`}\n              id={dotId(index)}\n              role='tab'\n              aria-label={text.goToSlide(index + 1)}\n              aria-selected={index === realIndex}\n              // Only at a panel that exists: `lazyRender` leaves most of them out.\n              aria-controls={isRendered(index + offset) ? slideId(index) : undefined}\n              // One tab stop for the whole set, arrow keys inside it.\n              tabIndex={index === realIndex ? 0 : -1}\n              onClick={() => goToIndex(index)}\n              className={cx(styles.carouselDot, index === realIndex && styles.carouselDotActive, classNames?.dot)}\n              style={slotStyles?.dot}\n            />\n          ))}\n        </div>\n      )}\n    </div>\n  );\n}\n\n/** Where the track is on screen, mid-transition included; null without a layout to read it from. */\nfunction shownPosition(track: HTMLElement | null): number | null {\n  const width = track?.offsetWidth;\n  if (!track || !width) return null;\n  const { m41 } = new DOMMatrixReadOnly(getComputedStyle(track).transform);\n  // Rounded, so a track at rest reads as a whole slide despite the float error in the matrix.\n  return Math.round((-m41 / width) * 100) / 100;\n}\n\n/** The track is one slide wide, so both are in slide widths. */\nfunction buildTrackTransform(position: number, dragPercent: number): string {\n  return `translateX(calc(${-position * 100}% + ${dragPercent}%))`;\n}\n\nfunction clampIndex(index: number, count: number, canLoop: boolean): number {\n  if (count === 0) return 0;\n  if (canLoop) return ((index % count) + count) % count;\n  return Math.max(0, Math.min(count - 1, index));\n}\n\nexport default Carousel;\n","import { useLayoutEffect, useRef, type RefObject } from 'react';\n\ninterface AutoGrowOptions {\n  enabled?: boolean;\n  /** Whether the text needs more than the field's `rows`, after each change of `value` or width. */\n  onGrow?: (isGrown: boolean) => void;\n}\n\n/** Sizes the field to its text, and says whether that took it past its `rows`. */\nfunction grow(field: HTMLElement): boolean {\n  // Collapsing the field to measure it would shrink its parent for that moment. A scroller that\n  // gains the room clamps its position and keeps it when the field grows back: a chat's message\n  // list lost its bottom on every keystroke. Holding the parent's height keeps the collapse inside;\n  // offsetHeight, as a transform (a modal scaling in) would make the rect too small to hold it.\n  const parent = field.parentElement;\n  const { minHeight } = parent?.style ?? {};\n  const { alignSelf } = field.style;\n  if (parent) parent.style.minHeight = `${parent.offsetHeight}px`;\n  // A stretched field would grow back to the held height and never measure shorter (Input's is).\n  field.style.alignSelf = 'flex-start';\n  field.style.height = 'auto';\n  // Collapsed, the field is as tall as its `rows`. The 1px absorbs rounding: a line is far more.\n  const isGrown = field.scrollHeight > field.clientHeight + 1;\n  field.style.height = `${field.scrollHeight}px`;\n  field.style.alignSelf = alignSelf;\n  if (parent) parent.style.minHeight = minHeight ?? '';\n  return isGrown;\n}\n\n/**\n * Keeps a textarea as tall as its text, up to the `max-height` its stylesheet sets. It measures\n * again when `value` changes, so the field must be controlled, and when its width changes.\n */\nexport function useAutoGrow(\n  ref: RefObject<HTMLElement | null>,\n  value: unknown,\n  { enabled = true, onGrow }: AutoGrowOptions = {},\n): void {\n  // Kept in a ref so a fresh callback each render does not resubscribe the observer.\n  const onGrowRef = useRef(onGrow);\n  useLayoutEffect(() => {\n    onGrowRef.current = onGrow;\n  });\n\n  useLayoutEffect(() => {\n    const field = ref.current;\n    if (!enabled || !field) return undefined;\n    let width = field.offsetWidth;\n    let frame = 0;\n    // A new width rewraps the same text onto more or fewer lines. Growing in the callback itself\n    // would resize what is observed, which browsers report as a ResizeObserver loop error, so it\n    // waits a frame. The height it then changes is skipped, as the width is the same.\n    const observer = new ResizeObserver(() => {\n      if (field.offsetWidth === width) return;\n      width = field.offsetWidth;\n      cancelAnimationFrame(frame);\n      frame = requestAnimationFrame(() => {\n        const isGrown = grow(field);\n        onGrowRef.current?.(isGrown);\n      });\n    });\n    observer.observe(field);\n    return () => {\n      observer.disconnect();\n      cancelAnimationFrame(frame);\n      // Only clears a height this set, so an explicit one from a style prop survives while\n      // disabled. Here rather than below, where it would collapse the field on every keystroke.\n      field.style.height = '';\n    };\n  }, [ref, enabled]);\n\n  useLayoutEffect(() => {\n    if (!enabled || !ref.current) return;\n    // Measured first: an optional call skips evaluating its argument when there is no callback.\n    const isGrown = grow(ref.current);\n    onGrowRef.current?.(isGrown);\n  }, [ref, value, enabled]);\n}\n",".chatComposer {\n  display: flex;\n  align-items: flex-end;\n  gap: 0.25em;\n  box-sizing: border-box;\n  padding: 0.375em;\n  border: 1px solid var(--border-color);\n  border-radius: calc(var(--default-border-radius) * 1.5);\n  background: var(--input-background);\n  color: var(--text-color);\n\n  &:focus-within {\n    border-color: var(--focus-border-color, var(--primary-color));\n  }\n\n  /* Once the text needs a second line, the field takes the whole width and the buttons a row\n     below it: beside it, a few of them would leave it too narrow to write in. Only the order\n     changes, never the element, so the focus and an IME composition survive the switch. */\n  &-stacked {\n    flex-wrap: wrap;\n  }\n\n  &-disabled {\n    background-color: var(--disabled-input-color);\n  }\n\n  /* As tall as one line of the field, so a smaller button centres on the first line rather than\n     sitting on the field's bottom edge. */\n  &-section {\n    display: flex;\n    flex: none;\n    align-items: center;\n    gap: 0.25em;\n    min-height: 2.25em;\n  }\n\n  &-input {\n    flex: 1 1 auto;\n    min-width: 0;\n    box-sizing: border-box;\n\n    /* Seven lines of line-height 1.5, plus the padding. */\n    max-height: 11.25em;\n    margin: 0;\n    padding: 0.375em 0.5em;\n    border: 0;\n    outline: none;\n    background: transparent;\n    color: inherit;\n    font: inherit;\n    line-height: 1.5;\n    resize: none;\n    overflow-y: auto;\n\n    &::placeholder {\n      color: var(--text-dark);\n    }\n\n    &:disabled {\n      color: var(--disabled-font-color);\n      -webkit-text-fill-color: var(--disabled-font-color);\n    }\n  }\n}\n\n.chatComposer-stacked .chatComposer-input {\n  flex-basis: 100%;\n  order: -1;\n}\n\n/* The end content, or the send button when there is none, and with it everything after: to the\n   far end of the row below. Two classes, as it can be the ActionIcon. */\n.chatComposer-stacked .chatComposer-input + * {\n  margin-inline-start: auto;\n}\n\n/* Two classes: it is an ActionIcon, whose own one-class rules it would otherwise tie with. As tall\n   as one line of the field, line-height plus padding, so the two line up while it is empty. */\n.chatComposer .chatComposer-button {\n  flex: none;\n  width: 2.25em;\n  height: 2.25em;\n  border-radius: 50%;\n}\n","import { useRef, useState, type KeyboardEvent, type MouseEvent, type ReactElement } from 'react';\nimport cx from 'classnames';\nimport { faPaperPlane, faStop } from '@fortawesome/free-solid-svg-icons';\n\nimport ActionIcon from '../actionIcon';\nimport { ButtonStyleType } from '../button/button.types';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport { useAutoGrow } from '../hooks/useAutoGrow';\nimport { useCoarsePointer } from '../hooks/useCoarsePointer';\nimport { ThemeColor } from '../themeProvider/color.types';\nimport { mergeRefs } from '../utils/mergeRefs';\nimport type { ChatComposerLabels, ChatComposerProps } from './chatComposer.types';\nimport styles from './chatComposer.module.pcss';\n\nconst DEFAULT_LABELS: Required<ChatComposerLabels> = {\n  placeholder: 'Message',\n  send: 'Send',\n  stop: 'Stop',\n};\n\nfunction ChatComposer(props: ChatComposerProps): ReactElement {\n  const {\n    onSend,\n    value,\n    defaultValue = '',\n    onChange,\n    onKeyDown,\n    onStop,\n    disabled = false,\n    readOnly = false,\n    rows = 1,\n    startContent,\n    endContent,\n    ref,\n    labels,\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    ...rest\n  } = useComponentDefaults('ChatComposer', props);\n  const text = useLabels('ChatComposer', labels, DEFAULT_LABELS);\n  const isCoarsePointer = useCoarsePointer();\n  const inputRef = useRef<HTMLTextAreaElement>(null);\n  const [internalValue, setInternalValue] = useState(defaultValue);\n  const current = value ?? internalValue;\n  const canSend = !disabled && !readOnly && current.trim() !== '';\n  const [isGrown, setIsGrown] = useState(false);\n\n  // Back to one row only once the field is empty. Below the buttons the field is wider, and text\n  // that wrapped beside them may fit on one line there: dropping back then would wrap it again.\n  if (isGrown && current === '') setIsGrown(false);\n  // With nothing besides the send button, a row of its own would cost more height than it frees.\n  const isStacked = isGrown && (startContent != null || endContent != null);\n\n  useAutoGrow(inputRef, current, {\n    onGrow: (grown) => {\n      // An empty field can measure taller than a line: Chrome counts a wrapping placeholder.\n      if (grown && inputRef.current?.value) setIsGrown(true);\n    },\n  });\n\n  const setValue = (next: string) => {\n    if (value === undefined) setInternalValue(next);\n    onChange?.(next);\n  };\n\n  const send = () => {\n    if (!canSend || onStop) return;\n    onSend(current.trim());\n    setValue('');\n    inputRef.current?.focus();\n  };\n\n  const handleKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>) => {\n    onKeyDown?.(event);\n    // A consumer's own use of Enter comes first, such as picking from an open mention list.\n    if (event.defaultPrevented || event.key !== 'Enter' || event.shiftKey) return;\n    // The Enter that confirms a word in a Japanese, Chinese or Korean IME. Safari sends its keydown\n    // after compositionend, with isComposing already false, and only keyCode 229 gives it away.\n    if (event.nativeEvent.isComposing || event.keyCode === 229) return;\n    // On a touch keyboard Enter is the only way to start a new line, so there the button sends.\n    if (isCoarsePointer && !event.metaKey && !event.ctrlKey) return;\n    event.preventDefault();\n    send();\n  };\n\n  // Keeps the focus in the field when the button is pressed, so a phone keyboard stays open.\n  const keepFocus = (event: MouseEvent) => event.preventDefault();\n\n  return (\n    <div\n      className={cx(\n        styles.chatComposer,\n        isStacked && styles.chatComposerStacked,\n        disabled && styles.chatComposerDisabled,\n        className,\n      )}\n      style={style}\n    >\n      {startContent != null && <div className={styles.chatComposerSection}>{startContent}</div>}\n      <textarea\n        // Before the spread, so a name of the consumer's own wins over the placeholder.\n        aria-label={text.placeholder}\n        {...rest}\n        ref={mergeRefs(inputRef, ref)}\n        rows={rows}\n        value={current}\n        placeholder={text.placeholder}\n        disabled={disabled}\n        readOnly={readOnly}\n        className={cx(styles.chatComposerInput, classNames?.input)}\n        style={slotStyles?.input}\n        onChange={(event) => setValue(event.target.value)}\n        onKeyDown={handleKeyDown}\n      />\n      {endContent != null && <div className={styles.chatComposerSection}>{endContent}</div>}\n      {onStop ? (\n        <ActionIcon\n          icon={faStop}\n          styleType={ButtonStyleType.Filled}\n          color={ThemeColor.Primary}\n          aria-label={text.stop}\n          title={text.stop}\n          disabled={disabled}\n          className={cx(styles.chatComposerButton, classNames?.button)}\n          style={slotStyles?.button}\n          onMouseDown={keepFocus}\n          onClick={onStop}\n        />\n      ) : (\n        <ActionIcon\n          icon={faPaperPlane}\n          styleType={ButtonStyleType.Filled}\n          color={ThemeColor.Primary}\n          aria-label={text.send}\n          title={text.send}\n          disabled={!canSend}\n          className={cx(styles.chatComposerButton, classNames?.button)}\n          style={slotStyles?.button}\n          onMouseDown={keepFocus}\n          onClick={send}\n        />\n      )}\n    </div>\n  );\n}\n\nexport default ChatComposer;\n","import type { CSSProperties, ReactNode } from 'react';\nimport type { SlotClassNames, SlotStyles, HtmlProps } from '../types/slots.types';\nimport type { ColorValue } from '../themeProvider/color.types';\n\n/** How much the message stands out. Independent of its colour. */\nexport enum ChatMessageStyleType {\n  /** Solid bubble, text in a contrasting colour. The default for `isOwn`. */\n  Filled = 'filled',\n  /** A light tint of the colour. The default for everyone else. */\n  Light = 'light',\n  /** No bubble, full width: an AI assistant's answer. */\n  Plain = 'plain',\n}\n\n/** Delivery state of an own message: an icon next to `time`, and `Failed` a notice under the bubble. */\nexport enum ChatMessageStatus {\n  Sending = 'sending',\n  Sent = 'sent',\n  Delivered = 'delivered',\n  Read = 'read',\n  Failed = 'failed',\n}\n\nexport type ChatMessageSlots = 'avatar' | 'body' | 'author' | 'bubble' | 'meta' | 'actions';\n\n/** Text ChatMessage renders of its own, for translating it. */\nexport interface ChatMessageLabels {\n  /** Read out for the status icon. */\n  sending?: string;\n  sent?: string;\n  delivered?: string;\n  read?: string;\n  /** Shown under the bubble. */\n  failed?: string;\n  /** Names an own message without `author` for screen readers. */\n  you?: string;\n}\n\nexport interface ChatMessageProps extends HtmlProps<HTMLDivElement> {\n  /** The message. A string keeps its line breaks; anything else (rendered Markdown, an image) renders as given. */\n  children?: ReactNode;\n  /** Sent by the current user: aligned to the end, `Filled` in `Primary` by default. */\n  isOwn?: boolean;\n  /** Follows a message from the same author: hides `author` from view, keeps the space of `avatar` and tightens the gap above. */\n  isContinued?: boolean;\n  /** Shown beside the bubble, typically an `Avatar`. */\n  avatar?: ReactNode;\n  /** Name above the bubble. Screen readers get it on every message of a run, not only the first. */\n  author?: ReactNode;\n  /** Shown in the bubble's corner. Format it yourself, e.g. with `Intl.DateTimeFormat`. */\n  time?: ReactNode;\n  status?: ChatMessageStatus;\n  /** Shown below the bubble, after the `Failed` notice: copy, retry, rate. */\n  actions?: ReactNode;\n  /** Default `Filled` when `isOwn`, otherwise `Light`. */\n  styleType?: ChatMessageStyleType;\n  /** A `ThemeColor` or any CSS colour. Default `Primary` when `isOwn`, otherwise `Neutral`. */\n  color?: ColorValue;\n  /** Overrides the text ChatMessage renders of its own. Merged per key. */\n  labels?: ChatMessageLabels;\n  className?: string;\n  style?: CSSProperties;\n  classNames?: SlotClassNames<ChatMessageSlots>;\n  styles?: SlotStyles<ChatMessageSlots>;\n}\n",".chatMessage {\n  position: relative;\n  display: flex;\n  align-items: flex-start;\n  gap: 0.5em;\n  margin-top: 0.75em;\n\n  &:first-child {\n    margin-top: 0;\n  }\n\n  &-own {\n    flex-direction: row-reverse;\n  }\n\n  &-continued {\n    margin-top: 0.125em;\n  }\n\n  &-avatar {\n    flex: none;\n    display: flex;\n  }\n\n  &-body {\n    display: flex;\n    flex-direction: column;\n    align-items: flex-start;\n    min-width: 0;\n    max-width: min(80%, 40em);\n  }\n\n  &-author {\n    margin-bottom: 0.25em;\n    padding-inline: 0.75em;\n    color: var(--text-dark);\n    font-size: var(--text-small-font-size);\n    font-weight: 600;\n\n    /* Out of view but read out. Pinned to the message, or its containing block could be the page\n       and leave empty space to scroll through below it. */\n    &-hidden {\n      position: absolute;\n      top: 0;\n      left: 0;\n      width: 1px;\n      height: 1px;\n      margin: 0;\n      padding: 0;\n      clip-path: inset(50%);\n      overflow: hidden;\n      white-space: nowrap;\n    }\n  }\n\n  /* Wraps rather than stacks: a short message keeps its meta on the same line, a long one pushes\n     it onto a line of its own at the end. */\n  &-bubble {\n    display: flex;\n    flex-wrap: wrap;\n    align-items: flex-end;\n    column-gap: 0.5em;\n    box-sizing: border-box;\n    max-width: 100%;\n    padding: 0.5em 0.75em;\n    border-radius: calc(var(--default-border-radius) * 1.5);\n    border-start-start-radius: 0.25em;\n    overflow-wrap: anywhere;\n\n    &-filled {\n      background: var(--tone-fill);\n\n      /* Must follow the shorthand above, which resets background-image. */\n      background-image: var(--tone-gradient);\n      color: var(--tone-contrast);\n    }\n\n    /* Text keeps the page colour, as on a Light Card: a paragraph in the tone would not read. */\n    &-light {\n      background: var(--tone-light);\n      color: var(--text-color);\n    }\n\n    &-plain {\n      padding: 0;\n      border-radius: 0;\n      color: var(--text-color);\n    }\n  }\n\n  &-content {\n    flex: 1 1 auto;\n    min-width: 0;\n\n    &-text {\n      white-space: pre-wrap;\n    }\n  }\n\n  &-meta {\n    display: inline-flex;\n    align-items: center;\n    gap: 0.25em;\n    margin-inline-start: auto;\n    color: color-mix(in srgb, currentcolor 70%, transparent);\n    font-size: 0.75em;\n    white-space: nowrap;\n  }\n\n  /* Read stands out by brightness as well as colour, which alone would not reach everyone, and on\n     a filled bubble in a blue theme reaches no one. */\n  &-status {\n    display: inline-flex;\n    opacity: 0.7;\n\n    &-read {\n      color: var(--info-color);\n      opacity: 1;\n    }\n  }\n\n  &-actions {\n    display: flex;\n    flex-wrap: wrap;\n    align-items: center;\n    gap: 0.25em;\n    margin-top: 0.25em;\n  }\n\n  /* The text in the page's own colour: the default dark theme's red reaches 2.1:1 on its\n     background, too little to read. The text carries the meaning, the red icon the signal. */\n  &-failed {\n    display: inline-flex;\n    align-items: center;\n    gap: 0.375em;\n    padding-inline: 0.25em;\n    color: var(--text-color);\n    font-size: var(--text-small-font-size);\n\n    &-icon {\n      color: var(--error-color);\n    }\n  }\n}\n\n.chatMessage-own .chatMessage-body {\n  align-items: flex-end;\n}\n\n.chatMessage-own .chatMessage-bubble {\n  border-start-start-radius: calc(var(--default-border-radius) * 1.5);\n  border-start-end-radius: 0.25em;\n}\n\n/* The corner pointing at the author marks the first message of a run, so the rest round it off.\n   After the own rule, which it ties with. */\n.chatMessage-continued .chatMessage-bubble {\n  border-start-start-radius: calc(var(--default-border-radius) * 1.5);\n  border-start-end-radius: calc(var(--default-border-radius) * 1.5);\n}\n\n/* Keeps the column, so a run of messages stays aligned under the avatar of its first. */\n.chatMessage-continued .chatMessage-avatar {\n  visibility: hidden;\n}\n\n.chatMessage-plain .chatMessage-body {\n  flex: 1 1 auto;\n  align-items: stretch;\n  max-width: none;\n}\n\n.chatMessage-plain .chatMessage-author {\n  padding-inline: 0;\n}\n\n/* The info blue would sink into a filled bubble. */\n.chatMessage-bubble-filled .chatMessage-status-read {\n  color: var(--tone-contrast);\n}\n","import type { ReactElement, ReactNode } from 'react';\nimport cx from 'classnames';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faCheck, faCheckDouble, faCircleExclamation, faClock } from '@fortawesome/free-solid-svg-icons';\nimport type { IconDefinition } from '@fortawesome/fontawesome-svg-core';\n\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport { ThemeColor } from '../themeProvider/color.types';\nimport { toneProps, useToneCheck } from '../styles/tone';\nimport { ChatMessageStatus, ChatMessageStyleType } from './chatMessage.types';\nimport type { ChatMessageLabels, ChatMessageProps } from './chatMessage.types';\nimport styles from './chatMessage.module.pcss';\n\nconst DEFAULT_LABELS: Required<ChatMessageLabels> = {\n  sending: 'Sending',\n  sent: 'Sent',\n  delivered: 'Delivered',\n  read: 'Read',\n  failed: 'Not sent',\n  you: 'You',\n};\n\n// Failed has no icon in the bubble: it is a notice under it, where it has the page's background\n// to stand out against rather than the bubble's own colour.\nconst ICON_BY_STATUS: Record<Exclude<ChatMessageStatus, ChatMessageStatus.Failed>, IconDefinition> = {\n  [ChatMessageStatus.Sending]: faClock,\n  [ChatMessageStatus.Sent]: faCheck,\n  [ChatMessageStatus.Delivered]: faCheckDouble,\n  [ChatMessageStatus.Read]: faCheckDouble,\n};\n\nconst CLASS_BY_STYLE: Record<ChatMessageStyleType, string> = {\n  [ChatMessageStyleType.Filled]: styles.chatMessageBubbleFilled,\n  [ChatMessageStyleType.Light]: styles.chatMessageBubbleLight,\n  [ChatMessageStyleType.Plain]: styles.chatMessageBubblePlain,\n};\n\n// So `actions={isFailed && <RetryButton />}` leaves no empty row behind when it is false.\nconst isShown = (node: ReactNode) => node != null && typeof node !== 'boolean' && node !== '';\n\nfunction ChatMessage(props: ChatMessageProps): ReactElement {\n  const {\n    children,\n    isOwn = false,\n    isContinued = false,\n    avatar,\n    author,\n    time,\n    status,\n    actions,\n    styleType = isOwn ? ChatMessageStyleType.Filled : ChatMessageStyleType.Light,\n    color = isOwn ? ThemeColor.Primary : ThemeColor.Neutral,\n    labels,\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    ...rest\n  } = useComponentDefaults('ChatMessage', props);\n  const text = useLabels('ChatMessage', labels, DEFAULT_LABELS);\n  const tone = toneProps(color);\n  useToneCheck('ChatMessage', [color]);\n  const isFailed = status === ChatMessageStatus.Failed;\n  const statusIcon = status == null || isFailed ? null : ICON_BY_STATUS[status];\n  // Alignment is what tells an own message apart, and that is not read out.\n  const sender = isShown(author) ? author : isOwn ? text.you : null;\n\n  return (\n    <div\n      {...rest}\n      className={cx(\n        styles.chatMessage,\n        isOwn && styles.chatMessageOwn,\n        isContinued && styles.chatMessageContinued,\n        styleType === ChatMessageStyleType.Plain && styles.chatMessagePlain,\n        className,\n      )}\n      style={style}\n    >\n      {isShown(avatar) && (\n        <div className={cx(styles.chatMessageAvatar, classNames?.avatar)} style={slotStyles?.avatar}>\n          {avatar}\n        </div>\n      )}\n      <div className={cx(styles.chatMessageBody, classNames?.body)} style={slotStyles?.body}>\n        {sender != null && (\n          <div\n            className={cx(\n              styles.chatMessageAuthor,\n              (isContinued || !isShown(author)) && styles.chatMessageAuthorHidden,\n              classNames?.author,\n            )}\n            style={slotStyles?.author}\n          >\n            {sender}\n          </div>\n        )}\n        <div\n          className={cx(styles.chatMessageBubble, tone.className, CLASS_BY_STYLE[styleType], classNames?.bubble)}\n          style={{ ...tone.style, ...slotStyles?.bubble }}\n        >\n          <div className={cx(styles.chatMessageContent, typeof children === 'string' && styles.chatMessageContentText)}>\n            {children}\n          </div>\n          {(isShown(time) || statusIcon) && (\n            <span className={cx(styles.chatMessageMeta, classNames?.meta)} style={slotStyles?.meta}>\n              {time}\n              {status != null && statusIcon && (\n                <span\n                  role='img'\n                  aria-label={text[status]}\n                  className={cx(\n                    styles.chatMessageStatus,\n                    status === ChatMessageStatus.Read && styles.chatMessageStatusRead,\n                  )}\n                >\n                  <FontAwesomeIcon icon={statusIcon} />\n                </span>\n              )}\n            </span>\n          )}\n        </div>\n        {(isFailed || isShown(actions)) && (\n          <div className={cx(styles.chatMessageActions, classNames?.actions)} style={slotStyles?.actions}>\n            {isFailed && (\n              <span className={styles.chatMessageFailed}>\n                <FontAwesomeIcon icon={faCircleExclamation} className={styles.chatMessageFailedIcon} />\n                {text.failed}\n              </span>\n            )}\n            {actions}\n          </div>\n        )}\n      </div>\n    </div>\n  );\n}\n\nexport default ChatMessage;\n","import { useEffect, useLayoutEffect, useRef, useState } from 'react';\nimport type React from 'react';\n\nconst RETRY_DELAY = 3000;\n// How far ahead of the end a load starts, so that scrolling at a normal pace rarely reaches the\n// spinner. The same as VirtualList's default `loadMoreThreshold`.\nconst LOAD_AHEAD = '100px 0px';\n\n// Loads more items once a sentinel element at the end of the list comes near.\n// IntersectionObserver only fires on a change of intersection state, so if a page of\n// freshly-loaded items still doesn't fill the container, the sentinel stays in range and\n// we have to re-arm the observer ourselves to pick up the next page.\n// Without `onLoadMore` it watches nothing. Passing it only once the container and sentinel are\n// rendered is what starts the watching: an observer set up before then never would.\nexport function useInfiniteScroll(\n  containerRef: React.RefObject<HTMLDivElement | null>,\n  onLoadMore: (() => Promise<void>) | undefined,\n) {\n  const [isLoading, setIsLoading] = useState(false);\n  const isLoadingRef = useRef(false);\n  const sentinelRef = useRef<HTMLDivElement>(null);\n  // The observer of the current effect, so that a load which outlives its own still re-arms this one.\n  const rearmRef = useRef(() => {});\n  const retryRef = useRef(0);\n  // Elements beside the sentinel when the last load started, until the list holds a different number.\n  const loadedFromRef = useRef<number | null>(null);\n\n  // Kept in a ref so a caller passing a fresh onLoadMore function each render doesn't\n  // tear down and recreate the observer every time.\n  const onLoadMoreRef = useRef(onLoadMore);\n  useEffect(() => {\n    onLoadMoreRef.current = onLoadMore;\n  });\n  const isEnabled = onLoadMore !== undefined;\n\n  // A load re-arms only once the list has changed, so one that added nothing ends the asking. Not\n  // when it settles: its items often render later, a React Query page a whole task later. Counted\n  // in the DOM rather than from the caller's items, where a Fragment of messages counts as one.\n  useLayoutEffect(() => {\n    const from = loadedFromRef.current;\n    if (isLoading || from === null) return;\n    if (sentinelRef.current?.parentElement?.childElementCount === from) return;\n    loadedFromRef.current = null;\n    rearmRef.current();\n  });\n\n  useEffect(() => {\n    const root = containerRef.current;\n    const sentinel = sentinelRef.current;\n    if (!isEnabled || !root || !sentinel) return undefined;\n\n    // Observing again reports the sentinel at once, for a page that still left it in range.\n    const rearm = () => {\n      observer.unobserve(sentinel);\n      observer.observe(sentinel);\n    };\n\n    const loadMore = () => {\n      const load = onLoadMoreRef.current;\n      if (!load || isLoadingRef.current) return;\n      isLoadingRef.current = true;\n      loadedFromRef.current = sentinel.parentElement?.childElementCount ?? null;\n      setIsLoading(true);\n\n      load().then(\n        () => {\n          isLoadingRef.current = false;\n          setIsLoading(false);\n        },\n        () => {\n          isLoadingRef.current = false;\n          loadedFromRef.current = null;\n          setIsLoading(false);\n          // A failure waits before trying again, or it would retry without end while the sentinel\n          // stays in range. It does try again: a list too short to scroll offers no other way.\n          retryRef.current = window.setTimeout(() => rearmRef.current(), RETRY_DELAY);\n        },\n      );\n    };\n\n    const observer = new IntersectionObserver(\n      ([entry]) => {\n        if (entry.isIntersecting) loadMore();\n      },\n      { root, rootMargin: LOAD_AHEAD },\n    );\n\n    observer.observe(sentinel);\n    rearmRef.current = rearm;\n\n    return () => {\n      window.clearTimeout(retryRef.current);\n      observer.disconnect();\n      rearmRef.current = () => {};\n    };\n  }, [containerRef, isEnabled]);\n\n  return { isLoading, sentinelRef };\n}\n",".chatMessageList {\n  position: relative;\n  display: flex;\n  flex-direction: column;\n  min-height: 0;\n\n  &-viewport {\n    display: flex;\n    flex: 1 1 auto;\n    flex-direction: column;\n    min-height: 0;\n    overflow-y: auto;\n\n    /* The component anchors the scroll position itself; the browser's own would correct it twice. */\n    overflow-anchor: none;\n    overscroll-behavior: contain;\n\n    &:focus-visible {\n      outline: 2px solid var(--focus-border-color, var(--primary-color));\n      outline-offset: -2px;\n    }\n  }\n\n  /* An auto margin rather than justify-content: flex-end, which would put an overflow above the\n     top where it can never be scrolled to. Keeps a short conversation at the bottom, by the composer. */\n  &-content {\n    display: flex;\n    flex-direction: column;\n    margin-top: auto;\n    padding: 0.75em;\n  }\n\n  /* A fixed height, so the loader showing up inside it moves nothing. */\n  &-loader {\n    display: flex;\n    flex: none;\n    align-items: center;\n    justify-content: center;\n    height: 2.5em;\n  }\n}\n\n/* Two classes: it is an ActionIcon, whose own one-class rules it would otherwise tie with. */\n.chatMessageList .chatMessageList-scrollButton {\n  position: absolute;\n  inset-inline-end: 0.75em;\n  bottom: 0.75em;\n  width: 2.5em;\n  height: 2.5em;\n  border-radius: 50%;\n  box-shadow: var(--card-shadow);\n}\n\n/* A Plain button over the messages needs a surface of its own. Once something new has arrived\n   below it is Filled, and keeps Button's fill. */\n.chatMessageList .chatMessageList-scrollButton-surface {\n  background: var(--background-card-elevated, var(--background));\n}\n","import { useCallback, useImperativeHandle, useLayoutEffect, useRef, useState, type ReactElement } from 'react';\nimport cx from 'classnames';\nimport { faArrowDown } from '@fortawesome/free-solid-svg-icons';\n\nimport ActionIcon from '../actionIcon';\nimport { ButtonStyleType } from '../button/button.types';\nimport Loader from '../loader';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport { useInfiniteScroll } from '../infiniteBlock/useInfiniteScroll';\nimport { ThemeColor } from '../themeProvider/color.types';\nimport type { ChatMessageListLabels, ChatMessageListProps } from './chatMessageList.types';\nimport styles from './chatMessageList.module.pcss';\n\nconst DEFAULT_LABELS: Required<ChatMessageListLabels> = {\n  messages: 'Messages',\n  scrollToBottom: 'Jump to latest',\n};\n\n/** How close to the bottom a user scrolling down gets before new messages are followed again. */\nconst STICK_DISTANCE = 32;\n\ninterface Anchor {\n  element: Element;\n  /** Distance from the viewport's top edge. */\n  top: number;\n}\n\n/**\n * The first message still showing at the top of the viewport, found by bisection because a long\n * conversation has thousands of children and this runs on every scroll event.\n */\nfunction findAnchor(viewport: HTMLElement, content: HTMLElement, skip: Element | null): Anchor | null {\n  const viewportTop = viewport.getBoundingClientRect().top;\n  const items = content.children;\n  let low = items[0] === skip ? 1 : 0;\n  let high = items.length - 1;\n  let found: Element | null = null;\n  while (low <= high) {\n    const middle = (low + high) >> 1;\n    if (items[middle].getBoundingClientRect().bottom > viewportTop) {\n      found = items[middle];\n      high = middle - 1;\n    } else {\n      low = middle + 1;\n    }\n  }\n  return found && { element: found, top: found.getBoundingClientRect().top - viewportTop };\n}\n\nfunction prefersReducedMotion(): boolean {\n  return typeof window.matchMedia === 'function' && window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n}\n\nfunction ChatMessageList(props: ChatMessageListProps): ReactElement {\n  const {\n    children,\n    onLoadMore,\n    ref,\n    labels,\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    ...rest\n  } = useComponentDefaults('ChatMessageList', props);\n  const text = useLabels('ChatMessageList', labels, DEFAULT_LABELS);\n  const viewportRef = useRef<HTMLDivElement>(null);\n  const contentRef = useRef<HTMLDivElement>(null);\n  const isStuckRef = useRef(true);\n  const lastScrollTopRef = useRef(0);\n  const anchorRef = useRef<Anchor | null>(null);\n  const lastChildRef = useRef<Element | null>(null);\n  const [isAway, setIsAway] = useState(false);\n  const [hasNew, setHasNew] = useState(false);\n  const { isLoading, sentinelRef } = useInfiniteScroll(viewportRef, onLoadMore);\n\n  const handleScroll = () => {\n    const viewport = viewportRef.current;\n    const content = contentRef.current;\n    if (!viewport || !content) return;\n    const { scrollTop, scrollHeight, clientHeight } = viewport;\n    const distance = scrollHeight - scrollTop - clientHeight;\n    // Any move up lets go, however small: a slow drag would otherwise be pulled back down by each\n    // line of a streaming answer. The browser clamping scrollTop as content shrinks also moves it\n    // up, but lands on the bottom, which is why the distance has to be checked too.\n    if (scrollTop < lastScrollTopRef.current && distance > 1) isStuckRef.current = false;\n    else if (distance <= STICK_DISTANCE) isStuckRef.current = true;\n    lastScrollTopRef.current = scrollTop;\n    setIsAway(!isStuckRef.current);\n    if (isStuckRef.current) setHasNew(false);\n    anchorRef.current = findAnchor(viewport, content, sentinelRef.current);\n  };\n\n  // Scroll anchoring by hand: Safari has no `overflow-anchor`, and where it exists it does nothing\n  // at scrollTop 0, which is exactly where older messages get prepended. Runs in a ResizeObserver\n  // callback, after layout and before paint, so neither the jump nor its correction is ever seen.\n  useLayoutEffect(() => {\n    const viewport = viewportRef.current;\n    const content = contentRef.current;\n    if (!viewport || !content) return undefined;\n\n    const follow = () => {\n      // A new last child is a message arriving; one prepended at the top or growing as it streams\n      // leaves it the same.\n      const last = content.lastElementChild;\n      if (!isStuckRef.current && last !== lastChildRef.current) setHasNew(true);\n      lastChildRef.current = last;\n      if (isStuckRef.current) {\n        viewport.scrollTop = viewport.scrollHeight;\n      } else {\n        const anchor = anchorRef.current;\n        if (anchor?.element.isConnected) {\n          viewport.scrollTop +=\n            anchor.element.getBoundingClientRect().top - viewport.getBoundingClientRect().top - anchor.top;\n        }\n      }\n      // So the scroll event this causes is not taken for the user scrolling up.\n      lastScrollTopRef.current = viewport.scrollTop;\n      anchorRef.current = findAnchor(viewport, content, sentinelRef.current);\n    };\n\n    follow();\n    // The viewport too: a composer growing or a phone keyboard opening shrinks it from below.\n    const observer = new ResizeObserver(follow);\n    observer.observe(viewport);\n    observer.observe(content);\n    return () => observer.disconnect();\n  }, [sentinelRef]);\n\n  const scrollToBottom = useCallback(() => {\n    const viewport = viewportRef.current;\n    if (!viewport) return;\n    isStuckRef.current = true;\n    setIsAway(false);\n    setHasNew(false);\n    viewport.scrollTo({ top: viewport.scrollHeight, behavior: prefersReducedMotion() ? 'auto' : 'smooth' });\n  }, []);\n\n  useImperativeHandle(ref, () => ({ scrollToBottom }), [scrollToBottom]);\n\n  return (\n    <div {...rest} className={cx(styles.chatMessageList, className)} style={style}>\n      <div\n        ref={viewportRef}\n        role='log'\n        aria-label={text.messages}\n        aria-busy={isLoading || undefined}\n        // A stop of its own, so the history can be scrolled from the keyboard between messages\n        // that hold nothing focusable.\n        tabIndex={0}\n        className={cx(styles.chatMessageListViewport, classNames?.viewport)}\n        style={slotStyles?.viewport}\n        onScroll={handleScroll}\n      >\n        <div\n          ref={contentRef}\n          className={cx(styles.chatMessageListContent, classNames?.content)}\n          style={slotStyles?.content}\n        >\n          {onLoadMore && (\n            <div ref={sentinelRef} className={styles.chatMessageListLoader}>\n              {isLoading && <Loader size='1.5em' />}\n            </div>\n          )}\n          {children}\n        </div>\n      </div>\n      {isAway && (\n        <ActionIcon\n          icon={faArrowDown}\n          styleType={hasNew ? ButtonStyleType.Filled : ButtonStyleType.Plain}\n          color={hasNew ? ThemeColor.Primary : ThemeColor.Neutral}\n          aria-label={text.scrollToBottom}\n          title={text.scrollToBottom}\n          className={cx(\n            styles.chatMessageListScrollButton,\n            !hasNew && styles.chatMessageListScrollButtonSurface,\n            classNames?.scrollButton,\n          )}\n          style={slotStyles?.scrollButton}\n          onClick={scrollToBottom}\n        />\n      )}\n    </div>\n  );\n}\n\nexport default ChatMessageList;\n",".checkbox {\n  --custom-checkbox-size: var(--checkbox-size, 1.375em);\n  --custom-checkbox-gap: var(--checkbox-gap, 0.5em);\n\n  user-select: none;\n  display: flex;\n  align-items: center;\n  column-gap: var(--custom-checkbox-gap);\n  margin: -1px;\n  border: 1px solid transparent;\n  width: fit-content;\n\n  /* The visible checkmark box (below) is nested inside this root, not directly on it — see\n     input.module.pcss for why this variable exists: a wrapping ButtonGroup sets --group-radius-*\n     per corner on the position this occupies, and unlike border-radius itself, custom properties\n     inherit through any nesting depth, reaching descendants a plain ancestor selector can't. */\n  --checkmark-radius: var(--group-radius-tl, var(--default-border-radius))\n    var(--group-radius-tr, var(--default-border-radius)) var(--group-radius-br, var(--default-border-radius))\n    var(--group-radius-bl, var(--default-border-radius));\n\n  &-icon {\n    color: var(--text-on-primary);\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    border-radius: 6px;\n    background-color: var(--light-background-color);\n    width: fit-content;\n    height: fit-content;\n  }\n\n  &-loading {\n    background: transparent;\n\n    /* The spinner fills its box, so the box has to carry the checkmark's own size. */\n    &-box {\n      width: var(--custom-checkbox-size);\n      height: var(--custom-checkbox-size);\n    }\n  }\n\n  &-pointer {\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n  }\n\n  &:focus-visible {\n    outline: none;\n    border: 1px solid var(--focus-border-color, var(--primary-color));\n    border-radius: 6px;\n  }\n\n  &-disabled {\n    filter: brightness(0.8);\n    cursor: not-allowed;\n  }\n\n  &-checkmark {\n    width: var(--custom-checkbox-size);\n    height: var(--custom-checkbox-size);\n    border: 0.1125em solid var(--border-color);\n    border-radius: var(--checkmark-radius);\n    position: relative;\n\n    /* The mark only. The fill is the box's own background below, which is what keeps the\n       checked and empty states the same size: an svg sized to be the fill sat proud of it. */\n    svg {\n      position: absolute;\n      inset: 0;\n      width: 100%;\n      height: 100%;\n      box-sizing: border-box;\n      padding: 0.125em;\n      stroke: var(--text-on-primary);\n      transition: transform 0.1s;\n      transform: scale(0);\n    }\n\n    /* Tick and dash are two paths, so a dash leaves as a dash rather than as a tick. One leaves in a\n       single step at 0.1s, the svg's own scale time, so it has scaled away on an uncheck, and the\n       other starts drawing at that moment on a switch: erasing it gradually crossed the two. */\n    path {\n      stroke-dasharray: 5;\n      stroke-dashoffset: 5;\n      transition: stroke-dashoffset 0s 0.1s;\n    }\n\n    &-checked,\n    &-indeterminate {\n      background: var(--checkbox-fill, var(--primary-color));\n      border-color: var(--checkbox-fill, var(--primary-color));\n\n      svg {\n        transform: scale(1);\n      }\n    }\n\n    &-checked .checkbox-tick,\n    &-indeterminate .checkbox-dash {\n      stroke-dashoffset: 0;\n      transition: stroke-dashoffset 0.25s 0.1s;\n    }\n  }\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .checkbox-checkmark svg,\n  .checkbox-checkmark path {\n    transition: none;\n  }\n}\n","import { useRef, useEffect, useState, type ReactElement } from 'react';\nimport cx from 'classnames';\nimport Ripple from 'components/common/ripple';\nimport Loader from '../loader';\nimport { useComponentDefaults } from '../configProvider';\n\nimport type { CheckboxProps } from './checkbox.types';\nimport styles from './checkbox.module.pcss';\n\nfunction Checkbox(props: CheckboxProps): ReactElement {\n  const {\n    label = '',\n    selected = false,\n    indeterminate = false,\n    onToggle = () => {},\n    className = '',\n    style,\n    disabled = false,\n    lineHeight = '',\n    title = '',\n    tabIndex = 0,\n    cursorDefault = false,\n    width,\n    height,\n    formValidator = null,\n    children = null,\n    background = null,\n    isLoading = false,\n    classNames,\n    styles: slotStyles,\n    ...rest\n  } = useComponentDefaults('Checkbox', props);\n  const rippleRef = useRef<{ addRipple: () => void }>(null);\n  const [, forceUpdate] = useState(0);\n\n  useEffect(() => {\n    if (!formValidator) return;\n    const listener = () => forceUpdate((n) => n + 1);\n    formValidator.registerOnUpdateListener(listener);\n    return () => formValidator.removeOnUpdateListener(listener);\n  }, [formValidator]);\n\n  function handleToggle(event?: React.MouseEvent | React.KeyboardEvent) {\n    if (disabled || isLoading) return;\n    rippleRef.current?.addRipple();\n    if (formValidator) {\n      formValidator.set(!formValidator.value);\n    }\n    onToggle(!selected, event);\n  }\n\n  const currSelected = (formValidator ? formValidator.value : selected) as boolean;\n\n  return (\n    <div\n      {...rest}\n      className={cx(\n        styles.checkbox,\n        {\n          [styles.checkboxDisabled]: disabled,\n          [styles.checkboxPointer]: !cursorDefault,\n        },\n        className,\n      )}\n      /* Through the variable, so the border and the spinner follow width too, not just the box. */\n      style={{ ...(width ? ({ '--checkbox-size': width } as React.CSSProperties) : null), ...style }}\n      onClick={handleToggle}\n      onKeyPress={(e) => {\n        if (e.key === 'Enter') handleToggle(e);\n      }}\n      role='checkbox'\n      aria-checked={indeterminate ? 'mixed' : currSelected}\n      aria-disabled={disabled || undefined}\n      aria-busy={isLoading || rest['aria-busy'] || undefined}\n      tabIndex={tabIndex}\n    >\n      <div\n        title={title}\n        className={cx(styles.checkboxIcon, isLoading ? styles.checkboxLoading : null, classNames?.icon)}\n        style={slotStyles?.icon}\n      >\n        {isLoading ? (\n          <div className={styles.checkboxLoadingBox} style={{ width, height }}>\n            {/* Hidden: a checkbox takes its name from its content, and would be read as \"Loading <label>\". */}\n            <Loader size='100%' aria-hidden />\n          </div>\n        ) : (\n          <div\n            style={{\n              width,\n              height,\n              ...(background ? ({ '--checkbox-fill': background } as React.CSSProperties) : null),\n              ...slotStyles?.checkmark,\n            }}\n            className={cx(\n              styles.checkboxCheckmark,\n              currSelected && !indeterminate && styles.checkboxCheckmarkChecked,\n              indeterminate && styles.checkboxCheckmarkIndeterminate,\n              classNames?.checkmark,\n            )}\n          >\n            <Ripple\n              ref={rippleRef}\n              manualOnly\n              overflowHidden={false}\n              rippleAnimationLength={0.3}\n              rippleBackground='radial-gradient(transparent, var(--primary-color))'\n            />\n            <svg\n              strokeLinejoin='round'\n              strokeLinecap='round'\n              xmlns='http://www.w3.org/2000/svg'\n              viewBox='-0.5 -1.5 4 2.6'\n            >\n              <path className={styles.checkboxTick} d='M 0 0 l 1 1 l 2 -2' strokeWidth='0.4' fill='none' />\n              {/* On the viewBox's centre line, y = -1.5 + 2.6 / 2. */}\n              <path className={styles.checkboxDash} d='M 0.5 -0.2 h 2' strokeWidth='0.4' fill='none' />\n            </svg>\n          </div>\n        )}\n      </div>\n      <div className={classNames?.label} style={{ lineHeight: lineHeight || '100%', ...slotStyles?.label }}>\n        {children || label}\n      </div>\n    </div>\n  );\n}\n\nexport default Checkbox;\n","import type { HtmlProps, SlotClassNames, SlotStyles } from '../types/slots.types';\nimport type { ColorValue } from '../themeProvider/color.types';\n\n/** How much the chip stands out. Independent of its colour. */\nexport enum ChipStyleType {\n  /** Solid fill. */\n  Filled = 'filled',\n  /** A light tint of the colour behind text in the colour. The default. */\n  Light = 'light',\n  /** Border and text in the colour, no fill. */\n  Outlined = 'outlined',\n}\n\n/** Text Chip renders of its own, for translating it. */\nexport interface ChipLabels {\n  /** The remove button, read out by screen readers (default `Remove`). */\n  remove?: string;\n}\n\nexport type ChipSlots = 'root' | 'inner' | 'thumbnail' | 'text' | 'removeIcon';\n\nexport interface ChipProps extends HtmlProps {\n  className?: string;\n  text?: string;\n  thumbnail?: React.ReactNode;\n  styleType?: ChipStyleType;\n  /** A `ThemeColor` (default `Neutral`) or any CSS colour, such as `'var(--brand)'`. */\n  color?: ColorValue;\n  showRemoveIcon?: boolean;\n  /** Accessible name for the remove button. Default `'Remove'`. */\n  onClick?: () => void;\n  onRemove?: () => void;\n  /** Overrides the text Chip renders of its own. Merged per key. */\n  labels?: ChipLabels;\n  /** Classes for the inner elements. See `ChipSlots`. */\n  classNames?: SlotClassNames<ChipSlots>;\n  /** Inline styles for the inner elements. See `ChipSlots`. */\n  styles?: SlotStyles<ChipSlots>;\n}\n\nexport interface ChipContainerProps extends HtmlProps {\n  className?: string;\n  children?: React.ReactNode;\n  inline?: boolean;\n}\n",".chip {\n  border-radius: 99999px;\n  width: fit-content;\n  position: relative;\n  box-sizing: border-box;\n  max-width: 100%;\n\n  &-clickable {\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n  }\n\n  &-style {\n    &-filled {\n      background: var(--tone-fill);\n\n      /* Must follow the shorthand above, which resets background-image. */\n      background-image: var(--tone-gradient);\n      color: var(--tone-contrast);\n    }\n\n    &-light {\n      background: var(--tone-light);\n      color: var(--tone-text);\n    }\n\n    /* An inset shadow rather than a border, so an outlined chip is exactly as big as the rest. */\n    &-outlined {\n      box-shadow: inset 0 0 0 1px var(--tone-outline);\n      color: var(--tone-text);\n    }\n  }\n\n  &-inner {\n    display: flex;\n    gap: 8px;\n    align-items: center;\n    padding: 4px 8px;\n\n    & > * {\n      white-space: nowrap;\n    }\n  }\n\n  &-text {\n    text-overflow: ellipsis;\n    overflow: hidden;\n  }\n\n  &-thumbnail {\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    width: 18px;\n    height: 18px;\n    background: rgb(0 0 0 / 8%);\n    border-radius: 18px;\n    overflow: hidden;\n    background-size: cover;\n  }\n\n  &-container {\n    display: flex;\n    gap: 8px;\n    flex-wrap: wrap;\n\n    &-inline {\n      flex-wrap: nowrap;\n      overflow: auto;\n    }\n  }\n}\n","import React, { useMemo, useRef } from 'react';\nimport { faXmarkCircle } from '@fortawesome/free-solid-svg-icons';\nimport cx from 'classnames';\n\nimport Ripple from '../ripple';\nimport type { RippleHandle } from '../ripple';\nimport InteractableDiv from '../interactableDiv';\nimport ActionIcon from '../actionIcon';\nimport { ChipStyleType, ChipProps } from './chip.types';\nimport type { ChipLabels } from './chip.types';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport { isSelfActivation } from '../utils/activationKey';\nimport { ThemeColor } from '../themeProvider/color.types';\nimport { toneProps, useToneCheck } from '../styles/tone';\n\nimport styles from './chip.module.pcss';\n\nconst CLASS_BY_STYLE: Record<ChipStyleType, string> = {\n  filled: styles.chipStyleFilled,\n  light: styles.chipStyleLight,\n  outlined: styles.chipStyleOutlined,\n};\n\nconst DEFAULT_LABELS: Required<ChipLabels> = {\n  remove: 'Remove',\n};\n\nfunction Chip(props: ChipProps) {\n  const {\n    className = '',\n    thumbnail = '',\n    text = '',\n    styleType = ChipStyleType.Light,\n    color = ThemeColor.Neutral,\n    showRemoveIcon = false,\n    labels,\n    onClick,\n    onRemove,\n    style,\n    classNames,\n    styles: slotStyles,\n    ...rest\n  } = useComponentDefaults('Chip', props);\n  const resolvedLabels = useLabels('Chip', labels, DEFAULT_LABELS);\n  const rippleRef = useRef<RippleHandle>(null);\n  const tone = toneProps(color);\n  useToneCheck('Chip', [color]);\n\n  // Keyboard activation calls onClick directly without dispatching a DOM click, so Ripple's own\n  // listener never sees it.\n  function handleKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {\n    if (onClick && isSelfActivation(event)) rippleRef.current?.addRipple();\n  }\n\n  const thumbnailUrl = useMemo(() => {\n    if (typeof thumbnail === 'string' && thumbnail.startsWith('http')) return thumbnail;\n    return undefined;\n  }, [thumbnail]);\n\n  return (\n    <InteractableDiv\n      {...rest}\n      className={cx(\n        styles.chip,\n        tone.className,\n        CLASS_BY_STYLE[styleType],\n        onClick && styles.chipClickable,\n        className,\n        classNames?.root,\n      )}\n      style={{ ...tone.style, ...style, ...slotStyles?.root }}\n      onClick={onClick}\n      onKeyDown={handleKeyDown}\n    >\n      <Ripple ref={rippleRef} disabled={!onClick} style={{ borderRadius: 'inherit' }}>\n        <div className={cx(styles.chipInner, classNames?.inner)} style={slotStyles?.inner}>\n          {thumbnail && (\n            <div\n              className={cx(styles.chipThumbnail, classNames?.thumbnail)}\n              style={{\n                ...(thumbnailUrl ? { backgroundImage: `url(${thumbnailUrl})` } : {}),\n                ...slotStyles?.thumbnail,\n              }}\n            >\n              {!thumbnailUrl && thumbnail}\n            </div>\n          )}\n          <div className={cx(styles.chipText, classNames?.text)} style={slotStyles?.text}>\n            {text}\n          </div>\n          {showRemoveIcon && (\n            <ActionIcon\n              icon={faXmarkCircle}\n              className={classNames?.removeIcon}\n              style={slotStyles?.removeIcon}\n              aria-label={resolvedLabels.remove}\n              onClick={(e) => {\n                e.stopPropagation();\n                onRemove?.();\n              }}\n            />\n          )}\n        </div>\n      </Ripple>\n    </InteractableDiv>\n  );\n}\n\nexport default Chip;\n","import type { ReactElement } from 'react';\nimport cx from 'classnames';\n\nimport { ChipContainerProps } from './chip.types';\nimport styles from './chip.module.pcss';\n\nfunction ChipContainer({\n  className = undefined,\n  children = null,\n  inline = false,\n  ...rest\n}: ChipContainerProps): ReactElement {\n  return (\n    <div {...rest} className={cx(styles.chipContainer, inline && styles.chipContainerInline, className)}>\n      {children}\n    </div>\n  );\n}\n\nexport default ChipContainer;\n","/* Both bubbles are portaled to the end of the document and placed by\n   `usePopoverPosition`, which writes `top`, `left`, `visibility` and\n   `data-placement` straight onto them. */\n.tooltipBubble,\n.tooltipError {\n  position: fixed;\n\n  /* Shared by every overlay, so the order they are portaled in, which is the order they opened in, decides. */\n  z-index: 1;\n  width: max-content;\n\n  /* So a max-width is the width you actually get, padding included. Without it the\n     screen cap below let the box out by its own padding. */\n  box-sizing: border-box;\n\n  /* Hidden until the first coordinates are written, to avoid a top-left flash. */\n  visibility: hidden;\n}\n\n.tooltipBubble {\n  /* Resolved on the bubble so it, its arrow and the badge cannot drift apart. The\n     default is an inverse of the theme, not a fixed slate: a tooltip has to separate\n     from the page it floats over, and a dark bubble sat almost invisibly on a dark\n     theme. */\n  --custom-tooltip-background: var(--tooltip-background, color-mix(in oklab, var(--text-color) 82%, var(--background)));\n  --custom-tooltip-color: var(--tooltip-color, var(--background));\n\n  /* The lengths here are em against this bubble's own 0.75em, so the ratios are of 12px,\n     not 16, and max-width is a line measure rather than a pixel cap. */\n  border-radius: 0.25em;\n\n  /* Padding included, per the border-box above. The second term is the screen, with\n     the gutter `usePopoverPosition` keeps when it shifts a panel back on-screen: a\n     bubble wider than that cannot be clamped into view, only pushed off one edge,\n     so it wraps instead. */\n  max-width: min(16.667em, calc(100vw - 16px));\n  font-size: 0.75em;\n  background-color: var(--custom-tooltip-background);\n  padding: 0.5em 0.75em;\n  color: var(--custom-tooltip-color);\n  pointer-events: none;\n\n  &::after {\n    content: '';\n    position: absolute;\n    top: 100%;\n\n    /* The bubble's leading edge is the trigger's, so half the trigger's width is the\n       trigger's centre. The second term must stay half the arrow's own width, or it\n       stops pointing at it. */\n    left: calc(var(--tooltip-trigger-width, 10px) / 2 - 0.4167em);\n    border-width: 0.4167em;\n    border-style: solid;\n    border-color: var(--custom-tooltip-background) transparent transparent;\n  }\n\n  &[data-placement='bottom']::after {\n    top: auto;\n    bottom: 100%;\n    border-color: transparent transparent var(--custom-tooltip-background);\n  }\n}\n\n.tooltipDetails {\n  /* Repeated from .tooltipBubble above: the badge is a second root, rendered without one. */\n  --custom-tooltip-background: var(--tooltip-background, color-mix(in oklab, var(--text-color) 82%, var(--background)));\n  --custom-tooltip-color: var(--tooltip-color, var(--background));\n\n  position: relative;\n  display: inline-flex;\n  align-items: center;\n\n  &-icon {\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n\n    /* The theme's base, not a multiple of the font below: that landed a fraction of a pixel short. */\n    width: var(--default-font-size);\n    height: var(--default-font-size);\n    border-radius: 50%;\n\n    /* calc, not em: this badge renders inline wherever the consumer puts it, so em would\n       measure that text. The bubble above can use em, because BodyEnd sets the theme's own\n       font size on everything it portals. */\n    font-size: calc(var(--default-font-size) * 0.6875);\n    font-weight: 700;\n    background-color: var(--custom-tooltip-background);\n    color: var(--custom-tooltip-color);\n    cursor: default;\n    user-select: none;\n  }\n}\n\n.tooltipError {\n  border-radius: 0.3em;\n\n  /* No measure of its own: a validator's message is as long as it is. The screen is\n     still the limit, though, the same one the bubble above takes. */\n  max-width: calc(100vw - 16px);\n  background-color: var(--error-lighter);\n  color: var(--error-color);\n  font-size: 0.625em;\n  padding: 0.6em 0.9em;\n\n  &::after {\n    content: '';\n    position: absolute;\n    top: 100%;\n    right: 1.2em;\n    border-width: 0.5em;\n    border-style: solid;\n    border-color: var(--error-border-color) transparent transparent transparent;\n  }\n\n  &[data-placement='bottom']::after {\n    top: auto;\n    bottom: 100%;\n    border-color: transparent transparent var(--error-border-color) transparent;\n  }\n}\n","import React, { useEffect, useId, useLayoutEffect, useRef, useState } from 'react';\nimport cx from 'classnames';\nimport BodyEnd from 'components/common/bodyEnd';\nimport { useLabels } from 'components/common/configProvider';\nimport { usePopoverPosition } from '../popover/usePopoverPosition';\nimport { PopoverAlign, PopoverPlacement } from '../popover/popover.types';\nimport { isSelfActivation } from '../utils/activationKey';\nimport styles from './tooltip.module.pcss';\nimport type { TooltipLabels, TooltipProps } from './tooltip.types';\n\n/** The gap the arrow sits in, between the bubble and the trigger it points at. */\nconst TOOLTIP_OFFSET = 6;\n\nconst DEFAULT_LABELS: Required<TooltipLabels> = { details: 'More information' };\n\nexport default function Tooltip({\n  variant,\n  message = '',\n  isVisible = true,\n  tooltipPosition,\n  onClick,\n  labels,\n}: TooltipProps) {\n  const [detailsHovered, setDetailsHovered] = useState(false);\n  const text = useLabels('Tooltip', labels, DEFAULT_LABELS);\n\n  const sentinelRef = useRef<HTMLSpanElement>(null);\n  const detailsRef = useRef<HTMLDivElement>(null);\n  const triggerRef = useRef<HTMLElement | null>(null);\n  const bubbleRef = useRef<HTMLDivElement>(null);\n  // The bubble draws its own arrow in CSS, pointed by `--tooltip-trigger-width`,\n  // rather than taking Popover's separate arrow element.\n  const arrowRef = useRef<HTMLElement | null>(null);\n  const [triggerWidth, setTriggerWidth] = useState(10);\n  const bubbleId = useId();\n\n  const shown = variant === 'details' ? detailsHovered : isVisible;\n\n  useLayoutEffect(() => {\n    triggerRef.current = variant === 'details' ? detailsRef.current : (sentinelRef.current?.parentElement ?? null);\n  });\n\n  // The trigger is whatever element the consumer happened to put the Tooltip inside, so the\n  // description can only be attached to it from here, imperatively. Removed again when the\n  // bubble goes, since aria-describedby pointing at a missing id reads as no description at all.\n  useEffect(() => {\n    const trigger = triggerRef.current;\n    if (!trigger || !shown) return;\n    trigger.setAttribute('aria-describedby', bubbleId);\n    return () => trigger.removeAttribute('aria-describedby');\n  }, [shown, bubbleId]);\n\n  useLayoutEffect(() => {\n    if (shown) {\n      const rect = triggerRef.current?.getBoundingClientRect();\n      setTriggerWidth(rect?.width ?? 10);\n    }\n  }, [shown]);\n\n  usePopoverPosition({\n    isOpen: shown,\n    placement: PopoverPlacement.Top,\n    // An error hangs off the trailing edge of the field it belongs to; the other\n    // two off the leading edge, which is where their arrow is drawn.\n    align: variant === 'error' ? PopoverAlign.End : PopoverAlign.Start,\n    offset: TOOLTIP_OFFSET,\n    triggerRef,\n    floatingRef: bubbleRef,\n    panelRef: bubbleRef,\n    arrowRef,\n    // A bubble that slid off its trigger's edge to stay on screen would leave its\n    // arrow pointing at nothing. Wrapping one more line keeps both.\n    shrinkToFit: true,\n  });\n\n  const bubble = shown && (\n    <BodyEnd>\n      <div\n        ref={bubbleRef}\n        id={bubbleId}\n        // An error appears in answer to something the user just did, so it has to be announced;\n        // the other two only describe what is already focused.\n        role={variant === 'error' ? 'alert' : 'tooltip'}\n        className={cx(variant === 'error' ? styles.tooltipError : styles.tooltipBubble, tooltipPosition)}\n        style={{ '--tooltip-trigger-width': `${triggerWidth}px` } as React.CSSProperties}\n        onClick={onClick}\n      >\n        {message}\n      </div>\n    </BodyEnd>\n  );\n\n  if (variant === 'details') {\n    return (\n      <div ref={detailsRef} className={styles.tooltipDetails}>\n        <span\n          className={styles.tooltipDetailsIcon}\n          role='button'\n          tabIndex={0}\n          aria-label={text.details}\n          aria-expanded={detailsHovered}\n          onMouseEnter={() => setDetailsHovered(true)}\n          onMouseLeave={() => setDetailsHovered(false)}\n          // Hover alone would leave the icon unreachable without a pointer, and it is the only\n          // way to read what it explains.\n          onFocus={() => setDetailsHovered(true)}\n          onBlur={() => setDetailsHovered(false)}\n          onKeyDown={(event) => {\n            if (!isSelfActivation(event)) return;\n            event.preventDefault();\n            setDetailsHovered((open) => !open);\n          }}\n        >\n          ?\n        </span>\n        {bubble}\n      </div>\n    );\n  }\n\n  if (variant === 'info' || variant === 'error') {\n    return (\n      <>\n        <span ref={sentinelRef} style={{ display: 'none' }} />\n        {bubble}\n      </>\n    );\n  }\n\n  return null;\n}\n",".animatedIcon {\n  position: relative;\n  display: inline-block;\n  width: 1em;\n  height: 1em;\n\n  /* Fab's original technique: a single shared rotation on the whole icon, not per layer — both\n     the outgoing and incoming icon sit at the exact same angle at every instant, only their\n     opacity differs, which is what makes the spin read as one coherent piece rather than two\n     icons independently rotating past each other. Always a further full turn per step (not\n     Fab's 225°, which only worked there because its \"+\" icon looks identical rotated 45°) so an\n     arbitrary icon always lands back upright, no matter how many times it's toggled. A real\n     `transition`, so retapping mid-spin reverses smoothly from wherever it currently is instead\n     of snapping. */\n  &-spin {\n    transition: transform var(--animated-icon-duration, 200ms) ease;\n    transform: rotate(var(--animated-icon-spin-rotation, 0deg));\n\n    /* The wrapper alone carries the rotation in this variant — the two layers underneath it\n       only need to crossfade, not rotate individually. */\n    .animatedIcon-layer {\n      &:first-child,\n      &:last-child {\n        transform: none;\n      }\n    }\n  }\n\n  /* Two layers, always both mounted — never conditionally rendered. A `transition` only\n     retargets smoothly when the property changes on an element already on screen; a freshly-\n     mounted element with its \"final\" state applied in the same paint has no \"from\" state to\n     animate from. Keeping both layers permanently in the DOM (like Fab does) and only ever\n     toggling which one is `-front` is what makes this cancel cleanly mid-transition instead of\n     jumping or finishing a stale animation. */\n  &-layer {\n    position: absolute;\n    inset: 0;\n\n    /* FontAwesome's unlayered `height: 1em` outranks anything short of !important from inside the\n       library's layer, and has no variable to set instead. */\n    width: 100% !important;\n    height: 100% !important;\n    opacity: 0;\n    transition:\n      opacity var(--animated-icon-duration, 200ms) ease,\n      transform var(--animated-icon-duration, 200ms) ease;\n\n    /* Fixed per-layer resting pose (not per-toggle) — the first layer always approaches/leaves\n       through +90°, the second always through -90°, so which direction a given swap rotates in\n       depends only on which layer is taking over, the same way Fab's own icon always rotates\n       between two fixed angles rather than accumulating. */\n    &:first-child {\n      transform: rotate(90deg) scale(0.5);\n\n      &.animatedIcon-layer-front {\n        transform: none;\n      }\n    }\n\n    &:last-child {\n      transform: rotate(-90deg) scale(0.5);\n\n      &.animatedIcon-layer-front {\n        transform: none;\n      }\n    }\n\n    &-front {\n      opacity: 1;\n    }\n  }\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .animatedIcon-spin,\n  .animatedIcon-layer {\n    transition: none;\n  }\n}\n","import { useState } from 'react';\nimport type { CSSProperties } from 'react';\nimport cx from 'classnames';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport type { IconDefinition } from '@fortawesome/fontawesome-svg-core';\nimport type { AnimatedIconProps } from './animatedIcon.types';\n\nimport styles from './animatedIcon.module.pcss';\n\nfunction iconKey(icon: IconDefinition) {\n  return `${icon.prefix}:${icon.iconName}`;\n}\n\n// A full turn per step, not Fab's original 225° — 225° only looked right there because the\n// resting \"open\" icon (a \"+\") happens to look identical rotated 45° (it's 4-fold symmetric).\n// For arbitrary icon pairs a shared rotation has to land on a multiple of 360° every time, or\n// the new icon ends up sitting sideways.\nconst SPIN_DEGREES_PER_STEP = 360;\n\nfunction AnimatedIcon({\n  icon,\n  duration = 200,\n  spin = false,\n  className,\n  style,\n  onClick,\n  onMouseEnter,\n  onMouseLeave,\n  classNames,\n  styles: slotStyles,\n  ...rest\n}: AnimatedIconProps) {\n  // Two permanently-mounted layers, ping-ponging which one is the visible (\"front\") icon,\n  // instead of conditionally mounting/unmounting an \"outgoing\" layer. That's what lets this use\n  // a real CSS `transition` (which retargets smoothly from wherever it currently is) rather than\n  // a keyframe `animation` (which, once started on a freshly-mounted element, plays to\n  // completion regardless of what happens mid-flight): tap the toggle again before a transition\n  // finishes and it reverses smoothly from its current position — the same thing that already\n  // let Fab's own hand-rolled icon rotation cancel cleanly.\n  const [slots, setSlots] = useState<[IconDefinition, IconDefinition]>([icon, icon]);\n  const [activeIndex, setActiveIndex] = useState<0 | 1>(0);\n  const [spinStep, setSpinStep] = useState(0);\n\n  // Adjusting state during render (not in an effect) on a prop change: the documented React\n  // pattern for reacting to a prop that changed since the last render, without an extra tick.\n  if (iconKey(icon) !== iconKey(slots[activeIndex])) {\n    const nextIndex = activeIndex === 0 ? 1 : 0;\n    const nextSlots: [IconDefinition, IconDefinition] = [...slots];\n    nextSlots[nextIndex] = icon;\n    setSlots(nextSlots);\n    setActiveIndex(nextIndex);\n    if (spin) setSpinStep((step) => step + 1);\n  }\n\n  return (\n    <span\n      {...rest}\n      className={cx(styles.animatedIcon, { [styles.animatedIconSpin]: spin }, classNames?.root, className)}\n      style={\n        {\n          ...style,\n          ...slotStyles?.root,\n          '--animated-icon-duration': `${duration}ms`,\n          '--animated-icon-spin-rotation': `${spinStep * SPIN_DEGREES_PER_STEP}deg`,\n        } as CSSProperties & Record<'--animated-icon-duration' | '--animated-icon-spin-rotation', string>\n      }\n      onClick={onClick}\n      onMouseEnter={onMouseEnter}\n      onMouseLeave={onMouseLeave}\n    >\n      <FontAwesomeIcon\n        icon={slots[0]}\n        className={cx(\n          styles.animatedIconLayer,\n          { [styles.animatedIconLayerFront]: activeIndex === 0 },\n          classNames?.icon,\n        )}\n        style={slotStyles?.icon as CSSProperties & Record<`--fa-font-${string}`, string>}\n      />\n      <FontAwesomeIcon\n        icon={slots[1]}\n        className={cx(\n          styles.animatedIconLayer,\n          { [styles.animatedIconLayerFront]: activeIndex === 1 },\n          classNames?.icon,\n        )}\n        style={slotStyles?.icon as CSSProperties & Record<`--fa-font-${string}`, string>}\n      />\n    </span>\n  );\n}\n\nexport default AnimatedIcon;\n","/* Chrome fills a field on page load without firing anything React can see, and an animation\n   bound to the pseudo-class is the only hook it offers. The declarations are inert on purpose;\n   the pair exists to be listened for — see handleAutofillAnimation in input.tsx. */\n@keyframes inputAutofillStart {\n  from {\n    opacity: 1;\n  }\n}\n\n@keyframes inputAutofillEnd {\n  from {\n    opacity: 1;\n  }\n}\n\n.required-mark {\n  padding: 0 0 0 4px;\n  color: var(--required-mark-color, var(--error-color));\n}\n\n.input {\n  position: relative;\n  width: 100%;\n\n  --input-notch-height: 1.75em;\n  --input-icon-size: 1.2em;\n  --input-icon-offset: 0.6em;\n  --input-icon-gutter: 2.2em;\n\n  /* Where the text starts, and so where the placeholder and the resting label start too. */\n  --input-padding-inline: 7.5px;\n  --field-label-offset: 0px;\n\n  /* A floating label hangs about 4px into the field, so a value centred under it looked pressed\n     against it. Everything that centres on the field moves down by this, splitting the difference. */\n  &-withLabel {\n    --field-label-offset: 1px;\n  }\n\n  /* The visible border lives on `fieldset`/`container` below, not this root — buried inside a\n     `<label>`, out of reach of a plain ancestor selector. --group-radius-* are custom properties\n     (not defined here) that a wrapping ButtonGroup sets per corner on the position it occupies;\n     unlike border-radius itself they inherit through any nesting depth, so they reach these\n     descendants and flatten the right corners when this Input sits inside a ButtonGroup. */\n  --input-radius: var(--group-radius-tl, var(--default-border-radius))\n    var(--group-radius-tr, var(--default-border-radius)) var(--group-radius-br, var(--default-border-radius))\n    var(--group-radius-bl, var(--default-border-radius));\n\n  &-placeholder {\n    position: absolute;\n    top: calc(50% + var(--field-label-offset));\n    left: var(--input-padding-inline);\n    transform: translateY(-50%);\n    transition: all 0.2s;\n    opacity: 0;\n    user-select: none;\n    pointer-events: none;\n\n    &-shown {\n      animation: 0.2s fadeIn 0.3s forwards;\n    }\n\n    &-multiline {\n      top: 14px;\n      transform: translateY(0);\n    }\n  }\n\n  &-matLabel {\n    position: absolute;\n    transition: all 0.3s;\n    left: var(--input-padding-inline);\n    right: 0;\n    top: calc(50% + var(--field-label-offset));\n    transform: translateY(-50%);\n    font-size: 1em;\n    color: var(--input-label-resting-color, var(--text-light));\n    pointer-events: none;\n    user-select: none;\n\n    &-floating {\n      inset: 0 0 auto 14px;\n      font-size: 0.8em;\n      color: var(--input-label-floating-color, var(--text-color));\n      transform: translateY(-50%);\n    }\n\n    &-multiline {\n      top: 14px;\n      transform: translateY(0);\n    }\n  }\n\n  &-fieldset {\n    position: absolute;\n    width: 100%;\n    margin: 0;\n    padding: 0;\n    min-width: 0;\n    box-sizing: border-box;\n    border-radius: var(--input-radius);\n    font-size: 0.8em;\n    inset: calc(var(--input-notch-height) / -2) 0 0;\n    padding-left: 8px;\n    border-width: 1px;\n    border-style: solid;\n    border-color: var(--border-color);\n    pointer-events: none;\n    user-select: none;\n\n    &-legend {\n      display: block;\n\n      /* Not taken from the text: an empty span gets no line box, which raised a label-less\n         field's border half a notch. */\n      height: var(--input-notch-height);\n      max-width: 0;\n      transition: max-width 0.2s;\n      white-space: nowrap;\n      padding: 0;\n\n      &-active {\n        max-width: 300px;\n      }\n\n      span {\n        line-height: var(--input-notch-height);\n        padding: 0 5px;\n        opacity: 0;\n      }\n\n      span:empty {\n        padding: 0;\n      }\n    }\n  }\n\n  &-iconLeft {\n    .input-matLabel {\n      left: var(--input-icon-gutter);\n\n      &-floating {\n        left: calc(var(--input-icon-gutter) + 0.5em);\n      }\n    }\n\n    .input-placeholder {\n      left: var(--input-icon-gutter);\n    }\n\n    .input-fieldset {\n      padding-left: var(--input-icon-gutter);\n    }\n  }\n\n  &-container {\n    display: flex;\n    align-items: center;\n    border-radius: var(--input-radius);\n    background-color: var(--input-background);\n    transition: border-color;\n    cursor: text;\n    height: 100%;\n    box-sizing: border-box;\n    padding-top: calc(var(--field-label-offset) * 2);\n\n    /* The height lives here rather than in the input's line, which the HTML standard keeps at least\n       as tall as the font's own `normal`: in Firefox and Safari, Open Sans made the field 36px beside\n       a 34px Dropdown. The same formula as MultiSelect's min-height and NumberInput's split button. */\n    min-height: calc(1.25em + 14px);\n\n    &-multiline {\n      overflow: hidden;\n      padding-top: 10px;\n      align-items: flex-start;\n\n      /* Matches the textarea's padding-top, so they sit on its first line. */\n      .input-field-icon,\n      .input-field-suffix {\n        margin-top: 4px;\n      }\n    }\n\n    /* Not on the textarea, whose grip would sit beside the icons. The textarea's\n       align-self: stretch is what makes it follow. */\n    &-resizable {\n      resize: vertical;\n    }\n\n    /* On the container, not the field, so it reaches under the icons and the suffix too. */\n    &-disabled {\n      background-color: var(--disabled-input-color);\n    }\n\n    &-border {\n      transition: border-color;\n      background-color: var(--input-background);\n      outline: none;\n\n      &-focused {\n        outline-color: var(--focus-border-color, var(--primary-color));\n        border-color: var(--focus-border-color, var(--primary-color));\n      }\n\n      &-searchFocused {\n        outline-color: var(--search-input-color);\n        outline-width: 2px;\n        border-color: var(--search-input-color);\n      }\n\n      &-invalid {\n        outline-color: var(--error-border-color);\n        border-color: var(--error-border-color);\n      }\n\n      &-invalid:hover {\n        outline-color: var(--error-border-color);\n        border-color: var(--error-border-color);\n      }\n\n      &-error {\n        outline-color: var(--error-border-color);\n        border-color: var(--error-border-color);\n      }\n    }\n  }\n\n  &-label {\n    display: flex;\n    color: var(--text-color);\n    height: 100%;\n\n    &-column {\n      flex-direction: column;\n    }\n\n    &-text {\n      margin-right: 5px;\n      margin-bottom: 6px;\n      display: flex;\n      align-items: center;\n      color: color(var(--border-color) lightness(40%));\n      background-color: transparent;\n      font-size: 12px;\n      position: absolute;\n      top: -15px;\n      white-space: nowrap;\n    }\n  }\n\n  &-upper-right-label {\n    position: absolute;\n    transform: translateY(calc(-100% - 1.5px));\n    right: 8px;\n    min-height: 12px;\n    font-size: 12px;\n    color: color(var(--border-color) lightness(40%));\n  }\n\n  &-field {\n    appearance: none;\n    -webkit-appearance: none;\n    font-family: inherit;\n    font-size: inherit;\n    box-sizing: border-box;\n    resize: none;\n    width: 100%;\n    border: none;\n    border-radius: var(--default-border-radius);\n    overflow: hidden;\n    line-height: 1.25;\n    color: var(--text-color);\n    background: transparent;\n\n    /* A single-line input centres its own text, so filling the container puts that text on the\n       container's middle, where the icons, the suffix and the label centre too. */\n    align-self: stretch;\n\n    &:focus {\n      outline: none;\n    }\n\n    &::placeholder {\n      color: var(--text-light);\n    }\n\n    /* Chrome overrides `background` and `color` on an autofilled field outright. Clipping the\n       background to the glyphs is what removes it: the page cannot win on colour, but it can\n       say where a background paints, and -webkit-text-fill-color then beats the text colour.\n       The field is transparent either way, so what shows through is whatever is really behind\n       it — no surface has to declare itself, which an inset-shadow cover-up would have needed. */\n    &:-webkit-autofill,\n    &:-webkit-autofill:hover,\n    &:-webkit-autofill:focus,\n    &:-webkit-autofill:active {\n      animation-name: inputAutofillStart;\n      animation-duration: 1ms;\n      -webkit-background-clip: text;\n      background-clip: text;\n      -webkit-text-fill-color: var(--text-color);\n      caret-color: var(--text-color);\n    }\n\n    &:not(:-webkit-autofill) {\n      animation-name: inputAutofillEnd;\n      animation-duration: 1ms;\n    }\n\n    /* The browser's own search-cancel decoration doesn't match the theme — Search has its own\n       themed clear icon (see isSearchCleared in input.tsx) that replaces it. */\n    &::-webkit-search-cancel-button,\n    &::-webkit-search-decoration {\n      -webkit-appearance: none;\n      appearance: none;\n      display: none;\n    }\n\n    /* The left icon's margins carry the gap, so the text starts exactly at --input-icon-gutter,\n       where the label, the placeholder and the notch are drawn. */\n    &-withIcon-left {\n      padding-left: 0 !important;\n    }\n\n    &-suffix {\n      padding-right: 9px;\n    }\n\n    &-date {\n      padding: 0 9px;\n      color-scheme: dark;\n\n      svg {\n        color: var(--input-icon-color);\n      }\n    }\n\n    &-default {\n      padding: 0 var(--input-padding-inline);\n    }\n\n    &[disabled] {\n      color: var(--disabled-font-color);\n      -webkit-text-fill-color: var(--disabled-font-color);\n      opacity: 1;\n\n      &::placeholder {\n        color: var(--disabled-font-color);\n        -webkit-text-fill-color: var(--disabled-font-color);\n      }\n    }\n\n    &-readOnly {\n      cursor: default;\n      color: var(--text-dark);\n      -webkit-text-fill-color: var(--text-dark);\n    }\n\n    &-uppercase {\n      text-transform: uppercase;\n    }\n\n    &-multiline {\n      overflow-y: auto;\n      padding-top: 4px;\n      padding-bottom: 6px;\n      min-height: 30px;\n\n      &-autoResize {\n        overflow: hidden;\n      }\n    }\n\n    &-icon {\n      appearance: none;\n      -webkit-appearance: none;\n      box-sizing: border-box;\n      cursor: pointer;\n      -webkit-tap-highlight-color: transparent;\n      background-color: transparent;\n      border: none;\n      outline: none;\n      flex: none;\n      margin-right: var(--input-icon-offset);\n      color: var(--input-icon-color);\n      width: var(--input-icon-size) !important;\n      height: var(--input-icon-size) !important;\n      user-select: none;\n\n      &:focus-visible {\n        outline: 1px solid var(--focus-border-color, var(--primary-color));\n        outline-offset: 2px;\n      }\n\n      &-left {\n        margin-left: var(--input-icon-offset);\n        margin-right: calc(var(--input-icon-gutter) - var(--input-icon-offset) - var(--input-icon-size));\n      }\n\n      &-notClickable {\n        pointer-events: none;\n      }\n\n      &-clear {\n        transition: 0.15s ease-out;\n        transition-property: width, margin-right, opacity;\n\n        &-hidden {\n          width: 0 !important;\n          margin-right: 0;\n          opacity: 0;\n          visibility: hidden;\n\n          /* Only on the way out: visibility holds at visible until the fade has played. */\n          transition-property: width, margin-right, opacity, visibility;\n        }\n      }\n    }\n  }\n\n  &-error {\n    height: 1em;\n    padding-top: 3px;\n    color: var(--error-font-color);\n    font-size: 11px;\n  }\n\n  @keyframes fadeIn {\n    from {\n      opacity: 0;\n    }\n\n    to {\n      opacity: 0.5;\n    }\n  }\n}\n\n/* The label's glide between resting and floating is the motion; it still moves, just instantly.\n   The clear button keeps its fade, while the suffix beside it jumps instead of sliding. */\n@media (prefers-reduced-motion: reduce) {\n  .input-matLabel,\n  .input-placeholder {\n    transition: none;\n  }\n\n  .input-field-icon-clear {\n    transition-property: opacity, visibility;\n  }\n}\n","import React from 'react';\nimport type { FormValidator } from 'services/formValidation';\nimport type { SlotClassNames, SlotStyles, DataProps } from '../types/slots.types';\n\nexport enum InputType {\n  Edit = 'edit',\n  Phone = 'phone',\n  Email = 'email',\n  Iban = 'iban',\n  Date = 'date',\n  HasNoIcon = 'hasNoIcon',\n  Search = 'searchInput',\n  Password = 'password',\n}\n\nexport enum InputIconPosition {\n  Left = 'left',\n  Right = 'right',\n}\n\nexport type InputSlots = 'label' | 'container' | 'input' | 'suffix' | 'upperRightLabel' | 'fieldset';\n\ntype OverriddenInputAttributes =\n  | 'onChange'\n  | 'onFocus'\n  | 'onBlur'\n  | 'onMouseEnter'\n  | 'onMouseLeave'\n  | 'onClick'\n  | 'type'\n  | 'value'\n  | 'min'\n  | 'max'\n  | 'autoComplete';\n\n/** Text Input renders of its own, for translating it. */\nexport interface InputLabels {\n  /** Clear button on a search input, read out by screen readers. */\n  clear?: string;\n  /** Reveals a password, read out by screen readers. */\n  showPassword?: string;\n  /** Hides a password again. */\n  hidePassword?: string;\n  /** Clears a search input, read out by screen readers. */\n  clearSearch?: string;\n}\n\nexport interface InputProps\n  extends Omit<React.InputHTMLAttributes<HTMLInputElement>, OverriddenInputAttributes>, DataProps {\n  onMouseEnter?: ((event: React.MouseEvent) => void) | null;\n  onMouseLeave?: ((event: React.MouseEvent) => void) | null;\n  onFocus?: ((event: React.FocusEvent) => void) | null;\n  onBlur?: ((event: React.FocusEvent) => void) | null;\n  onChange?:\n    | ((value: string, event?: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement, Element>) => void)\n    | null;\n  errorMessage?: string;\n  isValid?: boolean;\n  isRequired?: boolean;\n  label?: string;\n  name?: string;\n  className?: string;\n  style?: React.CSSProperties;\n  value?: string | number;\n  icon?: React.ReactElement | null;\n  iconPosition?: InputIconPosition;\n  isUppercase?: boolean;\n  isRow?: boolean;\n  tabIndex?: number;\n  type?: InputType;\n  onIconClick?: (() => void) | null;\n  parse?: ((value: string) => string) | null;\n  suffix?: string | React.ReactNode;\n  disabled?: boolean;\n  upperRightLabel?: string | React.ReactNode;\n  labelInBorder?: boolean;\n  /** Show a button that clears the field, whenever it has a value it can change. */\n  clearable?: boolean;\n  /** Called after a clear button empties the field, by click or key. */\n  onClear?: (() => void) | null;\n  min?: string | number | null;\n  max?: string | number | null;\n  placeholder?: string;\n  useMatLabelStyle?: boolean;\n  alwaysFloatLabel?: boolean;\n  autoComplete?: string | null;\n  onClick?: (() => void) | null;\n  readOnly?: boolean;\n  customInput?: React.ReactElement | null;\n  autoFocus?: boolean;\n  formValidator?: FormValidator | null;\n  multiline?: boolean;\n  rows?: number;\n  autoResize?: boolean;\n  /**\n   * Ref to the rendered `<input>` (or `<textarea>` when `multiline`). Not forwarded to a\n   * `customInput`, which owns its own ref.\n   */\n  ref?: React.Ref<HTMLInputElement | HTMLTextAreaElement>;\n  classNames?: SlotClassNames<InputSlots>;\n  styles?: SlotStyles<InputSlots>;\n  /** Overrides the text Input renders of its own. Merged per key. */\n  labels?: InputLabels;\n}\n","import React, { useState, useEffect, useRef, useId } from 'react';\nimport cx from 'classnames';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport {\n  faPhone,\n  faPen,\n  faEnvelope,\n  faSearch,\n  faXmark,\n  faEye as passwordHiddenIcon,\n  faEyeSlash as passwordShownIcon,\n} from '@fortawesome/free-solid-svg-icons';\nimport type { IconDefinition } from '@fortawesome/fontawesome-svg-core';\nimport Tooltip from 'components/common/tooltip';\nimport AnimatedIcon from 'components/common/animatedIcon';\nimport { useComponentDefaults, useLabels } from 'components/common/configProvider';\nimport styles from './input.module.pcss';\nimport { mergeRefs } from '../utils/mergeRefs';\nimport { useAutoGrow } from '../hooks/useAutoGrow';\nimport type { InputLabels, InputProps } from './input.types';\nimport { InputType, InputIconPosition } from './input.types';\n\nconst DEFAULT_LABELS: Required<InputLabels> = {\n  clear: 'Clear',\n  showPassword: 'Show password',\n  hidePassword: 'Hide password',\n  clearSearch: 'Clear search',\n};\n\nconst TYPE_ICONS: Partial<Record<InputType, IconDefinition>> = {\n  [InputType.Phone]: faPhone,\n  [InputType.Email]: faEnvelope,\n  [InputType.Edit]: faPen,\n};\n\nfunction clickableIconProps(onActivate?: ((event: React.SyntheticEvent) => void) | null) {\n  if (!onActivate) return undefined;\n  return {\n    role: 'button' as const,\n    tabIndex: 0,\n    // The icon sits inside the <label>, whose click focuses the field anyway. Letting mousedown\n    // focus the icon first would hand the consumer a blur and a focus for a single click.\n    onMouseDown: (event: React.MouseEvent) => event.preventDefault(),\n    onKeyDown: (event: React.KeyboardEvent) => {\n      if (event.key !== 'Enter' && event.key !== ' ') return;\n      event.preventDefault();\n      onActivate(event);\n    },\n  };\n}\n\nfunction Input(props: InputProps) {\n  const {\n    name = '',\n    id,\n    label = '',\n    errorMessage = '',\n    isValid = true,\n    className = '',\n    style,\n    value = '',\n    icon = null,\n    iconPosition,\n    onChange = () => {},\n    onFocus: onFocusProp = null,\n    onBlur: onBlurProp = null,\n    onMouseEnter: onMouseEnterProp = null,\n    onMouseLeave: onMouseLeaveProp = null,\n    isUppercase = false,\n    isRequired = false,\n    tabIndex = 0,\n    type = undefined,\n    isRow = false,\n    onIconClick = null,\n    parse = null,\n    formValidator = null,\n    autoFocus = false,\n    suffix = null,\n    upperRightLabel = null,\n    labelInBorder = false,\n    clearable = false,\n    onClear,\n    min = null,\n    max = null,\n    placeholder = '',\n    useMatLabelStyle = true,\n    alwaysFloatLabel = false,\n    autoComplete = null,\n    onClick = () => null,\n    readOnly = false,\n    customInput,\n    multiline = false,\n    rows,\n    autoResize = false,\n    classNames,\n    styles: slotStyles,\n    disabled = false,\n    labels,\n    ref,\n    ...rest\n  } = useComponentDefaults('Input', props);\n  const text = useLabels('Input', labels, DEFAULT_LABELS);\n  const [isFocused, setIsFocused] = useState(false);\n  // Chrome fills a field on page load without firing anything React sees. The animation the\n  // stylesheet binds to :-webkit-autofill is the only notification it gives.\n  const [isAutofilled, setIsAutofilled] = useState(false);\n  const [isHovered, setIsHovered] = useState(false);\n  const [isPasswordShown, setIsPasswordShown] = useState(false);\n  const [, forceUpdate] = useState(0);\n\n  const inputRef = useRef<HTMLInputElement | HTMLTextAreaElement>(null);\n  const fvRef = useRef(formValidator);\n\n  const generatedId = useId();\n  const inputId = id || name || generatedId;\n\n  const currVal = formValidator ? formValidator.value : value;\n\n  useAutoGrow(inputRef, currVal, { enabled: autoResize });\n\n  useEffect(() => {\n    if (autoFocus && inputRef.current) {\n      setTimeout(() => {\n        inputRef.current?.focus();\n      }, 1);\n    }\n  }, [autoFocus]);\n\n  useEffect(() => {\n    fvRef.current = formValidator;\n    const fv = fvRef.current;\n    if (!fv) return;\n    fv.label = label ?? '';\n    fv.validate();\n    const listener = () => forceUpdate((n) => n + 1);\n    fv.registerOnUpdateListener(listener);\n    return () => {\n      fv.removeOnUpdateListener(listener);\n    };\n  }, [formValidator, label]);\n\n  const hasError = (): boolean => {\n    return formValidator ? formValidator.shouldShowError : !isValid || !!errorMessage;\n  };\n\n  const computedIsRequired = (): boolean => {\n    return !!(\n      isRequired ||\n      (formValidator &&\n        formValidator.validators.find((v) => (v as { validatorId?: string }).validatorId === 'required'))\n    );\n  };\n\n  const onMouseEnterHandler = (event: React.MouseEvent) => {\n    setIsHovered(true);\n    if (onMouseEnterProp) onMouseEnterProp(event);\n  };\n\n  const onMouseLeaveHandler = (event: React.MouseEvent) => {\n    setIsHovered(false);\n    if (onMouseLeaveProp) onMouseLeaveProp(event);\n  };\n\n  const onFocusHandler = (event: React.FocusEvent) => {\n    setIsFocused(true);\n    if (onFocusProp) onFocusProp(event);\n  };\n\n  const onBlurHandler = (event: React.FocusEvent) => {\n    if (fvRef.current) fvRef.current.touched = true;\n    setIsFocused(false);\n    if (onBlurProp) onBlurProp(event);\n  };\n\n  const onChangeHandler = (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement> | string): void => {\n    let parsedEvent: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement> | undefined;\n    let val: string;\n\n    if (typeof event !== 'string') {\n      parsedEvent = event;\n      val = event.target.value;\n    } else {\n      val = event;\n    }\n\n    if (parse) val = parse(val);\n    if (formValidator) formValidator.set(val);\n    if (onChange) onChange(val, parsedEvent);\n  };\n\n  const getInputType = (): string | undefined => {\n    if (type === InputType.Email) return undefined;\n    if (type === InputType.Phone) return 'tel';\n    if (type === InputType.Search) return 'search';\n    if (type === 'password' && isPasswordShown) return 'text';\n    return type;\n  };\n\n  const getInputMode = (): React.HTMLAttributes<HTMLInputElement>['inputMode'] => {\n    if (type === InputType.Email) return 'email';\n    if (type === InputType.Phone) return 'tel';\n    if (type === InputType.Search) return 'search';\n    return undefined;\n  };\n\n  const isFunctionalIconType = type === InputType.Search || type === InputType.Password;\n  const hasCustomIcon = !!icon;\n  const customIconApplies = hasCustomIcon && !isFunctionalIconType;\n\n  const defaultsLeftWithoutMatLabel = type === InputType.Email || type === InputType.Phone || type === InputType.Search;\n  const resolvedIconPosition =\n    iconPosition ??\n    (!useMatLabelStyle && defaultsLeftWithoutMatLabel ? InputIconPosition.Left : InputIconPosition.Right);\n  const iconIsLeft = resolvedIconPosition === InputIconPosition.Left;\n\n  const iconProps = {\n    className: cx(styles.inputFieldIcon, {\n      [styles.inputFieldIconNotClickable]: !onIconClick,\n      [styles.inputFieldIconLeft]: iconIsLeft,\n    }),\n    onClick: onIconClick ?? undefined,\n    ...clickableIconProps(onIconClick),\n  };\n  const typeIcon = type && !hasCustomIcon ? TYPE_ICONS[type] : undefined;\n\n  const customIcon = icon as React.ReactElement<{ className?: string; onClick?: React.MouseEventHandler }> | null;\n  const resolvedCustomIconClick = onIconClick ?? customIcon?.props.onClick;\n  const renderedIcon =\n    customIconApplies && customIcon\n      ? React.cloneElement(customIcon, {\n          className: cx(\n            styles.inputFieldIcon,\n            {\n              [styles.inputFieldIconNotClickable]: !resolvedCustomIconClick,\n              [styles.inputFieldIconLeft]: iconIsLeft,\n            },\n            customIcon.props.className,\n          ),\n          onClick: resolvedCustomIconClick,\n          ...clickableIconProps(resolvedCustomIconClick as ((event: React.SyntheticEvent) => void) | undefined),\n        })\n      : null;\n\n  const hasErr = hasError();\n\n  let editedLabel = label;\n  if (editedLabel && computedIsRequired()) editedLabel = `${editedLabel} *`;\n  const requiredMark = computedIsRequired() ? <span className={styles.requiredMark}>*</span> : null;\n\n  const showFocus = isFocused && !readOnly;\n  const shouldFloatLabel =\n    useMatLabelStyle && (alwaysFloatLabel || showFocus || currVal || isAutofilled || type === InputType.Date);\n\n  const handleAutofillAnimation = (event: React.AnimationEvent) => {\n    const { animationName } = event;\n    if (animationName === styles.inputAutofillStart) {\n      setIsAutofilled(true);\n      // The field is controlled, so the commit this triggers would write `value` back over\n      // what the browser just put there and blank it. Replay it as an ordinary change first.\n      const filled = (event.currentTarget as HTMLInputElement).value;\n      if (filled && filled !== String(currVal ?? '')) onChangeHandler(filled);\n      return;\n    }\n    if (animationName === styles.inputAutofillEnd) {\n      setIsAutofilled(false);\n      return;\n    }\n    // Not ours. `onAnimationStart` arrives through `rest`, and the consumer must not receive\n    // the two above: the `:not()` rule fires one of them on every mount.\n    rest.onAnimationStart?.(event as React.AnimationEvent<HTMLInputElement>);\n  };\n\n  const canClear = !!currVal && !disabled && !readOnly;\n  const isSearchCleared = type === InputType.Search && canClear;\n\n  function togglePassword() {\n    setIsPasswordShown(!isPasswordShown);\n  }\n\n  let fieldIcon: React.ReactNode = renderedIcon;\n  if (type === InputType.Search) {\n    fieldIcon = (\n      <AnimatedIcon\n        icon={isSearchCleared ? faXmark : faSearch}\n        className={cx(styles.inputFieldIcon, {\n          [styles.inputFieldIconNotClickable]: !isSearchCleared,\n          [styles.inputFieldIconLeft]: iconIsLeft,\n        })}\n        onClick={isSearchCleared ? handleClear : undefined}\n        {...clickableIconProps(isSearchCleared ? handleClear : undefined)}\n        aria-label={isSearchCleared ? text.clearSearch : undefined}\n      />\n    );\n  } else if (type === InputType.Password) {\n    fieldIcon = (\n      <FontAwesomeIcon\n        icon={isPasswordShown ? passwordShownIcon : passwordHiddenIcon}\n        className={cx(styles.inputFieldIcon, { [styles.inputFieldIconLeft]: iconIsLeft })}\n        onClick={togglePassword}\n        {...clickableIconProps(togglePassword)}\n        aria-label={isPasswordShown ? text.hidePassword : text.showPassword}\n      />\n    );\n  } else if (typeIcon) {\n    fieldIcon = <FontAwesomeIcon icon={typeIcon} {...iconProps} />;\n  }\n  const hasLeftIcon = !!fieldIcon && iconIsLeft;\n\n  const customInputProps = customInput?.props as { className?: string; style?: React.CSSProperties } | undefined;\n\n  function handleClear() {\n    if (formValidator) formValidator.set('');\n    onChange?.('');\n    onClear?.();\n    // A key press would otherwise leave focus on a button that is about to vanish.\n    inputRef.current?.focus();\n  }\n\n  return (\n    <div\n      className={cx(\n        styles.input,\n        { [styles.inputIconLeft]: hasLeftIcon, [styles.inputWithLabel]: useMatLabelStyle && !!label },\n        className,\n      )}\n      style={style}\n    >\n      <label\n        htmlFor={inputId}\n        className={cx(styles.inputLabel, classNames?.label, {\n          [styles.inputLabelColumn]: !isRow,\n          [styles.inputLabelInBorder]: labelInBorder,\n        })}\n        style={slotStyles?.label}\n      >\n        {label && !useMatLabelStyle ? (\n          <span className={styles.inputLabelText}>\n            {label}\n            {requiredMark}\n          </span>\n        ) : null}\n        {upperRightLabel && (\n          <div\n            className={cx(styles.inputUpperRightLabel, classNames?.upperRightLabel)}\n            style={slotStyles?.upperRightLabel}\n          >\n            {upperRightLabel}\n          </div>\n        )}\n        <div\n          className={cx(\n            styles.inputContainer,\n            {\n              [styles.inputContainerMultiline]: multiline,\n              [styles.inputContainerResizable]: multiline && !autoResize,\n              [styles.inputContainerDisabled]: disabled,\n            },\n            classNames?.container,\n          )}\n          style={slotStyles?.container}\n          onMouseEnter={onMouseEnterHandler}\n          onMouseLeave={onMouseLeaveHandler}\n        >\n          {iconIsLeft && fieldIcon}\n          {React.cloneElement(customInput || (multiline ? <textarea /> : <input />), {\n            ...rest,\n            className: cx(\n              styles.inputField,\n              {\n                [styles.inputFieldUppercase]: isUppercase,\n                [styles.inputFieldDate]: type === InputType.Date,\n                [styles.inputFieldDefault]: type !== InputType.Date,\n                [styles.inputFieldWithIconLeft]: hasLeftIcon,\n                [styles.inputFieldMultiline]: multiline,\n                [styles.inputFieldMultilineAutoResize]: multiline && autoResize,\n                [styles.inputFieldReadOnly]: readOnly && !disabled,\n              },\n              classNames?.input,\n              customInputProps?.className,\n            ),\n            style:\n              customInputProps?.style || slotStyles?.input\n                ? { ...customInputProps?.style, ...slotStyles?.input }\n                : undefined,\n            name,\n            id: inputId,\n            value: currVal,\n            tabIndex,\n            ref: customInput ? undefined : mergeRefs(inputRef, ref),\n            onChange: onChangeHandler,\n            onAnimationStart: handleAutofillAnimation,\n            onFocus: onFocusHandler,\n            onBlur: onBlurHandler,\n            placeholder: !useMatLabelStyle ? placeholder : '',\n            autoComplete,\n            onClick,\n            readOnly,\n            disabled,\n            ...(!multiline && { type: getInputType(), inputMode: rest.inputMode ?? getInputMode(), min, max }),\n            ...(multiline && rows !== undefined && { rows }),\n          })}\n          {suffix && (\n            <div className={cx(styles.inputFieldSuffix, classNames?.suffix)} style={slotStyles?.suffix}>\n              {suffix}\n            </div>\n          )}\n          {/* Mounted while empty too, so the first character grows it in rather than popping it in. */}\n          {clearable && type !== InputType.Search && (\n            <FontAwesomeIcon\n              icon={faXmark}\n              className={cx(styles.inputFieldIcon, styles.inputFieldIconClear, {\n                [styles.inputFieldIconClearHidden]: !canClear,\n              })}\n              onClick={canClear ? handleClear : undefined}\n              {...clickableIconProps(canClear ? handleClear : undefined)}\n              aria-label={text.clear}\n            />\n          )}\n          {!iconIsLeft && fieldIcon}\n        </div>\n        <fieldset\n          className={cx(styles.inputFieldset, classNames?.fieldset, {\n            [styles.inputContainerBorderFocused]: showFocus,\n            [styles.inputContainerBorderSearchFocused]: type === InputType.Search && (showFocus || !!currVal),\n            [styles.inputContainerBorderInvalid]: hasErr,\n            [styles.inputContainerBorderError]: hasErr,\n          })}\n          style={slotStyles?.fieldset}\n        >\n          <legend\n            className={cx(styles.inputFieldsetLegend, {\n              [styles.inputFieldsetLegendActive]: shouldFloatLabel,\n            })}\n          >\n            <span>{editedLabel}</span>\n          </legend>\n        </fieldset>\n        {useMatLabelStyle && label && (\n          <div\n            className={cx(styles.inputMatLabel, {\n              [styles.inputMatLabelFloating]: shouldFloatLabel,\n              [styles.inputMatLabelMultiline]: multiline && !shouldFloatLabel,\n            })}\n          >\n            {label}\n            {requiredMark}\n          </div>\n        )}\n        {useMatLabelStyle && placeholder && (\n          <div\n            className={cx(\n              styles.inputPlaceholder,\n              showFocus && !currVal && !isAutofilled && styles.inputPlaceholderShown,\n              multiline && styles.inputPlaceholderMultiline,\n            )}\n          >\n            {placeholder}\n          </div>\n        )}\n        <Tooltip\n          variant='error'\n          message={formValidator ? formValidator.getCurrentErrorMessage() : errorMessage || ''}\n          isVisible={hasErr && (isFocused || isHovered)}\n        />\n      </label>\n    </div>\n  );\n}\n\nexport default Input;\n","/**\n * Normalises any CSS colour the browser understands into hex, which is the only form\n * ColorPicker's own arithmetic can take. Without it every non-hex value — `transparent`,\n * `rgba(...)`, and above all `oklch(...)`, which a whole theme may be written in — silently\n * became the picker's default blue: a swatch confidently showing a colour nothing asked for.\n *\n * The canvas 2D context is the browser's own colour parser. Under jsdom there is none, so\n * this returns `undefined` and callers keep their previous fallback — meaning the widened\n * behaviour is only observable in a real browser, and only `*.browser.test.ts` can cover it.\n */\n\n/** `undefined` until first use, `null` once we know there is no 2D context (jsdom). */\nlet ctx: CanvasRenderingContext2D | null | undefined;\n\n/*\n * Six or eight digits only. Shorthand is deliberately NOT short-circuited: ColorPicker's own\n * isValidHex accepts those two lengths and nothing else, so handing it `#abc` untouched would\n * have produced exactly the default-blue swatch this helper exists to prevent. Letting it fall\n * through to the canvas expands it.\n */\nconst HEX = /^#([0-9a-f]{6}|[0-9a-f]{8})$/i;\n/*\n * Two of them, because an unparseable value leaves fillStyle at whatever preceded it: seed\n * with each in turn and a real colour resolves the same way twice, while a rejected one\n * comes back as the two different seeds. One sentinel could not tell a rejected value from\n * a value that happens to BE the sentinel — `rgb(1, 2, 3)` normalises to exactly `#010203`.\n */\nconst SEED_A = '#010203';\nconst SEED_B = '#040506';\n\nfunction byteToHex(n: number): string {\n  return Math.max(0, Math.min(255, Math.round(n)))\n    .toString(16)\n    .padStart(2, '0');\n}\n\n/** `#rrggbb`, widened to `#rrggbbaa` only when the colour is not fully opaque. */\nfunction pack(r: number, g: number, b: number, a: number): string {\n  const alpha = Math.round(a);\n  return `#${byteToHex(r)}${byteToHex(g)}${byteToHex(b)}${alpha === 255 ? '' : byteToHex(alpha)}`;\n}\n\nexport function toHex(value: string | undefined): string | undefined {\n  const raw = value?.trim();\n  if (!raw) return undefined;\n  if (HEX.test(raw)) return raw;\n\n  if (ctx === undefined) ctx = document.createElement('canvas').getContext('2d');\n  if (!ctx) return undefined;\n\n  ctx.fillStyle = SEED_A;\n  ctx.fillStyle = raw;\n  const resolved = ctx.fillStyle;\n  ctx.fillStyle = SEED_B;\n  ctx.fillStyle = raw;\n  if (ctx.fillStyle !== resolved) return undefined;\n\n  // fillStyle normalises sRGB inputs to `#rrggbb` or `rgba(r, g, b, a)`, which are exact.\n  if (resolved.startsWith('#')) return resolved;\n  const parts = /^rgba?\\(([^)]+)\\)$/i.exec(resolved);\n  if (parts) {\n    const [r, g, b, a = '1'] = parts[1].split(',').map((part) => part.trim());\n    return pack(Number(r), Number(g), Number(b), Number(a) * 255);\n  }\n\n  /*\n   * Anything in a wide-gamut syntax — oklch(), oklab(), color-mix() — comes back verbatim\n   * rather than as sRGB, so the browser is asked to paint it and the pixel is read instead.\n   * Only reached for those: canvas un-premultiplies alpha on the way out, which costs a\n   * little precision on a very transparent colour, and the exact paths above avoid it.\n   */\n  ctx.clearRect(0, 0, 1, 1);\n  ctx.fillRect(0, 0, 1, 1);\n  const [r, g, b, a] = ctx.getImageData(0, 0, 1, 1).data;\n  return pack(r, g, b, a);\n}\n",".colorPicker {\n  /* The swatch sits inline beside a label, so it tracks the text. The panel below deliberately\n     does not: its layout is fixed and it holds up fine at a large base font. */\n  --custom-swatch-size: var(--swatch-size, 2em);\n\n  display: flex;\n  align-items: center;\n  gap: 0.5em;\n\n  /* The visible swatch (below) is nested inside a Popover trigger, not this root — see\n     input.module.pcss for why this variable exists: a wrapping ButtonGroup sets --group-radius-*\n     per corner on the position this occupies, and unlike border-radius itself, custom properties\n     inherit through any nesting depth, reaching descendants a plain ancestor selector can't. */\n  --swatch-radius: var(--group-radius-tl, var(--default-border-radius))\n    var(--group-radius-tr, var(--default-border-radius)) var(--group-radius-br, var(--default-border-radius))\n    var(--group-radius-bl, var(--default-border-radius));\n}\n\n.swatch {\n  width: var(--custom-swatch-size);\n  height: var(--custom-swatch-size);\n  border-radius: var(--swatch-radius);\n  position: relative;\n  cursor: pointer;\n  -webkit-tap-highlight-color: transparent;\n  flex-shrink: 0;\n  box-shadow: 0 0 0 1px rgb(0 0 0 / 15%);\n\n  /* checkerboard so transparency is visible */\n  background-image:\n    linear-gradient(45deg, #bbb 25%, transparent 25%), linear-gradient(-45deg, #bbb 25%, transparent 25%),\n    linear-gradient(45deg, transparent 75%, #bbb 75%), linear-gradient(-45deg, transparent 75%, #bbb 75%);\n  background-size: 0.5em 0.5em;\n  background-position:\n    0 0,\n    0 0.25em,\n    0.25em -0.25em,\n    -0.25em 0;\n\n  /* Deliberately not a theme colour: this is the transparency checkerboard, and alpha can\n     only be judged against a fixed neutral. Theming it would make a swatch read differently\n     in every theme. */\n  background-color: white;\n\n  &:hover {\n    & .swatchHex {\n      opacity: 1;\n    }\n  }\n}\n\n.swatchFill {\n  position: absolute;\n  inset: 0;\n  border-radius: inherit;\n}\n\n.swatchHex {\n  position: absolute;\n  inset: 0;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  font-size: 0.5em;\n  font-family: var(--monospace-font, monospace);\n\n  /* White on a fixed scrim, not on the theme: the label sits over the swatch's own colour,\n     which the theme does not control. */\n  background: rgb(0 0 0 / 55%);\n  color: white;\n  border-radius: inherit;\n  opacity: 0;\n  transition: opacity 0.15s;\n  user-select: none;\n  pointer-events: none;\n  z-index: 1;\n}\n\n.panel {\n  padding: 12px;\n  display: flex;\n  flex-direction: column;\n  gap: 10px;\n  width: 224px;\n}\n\n.gradientArea {\n  width: 200px;\n  height: 160px;\n  position: relative;\n  cursor: crosshair;\n  border-radius: 4px;\n  overflow: hidden;\n  touch-action: none;\n  background: linear-gradient(to right, white, hsl(calc(var(--hue) * 1deg) 100% 50%));\n\n  /* Outside the box: a ring drawn inside would sit on the colours being picked. */\n  &:focus-visible {\n    outline: 2px solid var(--focus-border-color, var(--primary-color));\n    outline-offset: 2px;\n  }\n}\n\n.gradientOverlay {\n  position: absolute;\n  inset: 0;\n  background: linear-gradient(to bottom, transparent, black);\n}\n\n.cursor {\n  position: absolute;\n  width: 12px;\n  height: 12px;\n  border-radius: 50%;\n  border: 2px solid white;\n  box-shadow: 0 0 3px rgb(0 0 0 / 50%);\n  transform: translate(-50%, -50%);\n  pointer-events: none;\n  z-index: 1;\n}\n\n.hueSlider {\n  -webkit-appearance: none;\n  appearance: none;\n  width: 100%;\n  height: 10px;\n  border-radius: var(--default-border-radius);\n  outline: none;\n  cursor: pointer;\n  -webkit-tap-highlight-color: transparent;\n  background: linear-gradient(\n    to right,\n    hsl(0deg 100% 50%),\n    hsl(60deg 100% 50%),\n    hsl(120deg 100% 50%),\n    hsl(180deg 100% 50%),\n    hsl(240deg 100% 50%),\n    hsl(300deg 100% 50%),\n    hsl(360deg 100% 50%)\n  );\n\n  &::-webkit-slider-thumb {\n    -webkit-appearance: none;\n    appearance: none;\n    width: 6px;\n    height: 20px;\n    border-radius: var(--default-border-radius);\n    background: var(--primary-color);\n    box-shadow: var(--card-shadow);\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n  }\n\n  &::-moz-range-thumb {\n    width: 6px;\n    height: 20px;\n    border-radius: var(--default-border-radius);\n    background: var(--primary-color);\n    box-shadow: var(--card-shadow);\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n  }\n\n  /* The thumb, not the track, which `outline: none` above dropped the default ring\n     from. Two rules rather than one list: an engine that does not know one of these\n     pseudo-elements throws out the whole selector, which is why the thumb rules\n     above are split too. */\n  &:focus-visible::-webkit-slider-thumb {\n    box-shadow: 0 0 0 0.25em color-mix(in srgb, var(--focus-border-color, var(--primary-color)) 35%, transparent);\n  }\n\n  &:focus-visible::-moz-range-thumb {\n    box-shadow: 0 0 0 0.25em color-mix(in srgb, var(--focus-border-color, var(--primary-color)) 35%, transparent);\n  }\n}\n\n.alphaSlider {\n  -webkit-appearance: none;\n  appearance: none;\n  width: 100%;\n  height: 10px;\n  border-radius: var(--default-border-radius);\n  outline: none;\n  cursor: pointer;\n  -webkit-tap-highlight-color: transparent;\n\n  /* checkerboard base so transparency is visible under the gradient */\n  background-image:\n    linear-gradient(to right, transparent, var(--alpha-color)), linear-gradient(45deg, #bbb 25%, transparent 25%),\n    linear-gradient(-45deg, #bbb 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #bbb 75%),\n    linear-gradient(-45deg, transparent 75%, #bbb 75%);\n  background-size:\n    100% 100%,\n    8px 8px,\n    8px 8px,\n    8px 8px,\n    8px 8px;\n  background-position:\n    0 0,\n    0 0,\n    0 4px,\n    4px -4px,\n    -4px 0;\n\n  /* Deliberately not a theme colour: this is the transparency checkerboard, and alpha can\n     only be judged against a fixed neutral. Theming it would make a swatch read differently\n     in every theme. */\n  background-color: white;\n\n  &::-webkit-slider-thumb {\n    -webkit-appearance: none;\n    appearance: none;\n    width: 6px;\n    height: 20px;\n    border-radius: var(--default-border-radius);\n    background: var(--primary-color);\n    box-shadow: var(--card-shadow);\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n  }\n\n  &::-moz-range-thumb {\n    width: 6px;\n    height: 20px;\n    border-radius: var(--default-border-radius);\n    background: var(--primary-color);\n    box-shadow: var(--card-shadow);\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n  }\n\n  /* The thumb, not the track, which `outline: none` above dropped the default ring\n     from. Two rules rather than one list: an engine that does not know one of these\n     pseudo-elements throws out the whole selector, which is why the thumb rules\n     above are split too. */\n  &:focus-visible::-webkit-slider-thumb {\n    box-shadow: 0 0 0 0.25em color-mix(in srgb, var(--focus-border-color, var(--primary-color)) 35%, transparent);\n  }\n\n  &:focus-visible::-moz-range-thumb {\n    box-shadow: 0 0 0 0.25em color-mix(in srgb, var(--focus-border-color, var(--primary-color)) 35%, transparent);\n  }\n}\n\n.hexRow {\n  display: flex;\n  align-items: center;\n  gap: 6px;\n}\n\n.hexInput {\n  width: 100%;\n  padding: 5px 8px;\n  border-radius: var(--default-border-radius);\n  border: 1px solid var(--border-color);\n  background: var(--input-background);\n  color: var(--text-color);\n  font-size: var(--default-font-size);\n  font-family: monospace;\n  box-sizing: border-box;\n  outline: none;\n\n  &:focus {\n    border-color: var(--focus-border-color, var(--primary-color));\n  }\n}\n\n/* Two classes, to outrank ActionIcon's own box whichever stylesheet loads first. */\n.hexRow .eyeDropperButton {\n  flex-shrink: 0;\n  width: 28px;\n  height: 28px;\n}\n","import { useState, useRef, type ReactElement } from 'react';\nimport cx from 'classnames';\nimport { faEyeDropper } from '@fortawesome/free-solid-svg-icons';\n\nimport InteractableDiv from 'components/common/interactableDiv';\nimport Popover from 'components/common/popover';\nimport Input from 'components/common/input';\nimport { PopoverAlign, PopoverPlacement } from 'components/common/popover/popover.types';\nimport type { ColorPickerLabels, ColorPickerProps, ColorPickerSlots } from './colorPicker.types';\nimport type { SlotClassNames, SlotStyles } from '../types/slots.types';\nimport { useLabels } from '../configProvider';\n// Aliased: `hsvToHex` has a local `toHex` that formats one channel byte, which is a\n// different job from normalising a whole CSS colour.\nimport { toHex as cssToHex } from './toHex';\n\nimport styles from './colorPicker.module.pcss';\nimport ActionIcon from 'components/common/actionIcon';\nimport { ButtonStyleType } from 'components/common/button/button.types';\n\ninterface EyeDropperConstructor {\n  new (): { open: (options?: { signal?: AbortSignal }) => Promise<{ sRGBHex: string }> };\n}\n\nconst eyeDropperSupported = typeof window !== 'undefined' && 'EyeDropper' in window;\n\nasync function pickColorFromScreen(): Promise<string | null> {\n  const EyeDropperCtor = (window as unknown as { EyeDropper?: EyeDropperConstructor }).EyeDropper;\n  if (!EyeDropperCtor) return null;\n  try {\n    const result = await new EyeDropperCtor().open();\n    return result.sRGBHex;\n  } catch {\n    return null; // user cancelled or picker failed\n  }\n}\n\n// ── Color utilities ──────────────────────────────────────────────────────────\n\nfunction clampPercent(n: number): number {\n  return Math.max(0, Math.min(100, n));\n}\n\nfunction isValidHex(s: string): boolean {\n  return /^#[0-9a-fA-F]{6}$/.test(s) || /^#[0-9a-fA-F]{8}$/.test(s);\n}\n\n/** Split any valid hex into its 6-digit base and 0-100 alpha. */\nfunction parseHex(hex: string): { hex6: string; alpha: number } {\n  if (/^#[0-9a-fA-F]{8}$/.test(hex)) {\n    return {\n      hex6: hex.slice(0, 7),\n      alpha: (parseInt(hex.slice(7, 9), 16) / 255) * 100,\n    };\n  }\n  return { hex6: hex.slice(0, 7), alpha: 100 };\n}\n\n/** Combine a 6-digit hex + 0-100 alpha into the right output format.\n *  Full opacity (alpha === 100) is omitted — 6-digit hex implies opaque. */\nfunction buildValue(hex6: string, alpha: number, showAlpha: boolean): string {\n  if (!showAlpha || alpha >= 100) return hex6;\n  const a = Math.round((alpha / 100) * 255)\n    .toString(16)\n    .padStart(2, '0');\n  return `${hex6}${a}`;\n}\n\nfunction hexToRgba(hex6: string, alpha: number): string {\n  const r = parseInt(hex6.slice(1, 3), 16);\n  const g = parseInt(hex6.slice(3, 5), 16);\n  const b = parseInt(hex6.slice(5, 7), 16);\n  return `rgba(${r}, ${g}, ${b}, ${alpha / 100})`;\n}\n\nfunction hexToHsv(hex6: string): { h: number; s: number; v: number } {\n  const r = parseInt(hex6.slice(1, 3), 16) / 255;\n  const g = parseInt(hex6.slice(3, 5), 16) / 255;\n  const b = parseInt(hex6.slice(5, 7), 16) / 255;\n\n  const max = Math.max(r, g, b);\n  const min = Math.min(r, g, b);\n  const delta = max - min;\n\n  let h = 0;\n  if (delta !== 0) {\n    if (max === r) h = ((g - b) / delta) % 6;\n    else if (max === g) h = (b - r) / delta + 2;\n    else h = (r - g) / delta + 4;\n    h *= 60;\n    if (h < 0) h += 360;\n  }\n\n  // Deliberately unrounded: hsvToHex rounds once, at the byte. Rounding here too costs up to a\n  // unit per channel, so a value handed in would come back out changed (#3b82f6 -> #3b82f5).\n  const s = max === 0 ? 0 : (delta / max) * 100;\n  const v = max * 100;\n\n  return { h, s, v };\n}\n\nfunction hsvToHex(h: number, s: number, v: number): string {\n  const sv = s / 100;\n  const vv = v / 100;\n  const c = vv * sv;\n  const x = c * (1 - Math.abs(((h / 60) % 2) - 1));\n  const m = vv - c;\n\n  let r = 0,\n    g = 0,\n    b = 0;\n  if (h < 60) {\n    r = c;\n    g = x;\n    b = 0;\n  } else if (h < 120) {\n    r = x;\n    g = c;\n    b = 0;\n  } else if (h < 180) {\n    r = 0;\n    g = c;\n    b = x;\n  } else if (h < 240) {\n    r = 0;\n    g = x;\n    b = c;\n  } else if (h < 300) {\n    r = x;\n    g = 0;\n    b = c;\n  } else {\n    r = c;\n    g = 0;\n    b = x;\n  }\n\n  const toHex = (n: number) =>\n    Math.round((n + m) * 255)\n      .toString(16)\n      .padStart(2, '0');\n  return `#${toHex(r)}${toHex(g)}${toHex(b)}`;\n}\n\n// ── HexInput ─────────────────────────────────────────────────────────────────\n\ninterface HexInputProps {\n  className?: string;\n  style?: React.CSSProperties;\n  hex: string;\n  onChange: (hex: string) => void;\n  useInputComponent?: boolean;\n}\n\nfunction HexInput({ hex, onChange, useInputComponent = false, className, style }: HexInputProps): ReactElement {\n  const [draft, setDraft] = useState(hex);\n\n  const [prevHex, setPrevHex] = useState(hex);\n  if (hex !== prevHex) {\n    setPrevHex(hex);\n    setDraft(hex);\n  }\n\n  function handleChange(raw: string) {\n    setDraft(raw);\n    const normalized = raw.startsWith('#') ? raw : `#${raw}`;\n    if (isValidHex(normalized)) onChange(normalized);\n  }\n\n  function handleBlur() {\n    const normalized = draft.startsWith('#') ? draft : `#${draft}`;\n    if (!isValidHex(normalized)) setDraft(hex);\n  }\n\n  if (useInputComponent) {\n    return <Input value={draft} onChange={(value: string) => handleChange(value)} onBlur={handleBlur} />;\n  }\n\n  return (\n    <input\n      type='text'\n      value={draft}\n      onChange={(e) => handleChange(e.target.value)}\n      onBlur={handleBlur}\n      className={cx(styles.hexInput, className)}\n      style={style}\n      spellCheck={false}\n    />\n  );\n}\n\n// ── Picker panel ─────────────────────────────────────────────────────────────\n\ninterface PickerPanelProps {\n  hue: number;\n  sat: number;\n  val: number;\n  alpha: number;\n  onHueChange: (h: number) => void;\n  onSVChange: (s: number, v: number) => void;\n  onAlphaChange: (a: number) => void;\n  showInput?: boolean;\n  showAlpha?: boolean;\n  labels: Required<ColorPickerLabels>;\n  classNames?: SlotClassNames<ColorPickerSlots>;\n  styles?: SlotStyles<ColorPickerSlots>;\n}\n\nfunction PickerPanel({\n  hue,\n  sat,\n  val,\n  alpha,\n  onHueChange,\n  onSVChange,\n  onAlphaChange,\n  showInput = true,\n  showAlpha = false,\n  labels,\n  classNames,\n  styles: slotStyles,\n}: PickerPanelProps): ReactElement {\n  const gradientRef = useRef<HTMLDivElement>(null);\n\n  function getSVFromEvent(e: { clientX: number; clientY: number }): { s: number; v: number } {\n    const rect = gradientRef.current!.getBoundingClientRect();\n    const x = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));\n    const y = Math.max(0, Math.min(1, (e.clientY - rect.top) / rect.height));\n    return { s: x * 100, v: (1 - y) * 100 };\n  }\n\n  function onGradientKeyDown(e: React.KeyboardEvent) {\n    const step = e.shiftKey ? 10 : 1;\n    let s = sat;\n    let v = val;\n    switch (e.key) {\n      case 'ArrowLeft':\n        s -= step;\n        break;\n      case 'ArrowRight':\n        s += step;\n        break;\n      case 'ArrowUp':\n        v += step;\n        break;\n      case 'ArrowDown':\n        v -= step;\n        break;\n      case 'Home':\n        s = 0;\n        break;\n      case 'End':\n        s = 100;\n        break;\n      case 'PageUp':\n        v = 100;\n        break;\n      case 'PageDown':\n        v = 0;\n        break;\n      default:\n        return;\n    }\n    e.preventDefault();\n    onSVChange(clampPercent(s), clampPercent(v));\n  }\n\n  // Pointer Events (not mouse events) so dragging works for touch/pen too.\n  function onGradientPointerDown(e: React.PointerEvent) {\n    e.currentTarget.setPointerCapture(e.pointerId);\n    const { s, v } = getSVFromEvent(e);\n    onSVChange(s, v);\n\n    const onMove = (ev: PointerEvent) => {\n      const { s, v } = getSVFromEvent(ev);\n      onSVChange(s, v);\n    };\n    const onUp = () => {\n      window.removeEventListener('pointermove', onMove);\n      window.removeEventListener('pointerup', onUp);\n    };\n    window.addEventListener('pointermove', onMove);\n    window.addEventListener('pointerup', onUp);\n  }\n\n  function onHexChange(normalized: string) {\n    const { hex6, alpha: parsedAlpha } = parseHex(normalized);\n    const { h, s, v } = hexToHsv(hex6);\n    onHueChange(h);\n    onSVChange(s, v);\n    if (showAlpha) onAlphaChange(parsedAlpha);\n  }\n\n  const hex6 = hsvToHex(hue, sat, val);\n  const hexInputValue = showAlpha ? buildValue(hex6, alpha, true) : hex6;\n\n  return (\n    <div className={cx(styles.panel, classNames?.panel)} style={slotStyles?.panel}>\n      {/* Two axes on one control, which ARIA has no role for. `slider` on the\n          horizontal axis is the closest fit, with both numbers in `aria-valuetext`. */}\n      <div\n        ref={gradientRef}\n        role='slider'\n        tabIndex={0}\n        aria-label={labels.saturationAndBrightness}\n        aria-valuemin={0}\n        aria-valuemax={100}\n        aria-valuenow={Math.round(sat)}\n        aria-valuetext={labels.saturationAndBrightnessValue(Math.round(sat), Math.round(val))}\n        className={cx(styles.gradientArea, classNames?.gradient)}\n        style={{ '--hue': hue, ...slotStyles?.gradient } as React.CSSProperties}\n        onPointerDown={onGradientPointerDown}\n        onKeyDown={onGradientKeyDown}\n      >\n        <div className={styles.gradientOverlay} />\n        <div className={styles.cursor} style={{ left: `${sat}%`, top: `${100 - val}%` }} />\n      </div>\n\n      <input\n        type='range'\n        min={0}\n        max={359}\n        value={Math.round(hue)}\n        onChange={(e) => onHueChange(Number(e.target.value))}\n        className={cx(styles.hueSlider, classNames?.hueSlider)}\n        style={slotStyles?.hueSlider}\n      />\n\n      {showAlpha && (\n        <input\n          type='range'\n          min={0}\n          max={100}\n          value={Math.round(alpha)}\n          onChange={(e) => onAlphaChange(Number(e.target.value))}\n          className={cx(styles.alphaSlider, classNames?.alphaSlider)}\n          style={{ '--alpha-color': hex6, ...slotStyles?.alphaSlider } as React.CSSProperties}\n        />\n      )}\n\n      {(showInput || eyeDropperSupported) && (\n        <div className={styles.hexRow}>\n          {showInput && (\n            <HexInput\n              hex={hexInputValue}\n              onChange={onHexChange}\n              className={classNames?.hexInput}\n              style={slotStyles?.hexInput}\n            />\n          )}\n          {eyeDropperSupported && (\n            <ActionIcon\n              icon={faEyeDropper}\n              styleType={ButtonStyleType.Outlined}\n              className={cx(styles.eyeDropperButton, classNames?.eyeDropper)}\n              style={slotStyles?.eyeDropper}\n              onClick={async () => {\n                const hex = await pickColorFromScreen();\n                if (hex) onHexChange(hex);\n              }}\n              aria-label={labels.pickFromScreen}\n            />\n          )}\n        </div>\n      )}\n    </div>\n  );\n}\n\n// ── ColorPicker ───────────────────────────────────────────────────────────────\n\nconst DEFAULT_COLOR = '#3b82f6';\n\nconst DEFAULT_LABELS: Required<ColorPickerLabels> = {\n  pickFromScreen: 'Pick color from screen',\n  saturationAndBrightness: 'Saturation and brightness',\n  saturationAndBrightnessValue: (saturation, brightness) => `${saturation}% saturation, ${brightness}% brightness`,\n};\n\nfunction ColorPicker({\n  className,\n  value = DEFAULT_COLOR,\n  onChange,\n  showInput = false,\n  showAlpha = false,\n  inline = false,\n  labels,\n  style,\n  classNames,\n  styles: slotStyles,\n  ...rest\n}: ColorPickerProps): ReactElement {\n  const text = useLabels('ColorPicker', labels, DEFAULT_LABELS);\n  const [isOpen, setIsOpen] = useState(false);\n\n  const initSafe = cssToHex(value) ?? DEFAULT_COLOR;\n  const { hex6: initHex6, alpha: initAlpha } = parseHex(initSafe);\n  const initHsv = hexToHsv(initHex6);\n\n  // Single state object so hue survives when saturation/value hit 0\n  // (hexToHsv of any grey returns h=0, which would corrupt hue if derived from props).\n  const [hsva, setHsva] = useState({ h: initHsv.h, s: initHsv.s, v: initHsv.v, a: initAlpha });\n\n  // Ref mirrors state so drag callbacks never read stale closure values.\n  const cur = useRef(hsva);\n\n  // Sync from external prop changes: \"derive state during render\" pattern\n  // avoids setState-in-effect. We compare the incoming hex against what our\n  // current state produces — if they match, the change came from us and we skip.\n  const [prevValue, setPrevValue] = useState(value);\n  if (value !== prevValue) {\n    setPrevValue(value);\n    const safe = cssToHex(value) ?? DEFAULT_COLOR;\n    const { hex6: incomingHex6, alpha: incomingA } = parseHex(safe);\n    const currentHex6 = hsvToHex(hsva.h, hsva.s, hsva.v);\n    if (incomingHex6 !== currentHex6 || incomingA !== hsva.a) {\n      const hsv = hexToHsv(incomingHex6);\n      const next = { h: hsv.h, s: hsv.s, v: hsv.v, a: incomingA };\n      setHsva(next);\n    }\n  }\n\n  const { h: hue, s: sat, v: val, a: alpha } = hsva;\n\n  function emit(h: number, s: number, v: number, a: number) {\n    onChange?.(buildValue(hsvToHex(h, s, v), a, showAlpha));\n  }\n\n  function handleHueChange(h: number) {\n    cur.current.h = h;\n    setHsva((prev) => ({ ...prev, h }));\n    emit(h, cur.current.s, cur.current.v, cur.current.a);\n  }\n\n  function handleSVChange(s: number, v: number) {\n    cur.current.s = s;\n    cur.current.v = v;\n    setHsva((prev) => ({ ...prev, s, v }));\n    emit(cur.current.h, s, v, cur.current.a);\n  }\n\n  function handleAlphaChange(a: number) {\n    cur.current.a = a;\n    setHsva((prev) => ({ ...prev, a }));\n    emit(cur.current.h, cur.current.s, cur.current.v, a);\n  }\n\n  const hex6 = hsvToHex(hue, sat, val);\n  const swatchBackground = showAlpha ? hexToRgba(hex6, alpha) : hex6;\n  const displayValue = buildValue(hex6, alpha, showAlpha);\n\n  const pickerPanelProps: PickerPanelProps = {\n    hue,\n    sat,\n    val,\n    alpha,\n    onHueChange: handleHueChange,\n    onSVChange: handleSVChange,\n    onAlphaChange: handleAlphaChange,\n    showAlpha,\n    labels: text,\n    classNames,\n    styles: slotStyles,\n  };\n\n  if (inline) {\n    return (\n      <div\n        {...rest}\n        className={cx(styles.colorPicker, className, classNames?.root)}\n        style={{ ...style, ...slotStyles?.root }}\n      >\n        <PickerPanel {...pickerPanelProps} showInput={showInput} />\n      </div>\n    );\n  }\n\n  return (\n    <div {...rest} className={cx(styles.colorPicker, className)}>\n      <Popover\n        isOpen={isOpen}\n        onOpenChange={setIsOpen}\n        placement={PopoverPlacement.Bottom}\n        align={PopoverAlign.Start}\n        // The panel sets its own 224px width.\n        constrainWidth={false}\n        content={<PickerPanel {...pickerPanelProps} />}\n      >\n        {/* The click handler is on Popover's wrapper, so `InteractableDiv` would\n            give the swatch neither a tab stop nor a role of its own. */}\n        <InteractableDiv\n          role='button'\n          tabIndex={0}\n          aria-haspopup='dialog'\n          aria-expanded={isOpen}\n          className={cx(styles.swatch, classNames?.swatch)}\n          style={slotStyles?.swatch}\n        >\n          <div className={styles.swatchFill} style={{ background: swatchBackground }} />\n          <span className={styles.swatchHex}>{displayValue}</span>\n        </InteractableDiv>\n      </Popover>\n\n      {showInput && <HexInput hex={displayValue} onChange={(v) => onChange?.(v)} useInputComponent />}\n    </div>\n  );\n}\n\nexport default ColorPicker;\n",".modal {\n  /* Theme tokens resolved once, each with a built-in fallback. Set any of the public\n     `--modal-*` variables via ThemeProvider (theme-wide) or `style` (per instance) to\n     override; unset ones fall back to the values below. */\n  --custom-modal-padding: var(--modal-padding, var(--spacing-l));\n  --custom-modal-gap: var(--modal-gap, var(--spacing-l));\n\n  /* Half the gap, so the scroll port's clipping edge — where the divider sits — lands midway\n     between the chrome and where the content rests, with the same room either side. Flush\n     against the chrome (bleed = gap) reads as a sliced line of text colliding with the title;\n     an unrelated value (this was `0.6em`) puts it at a distance that drifts with the font size. */\n  --custom-modal-body-bleed: var(--modal-body-bleed, calc(var(--custom-modal-gap) / 2));\n\n  /* The panel never comes closer to the screen edge than its own padding, at every size. A\n     percentage instead reads as a different inset on every screen, and pairing one with a\n     breakpoint made the two axes disagree: 5% of a 390px width is 19.5px but 5% of a 780px\n     height is 39px, so the same rule came out narrower and taller than the default on one phone. */\n  --custom-modal-max-width: var(--modal-max-width, calc(100% - 2 * var(--custom-modal-padding)));\n  --custom-modal-max-height: var(--modal-max-height, calc(100% - 2 * var(--custom-modal-padding)));\n\n  padding: var(--custom-modal-padding);\n  border-radius: var(--default-border-radius);\n  background: var(--background-modal);\n  color: var(--text-color);\n  font-family: var(--default-font);\n  box-sizing: border-box;\n  position: relative;\n  display: flex;\n  flex-direction: column;\n  transform-origin: bottom center;\n  animation:\n    slideIn 0.2s,\n    fadeIn 0.3s forwards;\n  max-width: var(--custom-modal-max-width);\n  max-height: var(--custom-modal-max-height);\n\n  /* The panel itself never scrolls, so the header stays put; `&-body` is what scrolls. */\n  overflow: hidden;\n\n  /* A sheet-presented modal carries forms and detail views, not a short menu, so it takes more\n     of the screen than the shared default — and keeps the panel's own padding at the bottom edge. */\n  &-sheet {\n    --sheet-max-height: 90dvh;\n    --sheet-safe-padding: var(--custom-modal-padding);\n  }\n\n  &-container {\n    position: fixed;\n    width: 100%;\n    height: 100%;\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    top: 0;\n    left: 0;\n\n    /* Shared by every overlay, so the order they are portaled in, which is the order they opened in, decides. */\n    z-index: 1;\n  }\n\n  &-header {\n    display: flex;\n    gap: var(--spacing-m);\n    justify-content: space-between;\n    align-items: center;\n    margin-bottom: var(--custom-modal-gap);\n    flex: 0 0 auto;\n\n    div {\n      overflow: hidden;\n      text-overflow: ellipsis;\n    }\n  }\n\n  &-body {\n    /* `min-height: 0` lets this shrink below its content's height so it, not the panel, scrolls. */\n    flex: 1 1 auto;\n    min-height: 0;\n    overflow: auto;\n\n    /* A negative margin cancelled by an equal padding, so a focus ring or a floating label at\n       the body's edge isn't clipped by the scroll container. Override the pair with\n       `--modal-body-bleed`, never with a plain `padding`, which drops the negative half and\n       brings the clipping back. See `docs/Modal.md` for what the two do together. */\n    margin: calc(-1 * var(--custom-modal-body-bleed));\n    padding: var(--custom-modal-body-bleed);\n\n    /* The mask lives in `styles/scrollFade.module.pcss`; the component applies it along with the\n       modifiers saying which end genuinely hides content. The run has to clear the port's own\n       padding before any content fades at all, hence gap + bleed. Set here as the shared sheet's\n       private length rather than as `--scroll-fade-length`, which would inherit down into a\n       `ScrollFade` nested in the body. */\n    --custom-scroll-fade-length: var(\n      --modal-scroll-fade,\n      calc(var(--custom-modal-gap) + var(--custom-modal-body-bleed))\n    );\n  }\n\n  &-footer {\n    margin-top: var(--custom-modal-gap);\n    flex: 0 0 auto;\n  }\n\n  &-close-button {\n    flex-shrink: 0;\n  }\n\n  &-wrapper {\n    position: fixed;\n    width: 100%;\n    height: 100%;\n    background: #00000052;\n    top: 0;\n    left: 0;\n    transition: all 0.3s ease-in-out;\n    opacity: 0;\n\n    &-shown {\n      opacity: 1;\n    }\n  }\n}\n\n@keyframes slideIn {\n  from {\n    transform: scale(0);\n  }\n\n  to {\n    transform: scale(0.99);\n  }\n}\n\n@keyframes fadeIn {\n  from {\n    opacity: 0;\n  }\n\n  to {\n    opacity: 1;\n  }\n}\n\n/* Only the scale-in goes; the backdrop and the panel still fade, which is not motion.\n   `slideIn` ends at scale(0.99) and the panel has no transform of its own, so dropping the\n   animation lands it at scale(1) rather than leaving it collapsed. */\n@media (prefers-reduced-motion: reduce) {\n  .modal {\n    animation: fadeIn 0.3s forwards;\n  }\n}\n","import { useEffect, useState, type RefObject } from 'react';\n\nexport interface ScrollEdges {\n  /** Content is scrolled out of view past the element's top (or left) edge. */\n  hasStart: boolean;\n  /** Content continues past the element's bottom (or right) edge. */\n  hasEnd: boolean;\n}\n\nexport interface ScrollEdgesOptions {\n  /** Which axis to measure. Default `'vertical'`. */\n  axis?: 'vertical' | 'horizontal';\n  /** Measure at all. Default `true`; pass `false` while the element is hidden. */\n  isEnabled?: boolean;\n}\n\nconst NONE: ScrollEdges = { hasStart: false, hasEnd: false };\n\n/**\n * Reports which edges of a scroll container currently hide content, so a caller can mark the\n * boundary, most often by fading it: see `styles/scrollFade.module.pcss`.\n */\nexport function useScrollEdges(ref: RefObject<HTMLElement | null>, options: ScrollEdgesOptions = {}): ScrollEdges {\n  const { axis = 'vertical', isEnabled = true } = options;\n  const [edges, setEdges] = useState<ScrollEdges>(NONE);\n\n  useEffect(() => {\n    const element = ref.current;\n    if (!isEnabled || !element) {\n      setEdges((previous) => (previous === NONE ? previous : NONE));\n      return;\n    }\n\n    const isVertical = axis === 'vertical';\n    // Read once rather than per measurement: `getComputedStyle` in the scroll handler forces a\n    // style flush on every frame of a scroll, and the overflow mode can only change with the\n    // element's class or inline style, which re-runs this effect anyway.\n    const style = getComputedStyle(element);\n    // `hidden` and `clip` cut the overflow off with no way for anyone to reach the rest, so\n    // marking an edge there would promise a scroll that cannot happen. The metrics alone never\n    // say so: `scrollHeight` reports the full content height under both.\n    const overflow = isVertical ? style.overflowY : style.overflowX;\n    const isClipped = overflow === 'hidden' || overflow === 'clip';\n\n    const measure = () => {\n      const offset = isVertical ? element.scrollTop : element.scrollLeft;\n      const total = isVertical ? element.scrollHeight : element.scrollWidth;\n      const visible = isVertical ? element.clientHeight : element.clientWidth;\n      // A pixel of slack: fractional layout at non-integer zoom leaves the offset a hair short of\n      // the maximum, which would otherwise read as \"there is still more past this edge\" forever.\n      const hasStart = !isClipped && offset > 1;\n      const hasEnd = !isClipped && total - visible - offset > 1;\n      setEdges((previous) =>\n        previous.hasStart === hasStart && previous.hasEnd === hasEnd ? previous : { hasStart, hasEnd },\n      );\n    };\n\n    const resizes = new ResizeObserver(measure);\n    // The port's own box stops changing once it is capped, so content that grows without one of\n    // its ancestors resizing, an image decoding or a web font swapping in, only shows on a child.\n    const observeContent = () => {\n      resizes.disconnect();\n      resizes.observe(element);\n      for (const child of Array.from(element.children)) resizes.observe(child);\n    };\n\n    // Content arriving through React changes `scrollHeight` without resizing anything already\n    // observed, and the new children have to be picked up for the next time they grow.\n    const mutations = new MutationObserver(() => {\n      observeContent();\n      measure();\n    });\n\n    measure();\n    observeContent();\n    mutations.observe(element, { childList: true, subtree: true, characterData: true });\n    element.addEventListener('scroll', measure, { passive: true });\n    return () => {\n      element.removeEventListener('scroll', measure);\n      mutations.disconnect();\n      resizes.disconnect();\n    };\n  }, [ref, axis, isEnabled]);\n\n  return edges;\n}\n","import cx from 'classnames';\nimport BodyEnd from 'components/common/bodyEnd';\n\nimport styles from './modal.module.pcss';\nimport sheetStyles from 'components/common/styles/sheet.module.pcss';\nimport { faXmark as closeIcon } from '@fortawesome/free-solid-svg-icons';\nimport { ModalProps } from './modal.types';\nimport type { ModalLabels } from './modal.types';\nimport { useEffect, useId, useRef, useState } from 'react';\nimport { useVisualViewportSize } from 'components/common/hooks/useVisualViewportSize';\nimport { useOverlay } from 'components/common/hooks/useOverlay';\nimport { useSheet } from 'components/common/hooks/useSheet';\nimport { useScrollEdges } from 'components/common/hooks/useScrollEdges';\nimport fadeStyles from 'components/common/styles/scrollFade.module.pcss';\nimport { useComponentDefaults, useLabels, usePresentationDefault } from 'components/common/configProvider';\nimport ActionIcon from 'components/common/actionIcon';\n\nconst DEFAULT_LABELS: Required<ModalLabels> = {\n  close: 'Close',\n};\n\nfunction Modal(props: ModalProps) {\n  const {\n    children = null,\n    title,\n    isOpen = false,\n    onClose = () => null,\n    footer = null,\n    className = '',\n    style,\n    hideHeader = false,\n    closeOnEscape = true,\n    presentation: presentationProp,\n    classNames,\n    styles: slotStyles,\n    labels,\n    ...rest\n  } = useComponentDefaults('Modal', props);\n  const text = useLabels('Modal', labels, DEFAULT_LABELS);\n  const [isRendered, setIsRendered] = useState(isOpen);\n  const [animateIn, setAnimateIn] = useState(isOpen);\n  const modalRef = useRef<HTMLDivElement>(null);\n  const bodyRef = useRef<HTMLDivElement>(null);\n  const titleId = useId();\n  // Centering/sizing against the actually-visible viewport (not the full layout viewport) keeps\n  // the modal — and whatever's focused inside it — from ending up under a mobile keyboard.\n  const { height: viewportHeight, offsetTop: viewportOffsetTop } = useVisualViewportSize();\n  // Explicit prop → this component's global default → the app-wide one. There's no built-in\n  // fallback to add: `Default` and no presentation at all both mean the ordinary centred dialog.\n  const appPresentation = usePresentationDefault();\n  const presentation = presentationProp ?? appPresentation;\n  const { isSheet, sheetStyle } = useSheet(presentation);\n\n  // Cancelled on a flip back, or a reopen during the close animation is hidden by its late unmount.\n  useEffect(() => {\n    if (isOpen) {\n      // eslint-disable-next-line react-hooks/set-state-in-effect\n      setIsRendered(true);\n      const id = setTimeout(() => setAnimateIn(true), 1);\n      return () => clearTimeout(id);\n    }\n    setAnimateIn(false);\n    const id = setTimeout(() => setIsRendered(false), 300);\n    return () => clearTimeout(id);\n  }, [isOpen]);\n\n  // Keyed on `animateIn` rather than `isOpen`, because the panel this needs to focus and trap\n  // focus inside of only mounts once that flips.\n  useOverlay({\n    isActive: isRendered && animateIn,\n    containerRef: modalRef,\n    onEscape: closeOnEscape ? onClose : undefined,\n  });\n\n  const { hasStart, hasEnd } = useScrollEdges(bodyRef, { isEnabled: isRendered && animateIn });\n\n  if (!isRendered) return null;\n\n  const hasHeaderTitle = !hideHeader && !!title;\n\n  return (\n    <BodyEnd>\n      <div\n        className={cx(styles.modalContainer, classNames?.container)}\n        style={{ height: viewportHeight, top: viewportOffsetTop, ...slotStyles?.container }}\n      >\n        <div\n          className={cx(styles.modalWrapper, animateIn && styles.modalWrapperShown, classNames?.backdrop)}\n          style={slotStyles?.backdrop}\n          onClick={onClose}\n        />\n        {animateIn && (\n          <div\n            {...rest}\n            ref={modalRef}\n            role='dialog'\n            aria-modal='true'\n            // aria-labelledby would outrank aria-label, which is the only way to name a title with no text.\n            aria-labelledby={hasHeaderTitle && !rest['aria-label'] ? titleId : undefined}\n            // Focusable so the trap has somewhere to put focus when the content has no tabbables.\n            tabIndex={-1}\n            className={cx(\n              styles.modal,\n              animateIn && styles.modalShown,\n              // The panel is its own sheet surface here: there's no inner panel to fill, since\n              // Modal already splits itself into a fixed header and a scrolling body.\n              isSheet && [sheetStyles.sheet, sheetStyles.sheetSurface, styles.modalSheet],\n              className,\n              classNames?.modal,\n            )}\n            style={{ ...(isSheet ? sheetStyle : undefined), ...style, ...slotStyles?.modal }}\n            onClick={(e) => e.stopPropagation()}\n          >\n            {!hideHeader && (\n              <div className={cx(styles.modalHeader, classNames?.header)} style={slotStyles?.header}>\n                <div id={titleId} className={classNames?.title} style={slotStyles?.title}>\n                  {title}\n                </div>\n                <ActionIcon\n                  icon={closeIcon}\n                  onClick={onClose}\n                  aria-label={text.close}\n                  className={cx(styles.modalCloseButton, classNames?.closeButton)}\n                  style={slotStyles?.closeButton}\n                />\n              </div>\n            )}\n            <div\n              ref={bodyRef}\n              className={cx(\n                styles.modalBody,\n                fadeStyles.scrollFade,\n                fadeStyles.scrollFadeY,\n                hasStart && fadeStyles.scrollFadeFadedStart,\n                hasEnd && fadeStyles.scrollFadeFadedEnd,\n                classNames?.body,\n              )}\n              style={slotStyles?.body}\n            >\n              {children}\n            </div>\n            {footer && (\n              <div className={cx(styles.modalFooter, classNames?.footer)} style={slotStyles?.footer}>\n                {footer}\n              </div>\n            )}\n          </div>\n        )}\n      </div>\n    </BodyEnd>\n  );\n}\n\nexport default Modal;\n","/**\n * Where the highlight goes next in a list the arrow keys move through.\n *\n * Shared so the library's listboxes agree on it. They did not: Dropdown wrapped and skipped\n * disabled options, MultiSelect wrapped but stopped on them, and InputDropdown clamped at both\n * ends and cleared the highlight when ArrowUp reached the top. Moving between two fields in the\n * same form behaved differently for no reason a user could see.\n *\n * Only the arithmetic is shared. Which key means what, and what selecting does, stays with each\n * component, because those genuinely differ.\n */\n\n/** `null` is \"nothing highlighted\": the state a freshly opened list starts in. */\nexport type ActiveIndex = number | null;\n\nexport interface ListNavigationOptions {\n  /** Options that cannot be highlighted, by index. */\n  isDisabled?: (index: number) => boolean;\n  /**\n   * Whether moving past an end comes back round. `false` stops on the last selectable option,\n   * for a list whose first entry is the text the user is still typing.\n   */\n  wrap?: boolean;\n}\n\n/**\n * The next selectable index in `step` direction, or `null` when the list has nothing to\n * highlight. From `null` a forward step enters at the top and a backward step at the bottom,\n * which is what both a native `<select>` and the ARIA combobox pattern do.\n */\nexport function nextIndex(\n  from: ActiveIndex,\n  step: 1 | -1,\n  length: number,\n  { isDisabled, wrap = true }: ListNavigationOptions = {},\n): ActiveIndex {\n  if (length === 0) return null;\n  const selectable = (index: number) => !isDisabled?.(index);\n\n  // Entering the list counts as the first hop, so start one step outside it.\n  const start = from ?? (step === 1 ? -1 : length);\n\n  for (let hop = 1; hop <= length; hop++) {\n    const raw = start + step * hop;\n    if (!wrap && (raw < 0 || raw >= length)) break;\n    // `+ length * hop` keeps the operand of `%` positive without a branch; `hop` is enough\n    // because a single step never moves further than one length.\n    const index = ((raw % length) + length) % length;\n    if (selectable(index)) return index;\n  }\n\n  // Nothing selectable that way. Staying put beats jumping somewhere arbitrary, but a `from`\n  // that is itself disabled must not be reported as highlightable.\n  return from !== null && selectable(from) ? from : null;\n}\n\n/** The first or last selectable index, for Home and End. */\nexport function edgeIndex(edge: 'first' | 'last', length: number, options: ListNavigationOptions = {}): ActiveIndex {\n  return edge === 'first'\n    ? nextIndex(null, 1, length, { ...options, wrap: false })\n    : nextIndex(null, -1, length, { ...options, wrap: false });\n}\n\n/**\n * Scrolls `option` into view inside `list`, moving nothing else on the page.\n *\n * The arrow keys move a highlight, not the focus — the focus stays on the combobox, because that\n * is what `aria-activedescendant` points from. So the browser never scrolls the option into view\n * by itself, and a list taller than its panel would highlight rows the user cannot see.\n *\n * Measured with `getBoundingClientRect` rather than `offsetTop`, which is relative to the nearest\n * positioned ancestor and so silently wrong whenever that is not the scrolling element itself.\n * `scrollIntoView` would be shorter but is free to scroll every ancestor, including the page\n * behind the panel.\n *\n * Rectangles are what the screen shows, and `scrollTop` is in layout pixels, so the two disagree\n * whenever an ancestor carries a scale — which every one of these lists does, because the Popover\n * panel holding them animates in from a smaller size. Measured mid-animation, a list 260 layout\n * pixels tall reports a rectangle of 182, and scrolling by the raw difference moved 30% short\n * every step. `offsetHeight` against the rectangle's height recovers the factor.\n */\nexport function scrollOptionIntoView(list: HTMLElement | null | undefined, option: Element | null | undefined): void {\n  if (!list || !option) return;\n  const listBox = list.getBoundingClientRect();\n  const optionBox = option.getBoundingClientRect();\n  const toLayoutPixels = listBox.height > 0 ? list.offsetHeight / listBox.height : 1;\n  const above = optionBox.top - listBox.top;\n  const below = optionBox.bottom - listBox.bottom;\n  if (above < 0) list.scrollTop += above * toLayoutPixels;\n  else if (below > 0) list.scrollTop += below * toLayoutPixels;\n}\n","import type { CSSProperties, ReactNode } from 'react';\nimport type { SlotClassNames, SlotStyles, HtmlProps } from '../types/slots.types';\nimport type { IconContent } from '../types/icon.types';\nimport type { PopoverAlign, PopoverPlacement } from '../popover/popover.types';\nimport type { Presentation } from '../configProvider/presentation.types';\n\n/** One thing the menu can do. */\nexport interface MenuAction {\n  /** Stable id, also what `onAction` reports. */\n  id: string;\n  label: ReactNode;\n  icon?: IconContent;\n  /**\n   * A keyboard shortcut for this action, in `useHotkeys` spelling: `'mod+k'`,\n   * `'shift+f2'`, `'delete'`. Shown on the trailing edge written for the\n   * platform: `⌘K` on an Apple one, `Ctrl+K` elsewhere.\n   *\n   * The menu draws it and nothing more. Binding is yours, because only the app\n   * knows what else is listening and whether the shortcut should work at all\n   * times or only while the menu is open. `menuHotkeys` turns these entries\n   * into a `useHotkeys` map, so the one spelling covers both ends.\n   */\n  shortcut?: string;\n  /** Extra search terms for `CommandPalette`, which also matches a string `label`. `Menu` ignores them. */\n  keywords?: string[];\n  disabled?: boolean;\n  /** Renders in the destructive colour. For deleting, discarding, revoking. */\n  danger?: boolean;\n  /**\n   * Followed after `onClick` and `onAction`, through `ConfigProvider`'s `navigate` when it is set.\n   * In `Menu` a real `<a>`, so Ctrl/⌘-click opens a new tab.\n   */\n  href?: string;\n  onClick?: () => void;\n}\n\n/** A line between groups of actions. */\nexport interface MenuSeparator {\n  separator: true;\n}\n\n/** A label over the actions that follow it. */\nexport interface MenuHeading {\n  heading: ReactNode;\n}\n\nexport type MenuEntry = MenuAction | MenuSeparator | MenuHeading;\n\nexport function isMenuSeparator(entry: MenuEntry): entry is MenuSeparator {\n  return 'separator' in entry;\n}\n\nexport function isMenuHeading(entry: MenuEntry): entry is MenuHeading {\n  return 'heading' in entry;\n}\n\nexport function isMenuAction(entry: MenuEntry): entry is MenuAction {\n  return !isMenuSeparator(entry) && !isMenuHeading(entry);\n}\n\nexport type MenuSlots =\n  | 'root'\n  | 'trigger'\n  | 'menu'\n  | 'item'\n  | 'itemIcon'\n  | 'itemLabel'\n  | 'itemShortcut'\n  | 'separator'\n  | 'heading';\n\nexport interface MenuProps extends Omit<HtmlProps, 'onSelect'> {\n  /** What the menu offers, in the order it is read. */\n  items: MenuEntry[];\n  /** The element that opens the menu. */\n  children?: ReactNode;\n  /** Called with the id of the action that was picked, after its own `onClick`. */\n  onAction?: (id: string) => void;\n  /** Controlled open state. Leave it out to let the menu manage its own. */\n  isOpen?: boolean;\n  onOpenChange?: (isOpen: boolean) => void;\n  /** Which side of the trigger the menu opens on (default `Bottom`). */\n  placement?: PopoverPlacement;\n  /** Which edge it lines up with on the cross axis (default `Start`). */\n  align?: PopoverAlign;\n  /** Anchored to the trigger, pinned to the bottom of the screen, or the latter only on touch. */\n  presentation?: Presentation;\n  className?: string;\n  style?: CSSProperties;\n  classNames?: SlotClassNames<MenuSlots>;\n  styles?: SlotStyles<MenuSlots>;\n}\n","import type { CSSProperties, ReactNode } from 'react';\n\nimport { followHref } from '../utils/navigation';\nimport { renderIcon } from '../utils/renderIcon';\nimport { isMenuAction, type MenuAction, type MenuEntry } from './menu.types';\n\nexport function hasAnyIcon(items: MenuEntry[]): boolean {\n  return items.some((entry) => isMenuAction(entry) && entry.icon != null);\n}\n\n/** The action's icon, or an empty cell as wide when another action has one, so the labels line up. */\nexport function renderItemIcon(\n  icon: MenuAction['icon'],\n  reserveCell: boolean,\n  props: { className: string; style?: CSSProperties },\n): ReactNode {\n  if (icon != null) return renderIcon(icon, props);\n  return reserveCell ? <span aria-hidden {...props} /> : null;\n}\n\n/**\n * Runs an action the way clicking it does: its own `onClick`, then `onAction`, then its `href`.\n * `follow` is `useFollowHref()` where the caller can reach the router; `menuHotkeys` cannot.\n */\nexport function runMenuAction(\n  action: MenuAction,\n  onAction?: (id: string) => void,\n  follow: (href: string) => void = followHref,\n): void {\n  action.onClick?.();\n  onAction?.(action.id);\n  if (action.href) follow(action.href);\n}\n","import type { ReactNode } from 'react';\nimport { isMenuAction, isMenuHeading, type MenuAction, type MenuEntry } from '../menu/menu.types';\n\nexport interface CommandGroup {\n  heading?: ReactNode;\n  /** Whether a separator started this group, so the divider can be drawn above it. */\n  separated: boolean;\n  actions: MenuAction[];\n}\n\n// Lower-cased and stripped of accents, so \"uber\" finds \"Übersicht\".\nfunction normalize(text: string): string {\n  return text\n    .normalize('NFD')\n    .replace(/\\p{Diacritic}/gu, '')\n    .toLowerCase();\n}\n\n/** Splits entries into groups at each heading and separator, keeping their order. */\nexport function groupCommands(items: MenuEntry[]): CommandGroup[] {\n  const groups: CommandGroup[] = [];\n  let current: CommandGroup = { separated: false, actions: [] };\n  const flush = () => {\n    if (current.heading !== undefined || current.actions.length > 0) groups.push(current);\n  };\n  for (const entry of items) {\n    if (isMenuAction(entry)) {\n      current.actions.push(entry);\n      continue;\n    }\n    flush();\n    current = isMenuHeading(entry)\n      ? { heading: entry.heading, separated: current.separated && current.actions.length === 0, actions: [] }\n      : { separated: true, actions: [] };\n  }\n  flush();\n  return groups.filter((group) => group.actions.length > 0);\n}\n\n/**\n * 0 when the label starts with the query, 1 when a word of the label or a keyword does, 2 for any\n * other match, `null` when a query word is missing from both the label and the keywords.\n *\n * A label that is not a string cannot be read, so such an action is found through its keywords only.\n */\nfunction rank(action: MenuAction, words: string[], query: string): number | null {\n  const label = typeof action.label === 'string' ? normalize(action.label) : '';\n  const keywords = (action.keywords ?? []).map(normalize);\n  const haystack = [label, ...keywords].join(' ');\n  if (!words.every((word) => haystack.includes(word))) return null;\n  if (label.startsWith(query)) return 0;\n  const hasWordStartingWith = (text: string) => text.split(/\\s+/).some((word) => word.startsWith(words[0]));\n  if (hasWordStartingWith(label) || keywords.some(hasWordStartingWith)) return 1;\n  return 2;\n}\n\ninterface RankedGroup {\n  group: CommandGroup;\n  best: number;\n}\n\n/**\n * The groups with only the actions matching `query`, best matches first within each group, and\n * the group holding the best match first. A heading moves with its group, never away from it.\n */\nexport function filterCommands(groups: CommandGroup[], query: string): CommandGroup[] {\n  const normalized = normalize(query.trim());\n  if (!normalized) return groups;\n  const words = normalized.split(/\\s+/);\n\n  // Both sorts rely on `Array.prototype.sort` being stable, so ties keep the order they were given in.\n  return groups\n    .map((group): RankedGroup | null => {\n      const matches = group.actions\n        .map((action) => ({ action, rank: rank(action, words, normalized) }))\n        .filter((match): match is { action: MenuAction; rank: number } => match.rank !== null)\n        .sort((a, b) => a.rank - b.rank);\n      if (matches.length === 0) return null;\n      return {\n        // Separators are dropped while filtering: the headings still say where one group ends.\n        group: { ...group, separated: false, actions: matches.map((match) => match.action) },\n        best: matches[0].rank,\n      };\n    })\n    .filter((ranked): ranked is RankedGroup => ranked !== null)\n    .sort((a, b) => a.best - b.best)\n    .map((ranked) => ranked.group);\n}\n",".commandPalette {\n  --modal-padding: var(--spacing-s);\n  --modal-body-bleed: 0;\n\n  width: 640px;\n\n  /* Near the top rather than centred, so the panel does not jump as the list shrinks while\n     typing. A sheet overrides both with its own doubled selector. */\n  align-self: flex-start;\n  margin-top: 12vh;\n\n  &-search {\n    display: flex;\n    align-items: center;\n    gap: var(--spacing-s);\n    padding: var(--spacing-s) var(--spacing-m);\n    border-bottom: 1px solid var(--border-color);\n\n    &-icon {\n      flex: none;\n      color: var(--text-light);\n    }\n  }\n\n  &-input {\n    flex: 1;\n    min-width: 0;\n    padding: 4px 0;\n    border: none;\n    outline: none;\n    background: transparent;\n    font: inherit;\n    font-size: 1.1em;\n    color: var(--text-color);\n\n    &::placeholder {\n      color: var(--text-light);\n    }\n  }\n\n  &-list {\n    max-height: min(24rem, 60vh);\n    max-height: min(24rem, 60dvh);\n    overflow-y: auto;\n    padding: 4px 0;\n\n    &:empty {\n      display: none;\n    }\n  }\n\n  &-item {\n    display: flex;\n    align-items: center;\n    gap: 10px;\n    padding: 8px 12px;\n    border-radius: var(--default-border-radius);\n    color: var(--text-color);\n    white-space: nowrap;\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n\n    /* Same token as Menu's highlight: `--background-accent-light` matches the modal surface in\n       the default theme. */\n    &-active {\n      background: var(--background-accent);\n    }\n\n    &-danger {\n      color: var(--error-color);\n    }\n\n    &-disabled {\n      opacity: 0.45;\n      cursor: default;\n    }\n\n    &-icon {\n      /* FontAwesome's own rule is unlayered, so it outranks `width` from inside the library's layer and\n         reads this instead. `width` still sizes a custom icon. */\n      --fa-width: 1em;\n\n      flex: none;\n      width: 1em;\n      text-align: center;\n    }\n\n    &-label {\n      flex: 1;\n      overflow: hidden;\n      text-overflow: ellipsis;\n    }\n\n    &-shortcut {\n      flex: none;\n      margin-left: 16px;\n      font-size: 0.85em;\n      color: var(--text-light);\n    }\n  }\n\n  &-heading {\n    padding: 10px 12px 4px;\n    font-size: 0.8em;\n    font-weight: 600;\n    text-transform: uppercase;\n    letter-spacing: 0.04em;\n    color: var(--text-light);\n  }\n\n  &-separator {\n    height: 1px;\n    margin: 4px 0;\n    background: var(--border-color);\n  }\n\n  &-empty {\n    padding: var(--spacing-l) var(--spacing-m);\n    text-align: center;\n    color: var(--text-light);\n  }\n\n  &-status {\n    position: absolute;\n    width: 1px;\n    height: 1px;\n    padding: 0;\n    border: 0;\n    clip-path: inset(50%);\n    overflow: hidden;\n    white-space: nowrap;\n  }\n}\n","import { useEffect, useId, useRef, useState, type KeyboardEvent, type ReactElement } from 'react';\nimport cx from 'classnames';\nimport { faMagnifyingGlass } from '@fortawesome/free-solid-svg-icons';\n\nimport Modal from '../modal';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport { ariaKeyshortcuts, formatHotkey } from '../hooks/useHotkeys';\nimport { edgeIndex, nextIndex, scrollOptionIntoView } from '../utils/listNavigation';\nimport { renderIcon } from '../utils/renderIcon';\nimport { useFollowHref } from '../utils/navigation';\nimport type { MenuAction } from '../menu/menu.types';\nimport { hasAnyIcon, renderItemIcon, runMenuAction } from '../menu/menuItem';\nimport { filterCommands, groupCommands } from './filterCommands';\nimport type { CommandPaletteLabels, CommandPaletteProps } from './commandPalette.types';\nimport styles from './commandPalette.module.pcss';\n\nconst DEFAULT_LABELS: Required<CommandPaletteLabels> = {\n  label: 'Search commands',\n  placeholder: 'Type a command or search…',\n  empty: 'No results',\n  loading: 'Searching…',\n  results: (count) => (count === 1 ? '1 result' : `${count} results`),\n};\n\nfunction CommandPalette(props: CommandPaletteProps): ReactElement {\n  const {\n    items,\n    isOpen,\n    onClose,\n    onAction,\n    onQueryChange,\n    filter = true,\n    isLoading = false,\n    presentation,\n    labels,\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    ...rest\n  } = useComponentDefaults('CommandPalette', props);\n  const text = useLabels('CommandPalette', labels, DEFAULT_LABELS);\n  const listId = useId();\n  const listRef = useRef<HTMLDivElement>(null);\n  const followHref = useFollowHref();\n\n  const [query, setQuery] = useState('');\n  // An id rather than an index, so the highlight stays on the same command while server results\n  // are replaced around it. Typing clears it instead, so Enter runs the best match.\n  const [activeId, setActiveId] = useState<string | null>(null);\n  const [wasOpen, setWasOpen] = useState(isOpen);\n  if (isOpen !== wasOpen) {\n    setWasOpen(isOpen);\n    if (isOpen) {\n      setQuery('');\n      setActiveId(null);\n    }\n  }\n\n  // Read through a ref so an inline handler does not re-fire the effect on every parent render.\n  const onQueryChangeRef = useRef(onQueryChange);\n  useEffect(() => {\n    onQueryChangeRef.current = onQueryChange;\n  });\n  useEffect(() => {\n    if (isOpen) onQueryChangeRef.current?.('');\n  }, [isOpen]);\n\n  const groups = filter ? filterCommands(groupCommands(items), query) : groupCommands(items);\n  // From all items, not the matches, so the labels do not jump sideways while the user types.\n  const hasIcons = hasAnyIcon(items);\n  const actions = groups.flatMap((group) => group.actions);\n  const isDisabled = (index: number) => !!actions[index]?.disabled;\n  const found = actions.findIndex((action) => action.id === activeId && !action.disabled);\n  const activeIndex = found >= 0 ? found : edgeIndex('first', actions.length, { isDisabled });\n  // From the command's id, not its position: an id that stayed the same while the option under it\n  // changed would not make a screen reader announce the new highlight.\n  const optionId = (action: MenuAction) => `${listId}-${encodeURIComponent(action.id)}`;\n  const activeOptionId = activeIndex === null ? undefined : optionId(actions[activeIndex]);\n\n  useEffect(() => {\n    if (!activeOptionId) return;\n    scrollOptionIntoView(listRef.current, document.getElementById(activeOptionId));\n  }, [activeOptionId]);\n\n  function run(action: MenuAction | undefined) {\n    if (!action || action.disabled) return;\n    runMenuAction(action, onAction, followHref);\n    onClose();\n  }\n\n  function handleKeyDown(event: KeyboardEvent<HTMLInputElement>) {\n    // Enter confirms an IME composition before it means anything here.\n    if (event.nativeEvent.isComposing) return;\n    if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n      const next = nextIndex(activeIndex, event.key === 'ArrowDown' ? 1 : -1, actions.length, { isDisabled });\n      setActiveId(next === null ? null : actions[next].id);\n    } else if (event.key === 'Enter') {\n      run(activeIndex === null ? undefined : actions[activeIndex]);\n    } else {\n      return;\n    }\n    event.preventDefault();\n  }\n\n  const hasQuery = query.trim() !== '';\n  const emptyText = isLoading ? text.loading : text.empty;\n  let index = -1;\n\n  return (\n    <Modal\n      {...rest}\n      aria-label={text.label}\n      isOpen={isOpen}\n      onClose={onClose}\n      hideHeader\n      presentation={presentation}\n      className={cx(styles.commandPalette, className, classNames?.root)}\n      style={{ ...style, ...slotStyles?.root }}\n    >\n      <div className={cx(styles.commandPaletteSearch, classNames?.search)} style={slotStyles?.search}>\n        {renderIcon(faMagnifyingGlass, { className: styles.commandPaletteSearchIcon })}\n        <input\n          type='text'\n          role='combobox'\n          aria-label={text.label}\n          aria-expanded='true'\n          aria-controls={listId}\n          aria-autocomplete='list'\n          aria-activedescendant={activeOptionId}\n          autoComplete='off'\n          spellCheck={false}\n          placeholder={text.placeholder}\n          value={query}\n          onChange={(event) => {\n            setQuery(event.target.value);\n            setActiveId(null);\n            onQueryChange?.(event.target.value);\n          }}\n          onKeyDown={handleKeyDown}\n          className={cx(styles.commandPaletteInput, classNames?.input)}\n          style={slotStyles?.input}\n        />\n      </div>\n\n      <div\n        ref={listRef}\n        id={listId}\n        role='listbox'\n        aria-label={text.label}\n        className={cx(styles.commandPaletteList, classNames?.list)}\n        style={slotStyles?.list}\n      >\n        {groups.map((group, groupIndex) => {\n          const headingId = `${listId}-heading-${groupIndex}`;\n          return (\n            <div\n              // Groups carry no id of their own; a new query re-renders the list wholesale anyway.\n              key={groupIndex}\n              role={group.heading === undefined ? 'presentation' : 'group'}\n              aria-labelledby={group.heading === undefined ? undefined : headingId}\n            >\n              {group.separated && groupIndex > 0 && (\n                <div\n                  aria-hidden\n                  className={cx(styles.commandPaletteSeparator, classNames?.separator)}\n                  style={slotStyles?.separator}\n                />\n              )}\n              {group.heading !== undefined && (\n                <div\n                  id={headingId}\n                  role='presentation'\n                  className={cx(styles.commandPaletteHeading, classNames?.heading)}\n                  style={slotStyles?.heading}\n                >\n                  {group.heading}\n                </div>\n              )}\n              {group.actions.map((action) => {\n                index += 1;\n                const own = index;\n                return (\n                  <div\n                    key={action.id}\n                    id={optionId(action)}\n                    role='option'\n                    aria-selected={own === activeIndex}\n                    aria-disabled={action.disabled || undefined}\n                    aria-keyshortcuts={ariaKeyshortcuts(action.shortcut)}\n                    className={cx(\n                      styles.commandPaletteItem,\n                      own === activeIndex && styles.commandPaletteItemActive,\n                      action.danger && styles.commandPaletteItemDanger,\n                      action.disabled && styles.commandPaletteItemDisabled,\n                      classNames?.item,\n                    )}\n                    style={slotStyles?.item}\n                    // Keeps the focus, and so the caret, in the search field.\n                    onMouseDown={(event) => event.preventDefault()}\n                    // Move, not enter: a list scrolling under a resting pointer must not steal the highlight.\n                    onMouseMove={() => !action.disabled && setActiveId(action.id)}\n                    onClick={() => run(action)}\n                  >\n                    {renderItemIcon(action.icon, hasIcons, {\n                      className: cx(styles.commandPaletteItemIcon, classNames?.itemIcon),\n                      style: slotStyles?.itemIcon,\n                    })}\n                    <span\n                      className={cx(styles.commandPaletteItemLabel, classNames?.itemLabel)}\n                      style={slotStyles?.itemLabel}\n                    >\n                      {action.label}\n                    </span>\n                    {action.shortcut != null && (\n                      <span\n                        aria-hidden\n                        className={cx(styles.commandPaletteItemShortcut, classNames?.itemShortcut)}\n                        style={slotStyles?.itemShortcut}\n                      >\n                        {formatHotkey(action.shortcut)}\n                      </span>\n                    )}\n                  </div>\n                );\n              })}\n            </div>\n          );\n        })}\n      </div>\n\n      {hasQuery && actions.length === 0 && (\n        <div className={cx(styles.commandPaletteEmpty, classNames?.empty)} style={slotStyles?.empty}>\n          {emptyText}\n        </div>\n      )}\n      <span className={styles.commandPaletteStatus} role='status' aria-live='polite'>\n        {hasQuery ? (actions.length > 0 ? text.results(actions.length) : emptyText) : ''}\n      </span>\n    </Modal>\n  );\n}\n\nexport default CommandPalette;\n","import { useState, type ReactElement } from 'react';\n\nimport Modal from '../modal';\nimport { ActionButtons } from '../actionButtons';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport type { ConfirmModalLabels, ConfirmModalProps } from './confirmModal.types';\nimport { ThemeColor } from '../themeProvider/color.types';\n\nconst DEFAULT_LABELS: Required<ConfirmModalLabels> = {\n  submit: 'Confirm',\n  cancel: 'Cancel',\n};\n\nfunction ConfirmModal(props: ConfirmModalProps): ReactElement {\n  const {\n    children = null,\n    className = '',\n    onClose = () => {},\n    onConfirm = () => {},\n    title,\n    content = null,\n    isOpen: propIsOpen = undefined,\n    labels,\n    isDelete = false,\n    actionButtonsProps = {},\n    style,\n    ...rest\n  } = useComponentDefaults('ConfirmModal', props);\n  const text = useLabels('ConfirmModal', labels, DEFAULT_LABELS);\n  const [internalOpen, setInternalOpen] = useState(false);\n  const [isSubmitting, setIsSubmitting] = useState(false);\n\n  function handleClose(didSubmit = false) {\n    setInternalOpen(false);\n    onClose(didSubmit);\n  }\n\n  async function handleConfirm() {\n    setIsSubmitting(true);\n    try {\n      await onConfirm();\n      handleClose(true);\n    } catch {\n      // Nothing is awaiting this handler, so a rejection would surface as an unhandled one.\n      // The dialog staying open is the signal that the action did not go through; reporting\n      // the failure itself belongs to whoever wrote `onConfirm`.\n    } finally {\n      setIsSubmitting(false);\n    }\n  }\n\n  const isOpen = propIsOpen === undefined ? internalOpen : propIsOpen;\n\n  const modal = (\n    <Modal\n      title={title}\n      isOpen={isOpen}\n      onClose={() => handleClose()}\n      className={children ? undefined : className}\n      footer={\n        <ActionButtons\n          onClose={() => handleClose()}\n          onSubmit={handleConfirm}\n          labels={{ submit: text.submit, cancel: text.cancel }}\n          submitButtonColor={isDelete ? ThemeColor.Error : undefined}\n          isSubmitButtonLoading={isSubmitting}\n          // The footer slot already supplies the gap above the buttons.\n          noMargin\n          {...actionButtonsProps}\n        />\n      }\n    >\n      {content}\n    </Modal>\n  );\n\n  if (!children) return modal;\n\n  return (\n    <div {...rest} className={className} style={style}>\n      <span onClick={() => setInternalOpen(true)}>{children}</span>\n      {modal}\n    </div>\n  );\n}\n\nexport default ConfirmModal;\n","import { useCallback, useEffect, useRef, useState } from 'react';\n\nexport interface UseClipboardOptions {\n  /** How long `copied` stays `true` after a successful copy, in ms. Default: 2000. */\n  timeout?: number;\n}\n\nexport interface UseClipboardResult {\n  /** Writes `text` to the clipboard. Resolves `true` on success, `false` on failure. */\n  copy: (text: string) => Promise<boolean>;\n  /** `true` for `timeout` ms after a successful copy. */\n  copied: boolean;\n  /** Why the last copy failed, until the next attempt. */\n  error: Error | null;\n  /** Clears `copied` and `error` early. */\n  reset: () => void;\n}\n\n// No `execCommand('copy')` fallback: it is deprecated, and where the async API is missing the\n// caller is better served by `error` than by a copy that may silently do nothing.\nexport function useClipboard({ timeout = 2000 }: UseClipboardOptions = {}): UseClipboardResult {\n  const [copied, setCopied] = useState(false);\n  const [error, setError] = useState<Error | null>(null);\n  const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n  const clearTimer = useCallback(() => {\n    if (timerRef.current) clearTimeout(timerRef.current);\n    timerRef.current = null;\n  }, []);\n\n  useEffect(() => clearTimer, [clearTimer]);\n\n  const reset = useCallback(() => {\n    clearTimer();\n    setCopied(false);\n    setError(null);\n  }, [clearTimer]);\n\n  const copy = useCallback(\n    async (text: string) => {\n      clearTimer();\n      try {\n        if (!navigator.clipboard?.writeText) throw new Error('Clipboard API unavailable');\n        await navigator.clipboard.writeText(text);\n        setError(null);\n        setCopied(true);\n        timerRef.current = setTimeout(() => setCopied(false), timeout);\n        return true;\n      } catch (e) {\n        setCopied(false);\n        setError(e instanceof Error ? e : new Error(String(e)));\n        return false;\n      }\n    },\n    [clearTimer, timeout],\n  );\n\n  return { copy, copied, error, reset };\n}\n\nexport default useClipboard;\n","/* Pinned to a corner because the span has no root of its own to sit in: left at its static\n   position, its containing block can be the page, and inside a scroll container that left\n   empty page to scroll through below it. */\n.copyButton-status {\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 1px;\n  height: 1px;\n  padding: 0;\n  border: 0;\n  clip-path: inset(50%);\n  overflow: hidden;\n  white-space: nowrap;\n}\n","import type { ReactElement } from 'react';\nimport { faCheck, faCopy } from '@fortawesome/free-solid-svg-icons';\n\nimport ActionIcon from '../actionIcon';\nimport AnimatedIcon from '../animatedIcon';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport { useClipboard } from '../hooks/useClipboard';\nimport { isIconDefinition, renderIcon } from '../utils/renderIcon';\nimport type { CopyButtonLabels, CopyButtonProps } from './copyButton.types';\nimport styles from './copyButton.module.pcss';\nimport { ThemeColor } from '../themeProvider/color.types';\n\nconst DEFAULT_LABELS: Required<CopyButtonLabels> = {\n  copy: 'Copy',\n  copied: 'Copied',\n  failed: 'Could not copy',\n};\n\nfunction CopyButton(props: CopyButtonProps): ReactElement {\n  const {\n    value,\n    timeout = 2000,\n    icon = faCopy,\n    copiedIcon = faCheck,\n    onCopy,\n    onError,\n    onClick,\n    color,\n    labels,\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    ...rest\n  } = useComponentDefaults('CopyButton', props);\n  const text = useLabels('CopyButton', labels, DEFAULT_LABELS);\n  const { copy, copied, error } = useClipboard({ timeout });\n\n  const label = copied ? text.copied : text.copy;\n  const current = copied ? copiedIcon : icon;\n  const iconElement =\n    isIconDefinition(icon) && isIconDefinition(copiedIcon) ? (\n      <AnimatedIcon\n        icon={copied ? copiedIcon : icon}\n        classNames={{ icon: classNames?.icon }}\n        styles={{ icon: slotStyles?.icon }}\n      />\n    ) : (\n      renderIcon(current, { className: classNames?.icon, style: slotStyles?.icon })\n    );\n\n  return (\n    <>\n      <ActionIcon\n        {...rest}\n        icon={<>{iconElement}</>}\n        title={label}\n        aria-label={label}\n        color={copied ? ThemeColor.Success : color}\n        className={className}\n        style={style}\n        classNames={{ root: classNames?.root }}\n        styles={{ root: slotStyles?.root }}\n        onClick={async (event) => {\n          onClick?.(event);\n          if (await copy(value)) onCopy?.(value);\n          else onError?.();\n        }}\n      />\n      {/* Beside the button, not in it: a live region inside a button is not reliably announced. */}\n      <span className={styles.copyButtonStatus} role='status' aria-live='polite'>\n        {copied ? text.copied : error ? text.failed : ''}\n      </span>\n    </>\n  );\n}\n\nexport default CopyButton;\n",".datePickerMonth {\n  &-item {\n    aspect-ratio: 1;\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    margin: 1px 0;\n  }\n\n  &-content {\n    display: grid;\n    text-align: center;\n    grid-template-columns: repeat(7, minmax(0, 1fr));\n    gap: 0;\n  }\n\n  &-day {\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n\n    &:hover {\n      /* background-color: var(--primary-color);\n      border-radius: 800px; */\n    }\n\n    &-today {\n      /* Not `--background-accent-light`: it is the same colour as the calendar's\n         own panel in the default theme, which left today unmarked. */\n      background-color: var(--background-accent);\n      border-radius: var(--default-border-radius);\n    }\n\n    &-selected {\n      background-color: var(--primary-color);\n      color: var(--text-on-primary);\n      border-radius: 0;\n\n      &-start {\n        border-top-left-radius: var(--default-border-radius);\n        border-bottom-left-radius: var(--default-border-radius);\n      }\n\n      &-end {\n        border-top-right-radius: var(--default-border-radius);\n        border-bottom-right-radius: var(--default-border-radius);\n      }\n    }\n\n    &-disabled {\n      filter: brightness(0.5);\n      cursor: not-allowed;\n    }\n  }\n\n  &-placeholder {\n    user-select: none;\n    pointer-events: none;\n  }\n}\n","import { dateToDay, daysInMonth, formatDay, parseDay, type DayParts } from '../utils/day';\nimport type { Weekday } from '../utils/weekStart';\n\n// Labels are formatted from a UTC date in UTC, so no time zone can move them onto another day.\n// `setUTCFullYear` rather than `Date.UTC`, which reads the years 0 to 99 as 1900 to 1999.\nfunction utcDate(year: number, month: number, day: number): Date {\n  const date = new Date(0);\n  date.setUTCFullYear(year, month - 1, day);\n  return date;\n}\n\nfunction getMonthName(locale: string | undefined, month: number): string {\n  return utcDate(2023, month, 1).toLocaleDateString(locale, { month: 'long', timeZone: 'UTC' });\n}\n\n// Cached: DatePicker renders on every hover and keystroke, and each name costs a formatter.\nconst monthNames = new Map<string, string[]>();\nconst weekDayNames = new Map<string, string[]>();\n\nexport function getMonthNames(locale: string | undefined): string[] {\n  const key = locale ?? '';\n  let names = monthNames.get(key);\n  if (!names) {\n    names = Array.from(Array(12).keys()).map((index) => getMonthName(locale, index + 1));\n    monthNames.set(key, names);\n  }\n  return names;\n}\n\n/** The seven short weekday names, from `weekStart` on. */\nexport function getWeekDays(locale: string | undefined, weekStart: Weekday): string[] {\n  const key = `${locale ?? ''}|${weekStart}`;\n  let names = weekDayNames.get(key);\n  if (!names) {\n    // 9 April 2023 was a Sunday.\n    names = Array.from(Array(7).keys()).map((index) =>\n      utcDate(2023, 4, 9 + weekStart + index).toLocaleDateString(locale, { weekday: 'short', timeZone: 'UTC' }),\n    );\n    weekDayNames.set(key, names);\n  }\n  return names;\n}\n\n/** The weekday of the 1st, as `Date#getDay()` counts: 0 is Sunday. */\nexport function getStartWeekDayOfMonth(month: number, year: number): Weekday {\n  return utcDate(year, month, 1).getUTCDay() as Weekday;\n}\n\n/** `delta` days from a day, rolling over into the months and years around it. */\nexport function addDays(day: number, month: number, year: number, delta: number): DayParts {\n  let d = day + delta;\n  let m = month;\n  let y = year;\n  while (d < 1) {\n    m -= 1;\n    if (m < 1) {\n      m = 12;\n      y -= 1;\n    }\n    d += daysInMonth({ year: y, month: m });\n  }\n  while (d > daysInMonth({ year: y, month: m })) {\n    d -= daysInMonth({ year: y, month: m });\n    m += 1;\n    if (m > 12) {\n      m = 1;\n      y += 1;\n    }\n  }\n  return { day: d, month: m, year: y };\n}\n\n/** Today in the user's time zone. */\nexport function getToday(): DayParts {\n  return parseDay(dateToDay(new Date()))!;\n}\n\n/** A `'YYYY-MM-DD'` day as the field shows it, `dd.mm.yyyy`, or `''` for anything else. */\nexport function getDateString(value: string | null | undefined): string {\n  const parts = parseDay(value);\n  if (!parts) return '';\n  return `${String(parts.day).padStart(2, '0')}.${String(parts.month).padStart(2, '0')}.${String(parts.year).padStart(4, '0')}`;\n}\n\n/** What the field holds, `dd.mm.yyyy`, as `'YYYY-MM-DD'`; `null` while incomplete, or for a day that does not exist. */\nexport function parseDateString(text: string): string | null {\n  const match = /^(\\d{2})\\.(\\d{2})\\.(\\d{4})$/.exec(text);\n  if (!match) return null;\n  const value = formatDay({ day: Number(match[1]), month: Number(match[2]), year: Number(match[3]) });\n  return parseDay(value) ? value : null;\n}\n\nexport interface DateMaskSelection {\n  start: number;\n  end: number;\n}\n\nexport interface DateMaskState {\n  value: string;\n  selection: DateMaskSelection | null;\n}\n\n/**\n * Auto-corrects a partially typed `dd.mm.yyyy` mask value so a single out-of-range\n * digit doesn't force the user to backtrack:\n * - a day \"tens\" digit > 3 (e.g. typing `4`) becomes `04` and the caret jumps to the month\n * - a day of `3x` where x > 1 (e.g. `35`) becomes `0x`\n * - a month \"tens\" digit > 1 (e.g. `2`) becomes `02` and the caret jumps to the year\n * - a month `1x` where x > 2 (e.g. `13`) is clamped to `12`\n *\n * `previousValue` supplies the digits to retain for the part of the field that isn't\n * being rewritten. The returned `selection` is the adjusted caret position, or the\n * passed-in `selection` when no correction applies.\n */\nexport function correctDateMaskValue(\n  nextValue: string,\n  previousValue: string,\n  selection: DateMaskSelection | null,\n): DateMaskState {\n  let value = nextValue;\n  let nextSelection = selection;\n\n  if (value[0] !== '_') {\n    if (value[0] > '3') {\n      value = `0${value[0]}${previousValue.substring(2)}`;\n      nextSelection = { start: 3, end: 3 };\n    } else if (value[0] === '3') {\n      if (value[1] !== '_' && value[1] > '1') {\n        value = `0${value[1]}${previousValue.substring(2)}`;\n        nextSelection = { start: 3, end: 3 };\n      }\n    }\n  }\n  if (value[3] !== '_' && value[3] > '1') {\n    value = `${value.substring(0, 3)}0${value[3]}${previousValue.substring(5)}`;\n    nextSelection = { start: 6, end: 6 };\n  }\n  if (value[3] === '1' && value[4] !== '_' && value[4] > '2') {\n    value = `${value.substring(0, 3)}${value[3]}2${previousValue.substring(5)}`;\n  }\n\n  return { value, selection: nextSelection };\n}\n","import { useEffect, useRef, useState } from 'react';\nimport cx from 'classnames';\n\nimport styles from './datePickerMonth.module.pcss';\nimport InteractableDiv from 'components/common/interactableDiv';\nimport { dateToDay, daysInMonth, formatDay, parseDay } from 'components/common/utils/day';\nimport { localeWeekStart } from 'components/common/utils/weekStart';\nimport { addDays, getStartWeekDayOfMonth, getToday, getWeekDays } from '../../dateFunctions';\nimport type { DatePickerMonthProps } from './datePickerMonth.types';\n\n// Which day gets the sole tab stop when this month first renders (or when the displayed\n// month changes via the header, not via arrow navigation): the selected day if it falls in\n// this month, else today if it falls in this month, else the 1st.\nfunction getDefaultFocusedDay(month: number, year: number, selectionStart: string | null | undefined): number {\n  const selected = parseDay(selectionStart);\n  if (selected && selected.month === month && selected.year === year) return selected.day;\n  const today = getToday();\n  if (today.month === month && today.year === year) return today.day;\n  return 1;\n}\n\n// `'YYYY-MM-DD'` sorts as it reads, so days compare as plain strings.\nfunction checkSelected(value: string, start: string | null | undefined, end: string | null | undefined) {\n  return {\n    isStartDate: value === start,\n    isEndDate: value === end,\n    isInRange: !!start && !!end && value >= start && value <= end,\n  };\n}\n\nfunction checkDisabled(value: string, minDate: string | null | undefined, maxDate: string | null | undefined): boolean {\n  return (!!minDate && value < minDate) || (!!maxDate && value > maxDate);\n}\n\nexport default function DatePickerMonth({\n  style,\n  className = '',\n  month = getToday().month,\n  year = getToday().year,\n  onSelect = () => null,\n  selectionStart,\n  selectionEnd,\n  minDate = null,\n  maxDate = null,\n  onNavigateMonth,\n  locale,\n  weekStartsOn,\n}: DatePickerMonthProps) {\n  const weekStart = weekStartsOn ?? localeWeekStart(locale);\n  const dayCount = daysInMonth({ year, month });\n  const leadingCells = (getStartWeekDayOfMonth(month, year) - weekStart + 7) % 7;\n  const today = dateToDay(new Date());\n\n  const dayRefs = useRef<Record<number, HTMLDivElement | null>>({});\n  const [focusedDay, setFocusedDay] = useState(() => getDefaultFocusedDay(month, year, selectionStart));\n\n  // A day arrowed onto in an adjacent month, waiting for that month's grid to render so it\n  // can be focused. `onNavigateMonth` only shifts `month`/`year` — the actual DOM focus has\n  // to happen after this component re-renders with the new days, hence the effect below.\n  const pendingFocusDayRef = useRef<number | null>(null);\n\n  useEffect(() => {\n    if (pendingFocusDayRef.current != null) {\n      const day = pendingFocusDayRef.current;\n      pendingFocusDayRef.current = null;\n      setFocusedDay(day);\n      dayRefs.current[day]?.focus();\n    } else {\n      // The displayed month changed some other way (header chevrons, month/year dropdowns) —\n      // re-pick a sensible tab stop, but don't steal focus from whatever the user just used.\n      setFocusedDay(getDefaultFocusedDay(month, year, selectionStart));\n    }\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [month, year]);\n\n  function handleDayKeyDown(event: React.KeyboardEvent<HTMLDivElement>, day: number) {\n    let delta = 0;\n    if (event.key === 'ArrowRight') delta = 1;\n    else if (event.key === 'ArrowLeft') delta = -1;\n    else if (event.key === 'ArrowDown') delta = 7;\n    else if (event.key === 'ArrowUp') delta = -7;\n    else return;\n\n    event.preventDefault();\n    const next = addDays(day, month, year, delta);\n\n    if (next.month !== month || next.year !== year) {\n      pendingFocusDayRef.current = next.day;\n      onNavigateMonth?.(next.month > month || next.year > year ? 1 : -1);\n    } else {\n      setFocusedDay(next.day);\n      dayRefs.current[next.day]?.focus();\n    }\n  }\n\n  return (\n    <div className={cx(styles.datePickerMonth, className)} style={style}>\n      <div className={cx(styles.datePickerMonthContent)}>\n        {getWeekDays(locale, weekStart).map((weekDay, index) => (\n          <div key={index}>{weekDay}</div>\n        ))}\n        {Array.from(Array(leadingCells).keys()).map((index) => (\n          <div key={index} className={cx(styles.datePickerMonthItem, styles.datePickerMonthPlaceholder)} />\n        ))}\n        {Array.from(Array(dayCount).keys()).map((index) => {\n          const day = index + 1;\n          const value = formatDay({ day, month, year });\n          const selectedRes = checkSelected(value, selectionStart, selectionEnd);\n          const isDisabled = checkDisabled(value, minDate, maxDate);\n          const isToday = value === today;\n\n          return (\n            <InteractableDiv\n              key={day}\n              ref={(el) => {\n                dayRefs.current[day] = el;\n              }}\n              tabIndex={day === focusedDay ? 0 : -1}\n              onKeyDown={(event) => handleDayKeyDown(event, day)}\n              onClick={() => (isDisabled ? null : onSelect(value))}\n              className={cx(styles.datePickerMonthDay, styles.datePickerMonthItem, {\n                [styles.datePickerMonthDaySelected]: selectedRes.isInRange,\n                [styles.datePickerMonthDaySelectedStart]: selectedRes.isStartDate,\n                [styles.datePickerMonthDaySelectedEnd]: selectedRes.isEndDate,\n                [styles.datePickerMonthDayDisabled]: isDisabled,\n                [styles.datePickerMonthDayToday]: isToday,\n              })}\n            >\n              {day}\n            </InteractableDiv>\n          );\n        })}\n        {Array.from(Array(42 - leadingCells - dayCount).keys()).map((index) => (\n          <div key={index} className={cx(styles.datePickerMonthItem, styles.datePickerMonthPlaceholder)}>\n            &nbsp;\n          </div>\n        ))}\n      </div>\n    </div>\n  );\n}\n",".required-mark {\n  padding: 0 0 0 4px;\n  color: var(--required-mark-color, var(--error-color));\n}\n\n.dropdown {\n  position: relative;\n\n  /* Nothing inside gives the root a width, so as a flex or grid item it collapsed to the chevron\n     and drew its border through the label. `input` and `multiSelect` say the same. */\n  width: 100%;\n\n  --dropdown-notch-height: 1.75em;\n  --field-label-offset: 0px;\n\n  /* Input's --field-label-offset, for the same reason: a floating label hangs into the field. */\n  &-withLabel {\n    --field-label-offset: 1px;\n  }\n\n  /* See input.module.pcss for why this exists: the visible border lives on `-input`/`-fieldset`\n     below, not this root, so a wrapping ButtonGroup's --group-radius-* custom properties (which\n     inherit through any nesting depth, unlike border-radius itself) are what let this component\n     flatten correctly when it sits inside a ButtonGroup. */\n  --dropdown-radius: var(--group-radius-tl, var(--default-border-radius))\n    var(--group-radius-tr, var(--default-border-radius)) var(--group-radius-br, var(--default-border-radius))\n    var(--group-radius-bl, var(--default-border-radius));\n\n  &-label {\n    display: flex;\n\n    &-column {\n      flex-direction: column;\n    }\n\n    &-text {\n      white-space: nowrap;\n      margin-right: 5px;\n      margin-bottom: 5px;\n      display: flex;\n      align-items: center;\n      color: var(--text-color);\n      font-size: 12px;\n      position: absolute;\n      top: -15px;\n    }\n  }\n\n  &-input {\n    position: relative;\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n    background-color: var(--input-background);\n    border-radius: var(--dropdown-radius);\n\n    &-plain {\n      background: transparent;\n    }\n\n    &-readOnly {\n      cursor: default;\n    }\n\n    &:focus-within {\n      outline: none;\n    }\n\n    &-button {\n      padding: calc(7px + var(--field-label-offset)) 20% calc(7px - var(--field-label-offset)) 7.5px !important;\n      background-color: transparent;\n      border: none;\n      font-family: inherit;\n      font-size: inherit;\n      overflow: hidden;\n      text-overflow: ellipsis;\n      white-space: nowrap;\n      cursor: pointer;\n      -webkit-tap-highlight-color: transparent;\n      text-align: left;\n      width: 100%;\n      line-height: 1.25;\n      color: var(--text-color);\n\n      &-disabled {\n        background-color: var(--disabled-input-color);\n        color: var(--disabled-font-color);\n        border-radius: var(--dropdown-radius);\n      }\n\n      &-readOnly {\n        cursor: default;\n      }\n\n      &:focus {\n        outline: none;\n      }\n\n      &-icon {\n        float: right;\n        background-color: transparent;\n        border: none;\n        position: absolute;\n        outline: none;\n        right: var(--spacing-s);\n        top: calc(50% + var(--field-label-offset));\n        transform: translateY(-50%);\n        color: var(--text-color);\n        cursor: pointer;\n        -webkit-tap-highlight-color: transparent;\n\n        &-arrow {\n          transition: transform 0.25s;\n          pointer-events: none;\n\n          &-open {\n            transform: rotate(180deg);\n          }\n        }\n      }\n    }\n\n    /* Chrome, placement and the portal come from the Popover this renders inside.\n       What is left is the clipping that keeps the first and last row inside the\n       panel's rounded corners. */\n    &-list-anim {\n      overflow: hidden;\n      border-radius: inherit;\n\n      &-wider {\n        width: max-content !important;\n      }\n    }\n\n    /* Passes the sheet's height on to the list, so that the list scrolls rather than the panel:\n       it is what keyboard navigation scrolls and what loading more watches. */\n    &-list-sheet {\n      display: flex;\n      flex-direction: column;\n      min-height: 0;\n    }\n\n    &-list {\n      width: 100%;\n      display: flex;\n      flex-direction: column;\n      overflow-y: auto;\n      max-height: 10em;\n      list-style-type: none;\n\n      &-item {\n        border: none;\n        cursor: pointer;\n        -webkit-tap-highlight-color: transparent;\n        background-color: var(--background-modal);\n        padding: 0.5em;\n        text-align: left;\n        border-radius: 0;\n        transition: background-color 0.3s;\n\n        &-active {\n          background-color: var(--background-accent);\n        }\n\n        &-selected {\n          font-weight: 500;\n        }\n\n        &:focus {\n          outline: none;\n        }\n\n        &:hover {\n          background-color: var(--background-accent);\n        }\n\n        &-disabled {\n          background-color: var(--disabled-input-color);\n          color: var(--disabled-font-color);\n          cursor: not-allowed;\n\n          &:hover {\n            background-color: var(--disabled-input-color);\n          }\n        }\n      }\n    }\n  }\n\n  &-fieldset {\n    position: absolute;\n    width: 100%;\n    margin: 0;\n    box-sizing: border-box;\n    border-radius: var(--dropdown-radius);\n    font-size: 0.8em;\n    inset: calc(var(--dropdown-notch-height) / -2) 0 0;\n    padding-left: 8px;\n    border-width: 1px;\n    border-style: solid;\n    border-color: var(--border-color);\n    pointer-events: none;\n    user-select: none;\n\n    &-legend {\n      display: block;\n\n      /* Not taken from the text: an empty span gets no line box, which raised a label-less\n         field's border half a notch. */\n      height: var(--dropdown-notch-height);\n      max-width: 0;\n      transition: max-width 0.2s;\n      white-space: nowrap;\n      padding: 0;\n\n      &-active {\n        max-width: 300px;\n      }\n\n      span {\n        line-height: var(--dropdown-notch-height);\n        padding: 0 5px;\n        opacity: 0;\n      }\n\n      /* With an empty label the span would still reserve its padding, leaving a gap in\n         the fieldset's top border. Collapse it so the notch closes when there's no label. */\n      span:empty {\n        padding: 0;\n      }\n    }\n  }\n\n  &-placeholder {\n    position: absolute;\n    top: calc(50% + var(--field-label-offset));\n    left: 7.5px;\n    transform: translateY(-50%);\n    transition: all 0.2s;\n    opacity: 0;\n\n    &-shown {\n      animation: 0.2s fadeIn 0.3s forwards;\n    }\n  }\n\n  &-matLabel {\n    position: absolute;\n    transition: all 0.3s;\n    left: 7.5px;\n    right: 0;\n    top: calc(50% + var(--field-label-offset));\n    transform: translateY(-50%);\n    font-size: 1em;\n    color: var(--input-label-resting-color, var(--text-light));\n    pointer-events: none;\n    user-select: none;\n\n    &-floating {\n      inset: 0 0 auto 14px;\n      font-size: 0.8em;\n      color: var(--input-label-floating-color, var(--text-color));\n      transform: translateY(-50%);\n    }\n  }\n\n  @keyframes fadeIn {\n    from {\n      opacity: 0;\n    }\n\n    to {\n      opacity: 0.5;\n    }\n  }\n\n  &-border {\n    &-focused {\n      outline-color: var(--focus-border-color, var(--primary-color));\n      border-color: var(--focus-border-color, var(--primary-color));\n    }\n\n    &-plain:not(&-focused) {\n      border-color: transparent;\n      outline-color: transparent;\n    }\n\n    &-invalid {\n      outline-color: var(--error-border-color);\n      border-color: var(--error-border-color);\n    }\n\n    &-invalid:hover {\n      outline-color: var(--error-border-color);\n      border-color: var(--error-border-color);\n    }\n\n    &-error {\n      outline-color: var(--error-border-color);\n      border-color: var(--error-border-color);\n    }\n  }\n}\n\n/* The list brings its own padding and its rows reach the panel edge. */\n.dropdownPanel {\n  padding: 0;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .dropdown-matLabel,\n  .dropdown-placeholder,\n  .dropdown-input-button-icon-arrow {\n    transition: none;\n  }\n}\n\n/* The sentinel the infinite-scroll hook watches, and the spinner shown while a page loads.\n   These came with `InfiniteBlock` when the list rendered through it; the list is its own scroll\n   container now, so they live here. */\n.dropdown-sentinel {\n  height: 1px;\n}\n\n/* An option's padding around a loader one line of text high, so the row is an option's height. */\n.dropdown-load-more {\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  padding: 0.5em 0;\n}\n","import React, { useEffect, useId, useLayoutEffect, useRef, useState } from 'react';\nimport cx from 'classnames';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faChevronDown as dropdownIcon } from '@fortawesome/free-solid-svg-icons';\nimport Tooltip from 'components/common/tooltip';\n\nimport Popover from '../popover';\nimport Loader from '../loader';\nimport { useInfiniteScroll } from '../infiniteBlock/useInfiniteScroll';\nimport { edgeIndex, nextIndex, scrollOptionIntoView, type ActiveIndex } from '../utils/listNavigation';\nimport { PopoverAlign, PopoverPlacement } from '../popover/popover.types';\nimport { useComponentDefaults, usePresentationDefault } from '../configProvider';\nimport { useSheet } from '../hooks/useSheet';\nimport { Presentation } from '../configProvider/presentation.types';\nimport styles from './dropdown.module.pcss';\nimport sheetStyles from '../styles/sheet.module.pcss';\nimport type { DropdownProps, DropdownItem } from './dropdown.types';\n\n/** How long a pause resets the type-ahead buffer, matching a native `<select>`. */\nconst TYPEAHEAD_RESET_MS = 500;\n\nfunction Dropdown(props: DropdownProps) {\n  const {\n    errorMessage = '',\n    className = '',\n    style,\n    label = '',\n    value = '',\n    isValid = true,\n    tabIndex = 0,\n    isRequired = false,\n    customProps = {},\n    isRow = false,\n    disabled = false,\n    readOnly = false,\n    onLoadMore,\n    onClick,\n    onChange,\n    onFocus: onFocusProp = null,\n    onBlur: onBlurProp = null,\n    onMouseEnter: onMouseEnterProp = null,\n    onMouseLeave: onMouseLeaveProp = null,\n    dropdownLabel: dropdownLabelProp,\n    isDropdownListWider = false,\n    formValidator = null,\n    classNames,\n    styles: slotStyles,\n    items = [],\n    useMatLabelStyle = true,\n    alwaysFloatLabel = false,\n    placeholder = '',\n    plainStyle = false,\n    scrollToSelected = false,\n    presentation: presentationProp,\n    reactRef,\n    // On the field rather than the root, which has no role for a name to belong to.\n    'aria-label': ariaLabel,\n    ...rest\n  } = useComponentDefaults('Dropdown', props);\n  const [isFocused, setIsFocused] = useState(false);\n  const [isHovered, setIsHovered] = useState(false);\n  const [, forceUpdate] = useState(0);\n  const fvRef = useRef(formValidator);\n  const triggerRef = useRef<HTMLDivElement>(null);\n  const anchorRef = useRef<HTMLDivElement>(null);\n  const listRef = useRef<HTMLDivElement>(null);\n  const [menuIsOpen, setMenuIsOpen] = useState(false);\n  // `null` is \"nothing highlighted\", the state a list opens in unless `scrollToSelected`\n  // asks for the current value. Arrowing from there enters the list at either end.\n  const [activeIndex, setActiveIndex] = useState<ActiveIndex>(null);\n  const typeahead = useRef({ buffer: '', at: 0 });\n\n  const baseId = useId();\n  const listboxId = `${baseId}-listbox`;\n  const labelId = `${baseId}-label`;\n  const optionId = (index: number) => `${baseId}-option-${index}`;\n\n  const isInteractive = !disabled && !readOnly;\n  const isOpen = menuIsOpen && isInteractive;\n\n  // The list is its own scroll container. It used to render through an `InfiniteBlock`, which\n  // also brought a tabbable container and a second key handler: inside a listbox that put a\n  // focusable element with no role in the tab order and gave the arrows two owners.\n  // Only while open: the list it watches is rendered then, and not before.\n  const { isLoading: showScrollSpinner, sentinelRef } = useInfiniteScroll(listRef, isOpen ? onLoadMore : undefined);\n\n  // Explicit prop → this component's global default → the app-wide one → anchored.\n  // Resolved here rather than left to Popover so that this component and the panel\n  // it opens can never disagree about whether they are presenting as a sheet.\n  const appPresentation = usePresentationDefault();\n  const presentation = presentationProp ?? appPresentation ?? Presentation.Default;\n  const { isSheet } = useSheet(presentation);\n\n  // The list reads at the field's size, which is not always the page's: a Dropdown\n  // inside something with a smaller font should not open a list at the default one.\n  // Width is Popover's job, through `width='trigger'`.\n  useLayoutEffect(() => {\n    const trigger = triggerRef.current;\n    const menu = anchorRef.current;\n    if (!isOpen || isSheet || !trigger || !menu) return;\n    menu.style.fontSize = window.getComputedStyle(trigger).fontSize;\n  }, [isOpen, isSheet]);\n\n  useEffect(() => {\n    fvRef.current = formValidator;\n    if (!fvRef.current) return;\n    fvRef.current.label = label ?? '';\n    fvRef.current.validate();\n    const listener = () => forceUpdate((n) => n + 1);\n    fvRef.current.registerOnUpdateListener(listener);\n    return () => fvRef.current?.removeOnUpdateListener(listener);\n  }, [formValidator, label]);\n\n  const currentValue = formValidator ? (formValidator.value as string | number) : value;\n  const selectedIndex = items.findIndex((item) => item.key === currentValue);\n\n  useEffect(() => {\n    if (!isOpen || activeIndex === null) return;\n    scrollOptionIntoView(listRef.current, document.getElementById(optionId(activeIndex)));\n    // optionId is derived from a stable useId, so it cannot go stale between renders.\n    // oxlint-disable-next-line react-hooks/exhaustive-deps\n  }, [isOpen, activeIndex]);\n\n  const onMouseEnterHandler = (event: React.MouseEvent) => {\n    setIsHovered(true);\n    onMouseEnterProp?.(event);\n  };\n\n  const onMouseLeaveHandler = (event: React.MouseEvent) => {\n    setIsHovered(false);\n    onMouseLeaveProp?.(event);\n  };\n\n  const onFocusHandler = (event: React.FocusEvent) => {\n    setIsFocused(true);\n    onFocusProp?.(event);\n  };\n\n  const onBlurHandler = (event: React.FocusEvent) => {\n    if (fvRef.current) fvRef.current.touched = true;\n    setIsFocused(false);\n    onBlurProp?.(event);\n  };\n\n  const hasError = (): boolean => {\n    return formValidator ? formValidator.shouldShowError : !isValid || !!errorMessage;\n  };\n\n  const computedIsRequired = (): boolean => {\n    return !!(\n      isRequired || formValidator?.validators.find((v) => (v as { validatorId?: string }).validatorId === 'required')\n    );\n  };\n\n  const onChangeHandler = (val: string | number): void => {\n    if (formValidator) {\n      formValidator.set(val);\n      onChange?.(formValidator.value, customProps);\n    } else {\n      onChange?.(val, customProps);\n    }\n  };\n\n  function handleOpenChange(next: boolean) {\n    if (next && !isInteractive) return;\n    setMenuIsOpen(next);\n    if (next) setActiveIndex(scrollToSelected && selectedIndex >= 0 ? selectedIndex : null);\n    else typeahead.current = { buffer: '', at: 0 };\n  }\n\n  function select(item: DropdownItem) {\n    if (item.disabled) return;\n    onChangeHandler(item.key);\n    handleOpenChange(false);\n  }\n\n  const navigation = { isDisabled: (index: number) => !!items[index].disabled };\n\n  /** Jumps to the option whose label starts with what was typed, as a native `<select>` does. */\n  function handleTypeahead(key: string) {\n    const now = Date.now();\n    const buffer = now - typeahead.current.at > TYPEAHEAD_RESET_MS ? key : typeahead.current.buffer + key;\n    typeahead.current = { buffer, at: now };\n\n    const needle = buffer.toLowerCase();\n    const match = items.findIndex(\n      (item) => !item.disabled && typeof item.label === 'string' && item.label.toLowerCase().startsWith(needle),\n    );\n    if (match === -1) return;\n    if (isOpen) setActiveIndex(match);\n    else select(items[match]);\n  }\n\n  function handleKeyDown(event: React.KeyboardEvent) {\n    if (!isInteractive) return;\n\n    if (!isOpen) {\n      if (event.key === 'ArrowDown' || event.key === 'ArrowUp' || event.key === 'Enter' || event.key === ' ') {\n        // Without this the browser turns the key into a click on the trigger, which\n        // Popover would read as a second toggle and close the list again.\n        event.preventDefault();\n        handleOpenChange(true);\n      } else if (event.key.length === 1) {\n        handleTypeahead(event.key);\n      }\n      return;\n    }\n\n    if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n      event.preventDefault();\n      setActiveIndex(nextIndex(activeIndex, event.key === 'ArrowDown' ? 1 : -1, items.length, navigation));\n    } else if (event.key === 'Home' || event.key === 'End') {\n      event.preventDefault();\n      setActiveIndex(edgeIndex(event.key === 'Home' ? 'first' : 'last', items.length, navigation));\n    } else if (event.key === 'Enter' || event.key === ' ') {\n      event.preventDefault();\n      const item = activeIndex === null ? undefined : items[activeIndex];\n      if (item) select(item);\n      else handleOpenChange(false);\n    } else if (event.key === 'Escape' || event.key === 'Tab') {\n      handleOpenChange(false);\n    } else if (event.key.length === 1) {\n      handleTypeahead(event.key);\n    }\n  }\n\n  let dropdownLabel = dropdownLabelProp;\n  const hasErr = hasError();\n\n  items.forEach((item: DropdownItem) => {\n    if (item.key === currentValue) {\n      dropdownLabel = item.label as string;\n    }\n  });\n\n  const displayValue = dropdownLabel || currentValue;\n  const isDisplayBlank = displayValue == null || (typeof displayValue === 'string' && displayValue.trim() === '');\n\n  const requiredMark = computedIsRequired() ? <span className={styles.requiredMark}>*</span> : null;\n  const editedLabel = label && computedIsRequired() ? `${label} *` : label;\n\n  const showFocus = isFocused && !readOnly;\n\n  return (\n    <div\n      {...rest}\n      className={cx(className, classNames?.root, styles.dropdownLabel, styles.dropdown, {\n        [styles.dropdownLabelColumn]: !isRow,\n        [styles.dropdownWithLabel]: useMatLabelStyle && !!label,\n      })}\n      style={{ ...style, ...slotStyles?.root }}\n      onMouseEnter={onMouseEnterHandler}\n      onMouseLeave={onMouseLeaveHandler}\n    >\n      <div>\n        <Popover\n          // Popover owns opening and closing, including the outside click, Escape and the\n          // click on the field itself. The trigger only has to say what it wants.\n          isOpen={isOpen}\n          onOpenChange={handleOpenChange}\n          disabled={!isInteractive}\n          placement={PopoverPlacement.Bottom}\n          align={PopoverAlign.Start}\n          offset={0}\n          withArrow={false}\n          constrainWidth={false}\n          width='trigger'\n          // The focus stays on the trigger while the list is open, because that is what\n          // `aria-activedescendant` points from.\n          focusOnOpen={false}\n          presentation={presentation}\n          classNames={{ panel: styles.dropdownPanel }}\n          content={\n            <div\n              ref={anchorRef}\n              // A sheet's own panel does the clipping, and spans the screen rather than\n              // sizing itself to the field.\n              className={cx(isSheet ? styles.dropdownInputListSheet : styles.dropdownInputListAnim, {\n                [styles.dropdownInputListAnimWider]: isDropdownListWider && !isSheet,\n              })}\n            >\n              <div\n                ref={listRef}\n                id={listboxId}\n                role='listbox'\n                aria-label={label || ariaLabel || placeholder || undefined}\n                aria-busy={showScrollSpinner || undefined}\n                className={cx(styles.dropdownInputList, isSheet && sheetStyles.sheetScroll, classNames?.list)}\n                style={slotStyles?.list}\n              >\n                {items.map((item: DropdownItem, index: number) => (\n                  <div\n                    key={item.key}\n                    id={optionId(index)}\n                    role='option'\n                    aria-selected={item.key === currentValue}\n                    aria-disabled={item.disabled || undefined}\n                    style={slotStyles?.option}\n                    className={cx(styles.dropdownInputListItem, classNames?.option, {\n                      [styles.dropdownInputListItemActive]: activeIndex === index,\n                      [styles.dropdownInputListItemSelected]: item.key === currentValue,\n                      [styles.dropdownInputListItemDisabled]: !!item.disabled,\n                    })}\n                    // Selecting must not move the focus off the trigger, which is what\n                    // `aria-activedescendant` is anchored to.\n                    onMouseDown={(event) => event.preventDefault()}\n                    onMouseEnter={() => setActiveIndex(index)}\n                    onClick={() => select(item)}\n                  >\n                    {item.label}\n                  </div>\n                ))}\n                <div ref={sentinelRef} className={styles.dropdownSentinel} aria-hidden='true' />\n                {showScrollSpinner && (\n                  <div className={styles.dropdownLoadMore} aria-hidden='true'>\n                    <Loader size='1.125em' />\n                  </div>\n                )}\n              </div>\n            </div>\n          }\n        >\n          <div\n            ref={triggerRef}\n            onFocus={onFocusHandler}\n            onBlur={onBlurHandler}\n            className={cx(\n              styles.dropdownInput,\n              {\n                [styles.dropdownInputPlain]: plainStyle,\n                [styles.dropdownInputReadOnly]: readOnly && !disabled,\n              },\n              classNames?.field,\n            )}\n            style={slotStyles?.field}\n          >\n            <button\n              tabIndex={tabIndex}\n              type='button'\n              disabled={disabled}\n              // A select-only combobox: the accessible name comes from the field's label,\n              // and the element's own content is read as its value. A plain `button` role\n              // would announce the value as the name and never mention the label.\n              role='combobox'\n              aria-haspopup='listbox'\n              aria-expanded={isOpen}\n              aria-controls={listboxId}\n              aria-activedescendant={isOpen && activeIndex !== null ? optionId(activeIndex) : undefined}\n              aria-labelledby={label ? labelId : undefined}\n              aria-label={label ? undefined : ariaLabel || placeholder || undefined}\n              aria-readonly={readOnly || undefined}\n              className={cx(\n                styles.dropdownInputButton,\n                {\n                  [styles.dropdownInputButtonDisabled]: disabled,\n                  [styles.dropdownInputButtonReadOnly]: readOnly && !disabled,\n                },\n                classNames?.control,\n              )}\n              style={{ ...(isDropdownListWider ? { padding: '6px', border: 'none' } : {}), ...slotStyles?.control }}\n              onKeyDown={handleKeyDown}\n              onClick={onClick}\n              ref={reactRef}\n            >\n              {/* Non-breaking space when blank (same text-node layout as a\n                real value) so the button keeps a full line of height. */}\n              {isDisplayBlank ? ' ' : displayValue}\n            </button>\n            <button\n              type='button'\n              tabIndex={-1}\n              disabled={disabled}\n              // The chevron repeats what the combobox beside it already does, so it is a\n              // pointer affordance only. Announcing it would offer the same control twice.\n              aria-hidden='true'\n              className={cx(styles.dropdownInputButtonIcon, classNames?.icon)}\n              style={slotStyles?.icon}\n              // Keeping the focus where it is, rather than letting mousedown move it here:\n              // the trigger is what `aria-activedescendant` points from, and the click\n              // still reaches Popover by bubbling.\n              onMouseDown={(event: React.MouseEvent<HTMLButtonElement>) => event.preventDefault()}\n              onClick={onClick}\n            >\n              {!disabled && !readOnly && (\n                <FontAwesomeIcon\n                  icon={dropdownIcon}\n                  className={cx(styles.dropdownInputButtonIconArrow, {\n                    [styles.dropdownInputButtonIconArrowOpen]: isOpen,\n                  })}\n                />\n              )}\n            </button>\n            <fieldset\n              className={cx(styles.dropdownFieldset, {\n                [styles.dropdownBorderPlain]: plainStyle,\n                [styles.dropdownBorderFocused]: showFocus,\n                [styles.dropdownBorderInvalid]: hasErr,\n                [styles.dropdownBorderError]: hasErr,\n              })}\n            >\n              <legend\n                className={cx(styles.dropdownFieldsetLegend, {\n                  [styles.dropdownFieldsetLegendActive]:\n                    useMatLabelStyle && (alwaysFloatLabel || showFocus || !isDisplayBlank),\n                })}\n              >\n                <span>{editedLabel}</span>\n              </legend>\n            </fieldset>\n            {useMatLabelStyle && label && (\n              <div\n                id={labelId}\n                className={cx(\n                  styles.dropdownMatLabel,\n                  {\n                    [styles.dropdownMatLabelFloating]: alwaysFloatLabel || showFocus || !isDisplayBlank,\n                  },\n                  // The floating and the stacked label are two renderings of\n                  // the same thing, so one slot reaches whichever is in use.\n                  classNames?.label,\n                )}\n                style={slotStyles?.label}\n              >\n                {label}\n                {requiredMark}\n              </div>\n            )}\n            {useMatLabelStyle && placeholder && (\n              <div\n                className={cx(\n                  styles.dropdownPlaceholder,\n                  showFocus && !dropdownLabel && styles.dropdownPlaceholderShown,\n                )}\n              >\n                {placeholder}\n              </div>\n            )}\n          </div>\n        </Popover>\n      </div>\n      {label && !useMatLabelStyle && (\n        <div id={labelId} className={cx(styles.dropdownLabelText, classNames?.label)} style={slotStyles?.label}>\n          {label}\n          {requiredMark}\n        </div>\n      )}\n      <Tooltip\n        variant='error'\n        message={formValidator ? formValidator.getCurrentErrorMessage() : errorMessage || ''}\n        isVisible={hasErr && (isFocused || isHovered)}\n      />\n    </div>\n  );\n}\n\nexport default Dropdown;\n","/**\n * A fixed-width digit mask: `99:99`, `99.99.9999`.\n *\n * Fixed means every character keeps its position. Clearing a digit in the middle leaves a hole\n * rather than pulling the rest left, which is what a date or a time wants: deleting the month\n * out of `11.11.2025` must not turn the year into the month.\n *\n * `9` marks a slot that takes a digit. Every other character is a literal the caret steps over\n * and the user cannot type into.\n */\n\nconst DIGIT_SLOT = '9';\n\nexport interface MaskSelection {\n  start: number;\n  end: number;\n}\n\nexport interface MaskState {\n  value: string;\n  selection: MaskSelection | null;\n}\n\nconst isDigit = (character: string | undefined): boolean =>\n  character !== undefined && character >= '0' && character <= '9';\n\n/** The mask indices that take a digit, in order. */\nexport function slotIndices(mask: string): number[] {\n  const slots: number[] = [];\n  for (let index = 0; index < mask.length; index++) if (mask[index] === DIGIT_SLOT) slots.push(index);\n  return slots;\n}\n\n/** The mask with every slot empty: `__.__.____`. */\nexport function emptyMask(mask: string, placeholder: string): string {\n  return [...mask].map((character) => (character === DIGIT_SLOT ? placeholder : character)).join('');\n}\n\n/**\n * Any string as one character per mask index, so slots can be read and written by position.\n *\n * Every other function here assumes a value exactly as long as the mask, with literals where the\n * mask has them. Putting a value through this is what guarantees that, whether it came from a\n * keystroke, from a consumer's state, or from nothing at all.\n */\nexport function toMask(value: string, mask: string, placeholder: string): string {\n  return [...mask]\n    .map((maskCharacter, index) => {\n      if (maskCharacter !== DIGIT_SLOT) return maskCharacter;\n      return isDigit(value[index]) ? value[index] : placeholder;\n    })\n    .join('');\n}\n\n/** Where the caret belongs so the next digit typed lands in `slot`. */\nfunction caretForSlot(slot: number, slots: number[], mask: string): number {\n  return slot >= slots.length ? mask.length : slots[slot];\n}\n\n/** The first slot at or after `index`, or the slot count when there is none. */\nfunction slotAtOrAfter(index: number, slots: number[]): number {\n  const found = slots.findIndex((position) => position >= index);\n  return found === -1 ? slots.length : found;\n}\n\n/** The last slot strictly before `index`, or -1. */\nfunction slotBefore(index: number, slots: number[]): number {\n  for (let slot = slots.length - 1; slot >= 0; slot--) if (slots[slot] < index) return slot;\n  return -1;\n}\n\n/**\n * Where the caret belongs when the field is entered: the first slot with no digit in it.\n *\n * A mask is as wide when empty as when full, so a click lands wherever it likes, and clicking at\n * the end of `__.__.____` would otherwise leave the caret past every slot with nowhere to put the\n * next digit.\n */\nexport function firstEmptyCaret(value: string, mask: string, placeholder: string): number {\n  const slots = slotIndices(mask);\n  const filled = toMask(value, mask, placeholder);\n  const empty = slots.findIndex((index) => !isDigit(filled[index]));\n  return empty === -1 ? mask.length : slots[empty];\n}\n\n/** One edit, as the region of the old value it replaced and the text it put there. */\nfunction editOf(previous: string, raw: string, caret: number) {\n  // Everything after the caret is untouched, so the replaced region ends exactly as far from the\n  // end of the old value as the caret is from the end of the new one. This is what the caret is\n  // for: a prefix/suffix diff alone cannot tell a forward Delete from a Backspace one place along,\n  // nor typing `1` over a `1` from typing nothing at all.\n  const to = Math.max(0, Math.min(previous.length, previous.length - (raw.length - caret)));\n\n  let from = 0;\n  while (from < to && from < caret && previous[from] === raw[from]) from++;\n\n  return { from, to, inserted: raw.slice(from, caret) };\n}\n\n/**\n * What the field should show after the browser has changed its raw text, and where the caret\n * belongs.\n *\n * Works from the edit rather than from key events, so a paste, a drag, a replaced selection, an\n * autofill and a keystroke all arrive the same way.\n */\nexport function maskChange(previous: string, raw: string, caret: number, mask: string, placeholder: string): MaskState {\n  const slots = slotIndices(mask);\n  const characters = [...toMask(previous, mask, placeholder)];\n  const at = (slot: number) => caretForSlot(slot, slots, mask);\n  const held = (from: number, to: number) => slots.filter((index) => index >= from && index < to);\n\n  const { from, to, inserted } = editOf(previous, raw, caret);\n  const digits = [...inserted].filter(isDigit);\n\n  // Something was typed that the mask has no room for: a separator the mask already draws, a\n  // letter, a symbol. The old library dropped these, and so does this — writing nothing would\n  // still be wrong, because the browser has already put the character in the field.\n  if (inserted.length > 0 && digits.length === 0) {\n    const caretSlot = slotAtOrAfter(from, slots);\n    return { value: characters.join(''), selection: { start: at(caretSlot), end: at(caretSlot) } };\n  }\n\n  if (inserted.length === 0) {\n    const touched = held(from, to);\n\n    // Backspace over a literal reads as deleting only that literal, which the user cannot have\n    // meant: nothing they typed is there. Take the digit in front of it instead, the way the\n    // caret would have if it had never been allowed to rest on a literal.\n    const previousSlot = slotBefore(from, slots);\n    const target = touched.length > 0 ? touched : previousSlot === -1 ? [] : [slots[previousSlot]];\n\n    for (const index of target) characters[index] = placeholder;\n    const caretSlot = touched.length > 0 ? slotAtOrAfter(from, slots) : Math.max(previousSlot, 0);\n    return { value: characters.join(''), selection: { start: at(caretSlot), end: at(caretSlot) } };\n  }\n\n  // A replaced selection clears what it covered first, so typing over a highlighted month does\n  // not leave its second digit behind.\n  for (const index of held(from, to)) characters[index] = placeholder;\n\n  let slot = slotAtOrAfter(from, slots);\n  // Typed past the last slot: a mask is as wide empty as full, so a click can leave the caret\n  // there with nothing after it. Fall back to the first slot still free, which is where the digit\n  // was going to end up anyway. A value with no free slot is left alone, as it should be.\n  if (slot >= slots.length) {\n    const free = slots.findIndex((index) => !isDigit(characters[index]));\n    if (free !== -1) slot = free;\n  }\n\n  for (const digit of digits) {\n    if (slot >= slots.length) break;\n    characters[slots[slot]] = digit;\n    slot++;\n  }\n\n  return { value: characters.join(''), selection: { start: at(slot), end: at(slot) } };\n}\n\n/** Whether a masked value holds no digits at all, so the field reads as empty. */\nexport function isBlank(value: string, mask: string): boolean {\n  return !slotIndices(mask).some((index) => isDigit(value[index]));\n}\n","import { useLayoutEffect, useRef, useState } from 'react';\nimport type React from 'react';\n\nimport { mergeRefs } from '../utils/mergeRefs';\nimport { firstEmptyCaret, isBlank, maskChange, toMask } from './mask';\nimport type { MaskedInputProps } from './maskedInput.types';\n\n/**\n * A text input under a fixed digit mask, used by `DatePicker` and `TimeInput` through `Input`'s\n * `customInput`. Internal: consumers reach it through those two.\n *\n * Controlled, with one exception. An empty field shows nothing until it is focused, and then shows\n * the bare mask, so a form full of `__.__.____` does not greet someone who has typed nothing. That\n * mask is display only and is never reported through `onChange`, or every untouched date field\n * would submit a value made of underscores.\n */\nfunction MaskedInput(props: MaskedInputProps) {\n  const {\n    mask,\n    maskPlaceholder = '_',\n    value = '',\n    onChange,\n    beforeMaskedStateChange,\n    onFocus,\n    onBlur,\n    onClick,\n    ref,\n    ...rest\n  } = props;\n\n  const inputRef = useRef<HTMLInputElement>(null);\n  const [isFocused, setIsFocused] = useState(false);\n  /**\n   * Where the caret belongs after the value the parent holds comes back to us. Set during the\n   * change and applied on the next paint, because the browser puts the caret at the end of any\n   * value it did not compute itself.\n   */\n  const caretRef = useRef<number | null>(null);\n\n  useLayoutEffect(() => {\n    if (caretRef.current === null) return;\n    inputRef.current?.setSelectionRange(caretRef.current, caretRef.current);\n    caretRef.current = null;\n  });\n\n  const blank = isBlank(value, mask);\n  // Everything below assumes a value exactly as long as the mask. The prop is whatever the parent\n  // holds, which on a first render can be a date from somewhere else entirely, so it is put\n  // through the mask rather than trusted.\n  const display = blank ? (isFocused ? toMask('', mask, maskPlaceholder) : '') : toMask(value, mask, maskPlaceholder);\n\n  const setCaret = (position: number) => {\n    caretRef.current = position;\n    inputRef.current?.setSelectionRange(position, position);\n  };\n\n  const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {\n    const input = event.target;\n    const raw = input.value;\n    const nextState = maskChange(display, raw, input.selectionStart ?? raw.length, mask, maskPlaceholder);\n\n    const state = beforeMaskedStateChange\n      ? beforeMaskedStateChange({ previousState: { value: display, selection: null }, nextState })\n      : nextState;\n\n    const next = toMask(state.value, mask, maskPlaceholder);\n    const caret = state.selection?.start ?? firstEmptyCaret(next, mask, maskPlaceholder);\n\n    // Nothing came of it — a letter typed into an empty field, or a separator the mask already\n    // draws. React re-renders nothing when the value it holds has not changed, so the caret has\n    // to be put back here; the layout effect will not run.\n    if (next === display) {\n      input.value = display;\n      setCaret(caret);\n      return;\n    }\n\n    caretRef.current = caret;\n    onChange?.(isBlank(next, mask) ? '' : next);\n  };\n\n  const handleFocus = (event: React.FocusEvent<HTMLInputElement>) => {\n    setIsFocused(true);\n    // Only when the mask is about to appear. The field showed nothing a moment ago, so whatever\n    // caret the browser picked was measured against an empty string. A field with something in it\n    // keeps the caret where the user put it, and `handleClick` pulls it back only if it landed\n    // past the last free slot.\n    if (blank) caretRef.current = 0;\n    onFocus?.(event);\n  };\n\n  /**\n   * A click can leave the caret past every slot, because a mask is as wide empty as full. Runs on\n   * click rather than on focus: the browser sets the selection on mouse *up*, after focus, so at\n   * focus time there is nothing reliable to read.\n   */\n  const handleClick = (event: React.MouseEvent<HTMLInputElement>) => {\n    const input = event.currentTarget;\n    const limit = firstEmptyCaret(display, mask, maskPlaceholder);\n    // A drag that selected something is left alone; so is a caret already inside the filled part.\n    if (input.selectionStart === input.selectionEnd && (input.selectionStart ?? 0) > limit) {\n      input.setSelectionRange(limit, limit);\n    }\n    onClick?.(event);\n  };\n\n  const handleBlur = (event: React.FocusEvent<HTMLInputElement>) => {\n    setIsFocused(false);\n    onBlur?.(event);\n  };\n\n  return (\n    <input\n      {...rest}\n      ref={mergeRefs(inputRef, ref)}\n      value={display}\n      onChange={handleChange}\n      onFocus={handleFocus}\n      onBlur={handleBlur}\n      onClick={handleClick}\n      inputMode='numeric'\n    />\n  );\n}\n\nexport default MaskedInput;\n",".datePicker {\n  position: relative;\n  width: 100%;\n  flex: 1;\n  min-width: 105px;\n\n  &-header {\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n    margin: 8px 0;\n\n    &-monthAndYear {\n      display: flex;\n\n      &-container {\n        display: flex;\n        gap: 16px;\n        position: relative;\n      }\n    }\n\n    &-month {\n      width: 120px;\n    }\n\n    &-year {\n      width: 75px;\n    }\n  }\n\n  &-month {\n    min-width: 185px;\n  }\n\n  &-modalInput {\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n    color: var(--text-color);\n    -webkit-text-fill-color: var(--text-color);\n  }\n}\n","import React, { useState, useEffect, useRef } from 'react';\nimport cx from 'classnames';\nimport Input from '../input';\nimport { useComponentDefaults, useLabels, useLocale } from '../configProvider';\nimport { validLocale } from '../configProvider/validLocale';\nimport DatePickerMonth from './components/datePickerMonth';\nimport { faChevronLeft, faChevronRight } from '@fortawesome/free-solid-svg-icons';\nimport ActionIcon from '../actionIcon';\nimport Modal from '../modal';\nimport Popover from '../popover';\nimport Dropdown from '../dropdown';\nimport { PopoverAlign, PopoverPlacement } from '../popover/popover.types';\nimport MaskedInput from '../maskedInput';\nimport type { BeforeMaskedStateChangeStates, MaskState } from '../maskedInput';\nimport { asDay, parseDay } from '../utils/day';\nimport { getMonthNames, getDateString, getToday, parseDateString, correctDateMaskValue } from './dateFunctions';\nimport Button from '../button';\nimport Tooltip from '../tooltip';\nimport type { FormValidator } from 'services/formValidation';\nimport type { DatePickerLabels, DatePickerProps } from './datePicker.types';\n\nimport styles from './datePicker.module.pcss';\n\n/**\n * Why `value` is not a day, or `null` when it is one or is empty. Days were `Date`s before 0.39,\n * and stored as their JSON, a timestamp; either still gets through a loosely typed validator or\n * plain JavaScript, and would otherwise just show an empty field.\n */\nfunction notADay(value: unknown): string | null {\n  if (value === null || value === undefined || value === '' || asDay(value)) return null;\n  if (Object.prototype.toString.call(value) === '[object Date]') return 'a Date. Convert it with dateToDay()';\n  return `${JSON.stringify(value)}, which is not a day`;\n}\n\nconst DEFAULT_LABELS: Required<DatePickerLabels> = {\n  previousMonth: 'Previous month',\n  nextMonth: 'Next month',\n  clear: 'Clear',\n  month: 'Month',\n  year: 'Year',\n};\n\nexport default function DatePicker(props: DatePickerProps) {\n  const {\n    className = '',\n    selected,\n    onSelect = () => null,\n    formValidator = null,\n    noInput = false,\n    noHeader = false,\n    isModal = false,\n    isInline = false,\n    onChange = () => null,\n    minDate = null,\n    maxDate = null,\n    children = null,\n    showClearButton = false,\n    label = '',\n    isValid = true,\n    errorMessage = '',\n    isRequired = false,\n    startDate,\n    endDate,\n    locale: localeProp,\n    weekStartsOn,\n    labels,\n    style,\n    classNames,\n    styles: slotStyles,\n    ...rest\n  } = useComponentDefaults('DatePicker', props);\n  const text = useLabels('DatePicker', labels, DEFAULT_LABELS);\n  const appLocale = useLocale();\n  const locale = validLocale(localeProp, 'DatePicker') ?? appLocale;\n  const getCurrValue = (): string | null => asDay(formValidator ? formValidator.value : selected);\n  const minDay = asDay(minDate);\n  const maxDay = asDay(maxDate);\n\n  const initialValue = getCurrValue();\n  const initialDay = parseDay(initialValue) ?? getToday();\n\n  const [currentMonth, setCurrentMonth] = useState(initialDay.month);\n  const [currentYear, setCurrentYear] = useState(initialDay.year);\n  const [isPickerOpen, setIsPickerOpen] = useState(false);\n  const [currentDateString, setCurrentDateString] = useState(() => getDateString(initialValue));\n  const [isFocused, setIsFocused] = useState(false);\n  const [isHovered, setIsHovered] = useState(false);\n  const [, forceUpdate] = useState(0);\n  const [prevSelected, setPrevSelected] = useState(selected);\n\n  const fvRef = useRef(formValidator);\n\n  // Sync navigation view when `selected` prop changes externally\n  if (prevSelected !== selected) {\n    setPrevSelected(selected);\n    const shown = parseDay(selected) ?? getToday();\n    setCurrentMonth(shown.month);\n    setCurrentYear(shown.year);\n    setCurrentDateString(getDateString(selected));\n  }\n\n  // Once per prop: a `new Date()` written inline is a new object on every render.\n  const warned = useRef(new Set<string>());\n  const validatorValue: unknown = formValidator?.value;\n  useEffect(() => {\n    const values = { selected, formValidator: validatorValue, minDate, maxDate, startDate, endDate };\n    for (const [name, value] of Object.entries(values)) {\n      const problem = notADay(value);\n      if (!problem || warned.current.has(name)) continue;\n      warned.current.add(name);\n      console.warn(\n        `DatePicker: \\`${name}\\` takes a day as a 'YYYY-MM-DD' string, and got ${problem}. ` +\n          `See \"Migration from Date values\" in docs/DatePicker.md.`,\n      );\n    }\n  }, [selected, validatorValue, minDate, maxDate, startDate, endDate]);\n\n  // Subscribe to formValidator updates (external system → setState in callback)\n  useEffect(() => {\n    fvRef.current = formValidator;\n    if (!formValidator) return;\n    formValidator.label = label;\n    formValidator.validate();\n    const listener = () => {\n      const shown = parseDay(formValidator.value);\n      if (shown) {\n        setCurrentMonth(shown.month);\n        setCurrentYear(shown.year);\n      }\n      setCurrentDateString(getDateString(formValidator.value));\n      forceUpdate((n: number) => n + 1);\n    };\n    formValidator.registerOnUpdateListener(listener);\n    return () => formValidator.removeOnUpdateListener(listener);\n  }, [formValidator, label]);\n\n  const hasError = (): boolean => {\n    return formValidator ? formValidator.shouldShowError : !isValid || !!errorMessage;\n  };\n\n  const computedIsRequired = (): boolean => {\n    return !!(\n      isRequired || formValidator?.validators.find((v) => (v as { validatorId?: string }).validatorId === 'required')\n    );\n  };\n\n  const handleChange = (day: string | null) => {\n    setCurrentDateString(getDateString(day));\n    // Typed as a union so each validator TypeScript infers for a day fits; all of them hold one.\n    if (formValidator) (formValidator as FormValidator<string | null>).set(day);\n    onChange(day);\n  };\n\n  const handleSelect = (day: string | null) => {\n    handleChange(day);\n    onSelect(day);\n    setIsPickerOpen(false);\n  };\n\n  const changeMonth = (delta: number) => {\n    const index = currentYear * 12 + currentMonth - 1 + delta;\n    const year = Math.floor(index / 12);\n    // A day is written with four digits, so the calendar stops at the years it can name.\n    if (year < 0 || year > 9999) return;\n    setCurrentYear(year);\n    setCurrentMonth((index % 12) + 1);\n  };\n\n  const tryParseDateString = (text: string): string | null => {\n    const day = parseDateString(text);\n    // `'YYYY-MM-DD'` sorts as it reads, so the bounds compare as plain strings.\n    if (!day || (minDay && day < minDay) || (maxDay && day > maxDay)) return null;\n    return day;\n  };\n\n  const beforeMaskedStateChange = ({ previousState, nextState }: BeforeMaskedStateChangeStates): MaskState => {\n    // A change that moved nothing needs no correcting, and re-running the day/month padding on\n    // an unchanged value would fight the caret the mask just placed.\n    if (nextState.value === previousState.value) return nextState;\n\n    const corrected = correctDateMaskValue(nextState.value, previousState.value, nextState.selection ?? null);\n\n    const parsed = tryParseDateString(corrected.value);\n    if (parsed) handleChange(parsed);\n\n    return corrected;\n  };\n\n  const getYearDropdownOptions = () => {\n    let length = 400;\n    let zeroOffset = getToday().year - 200;\n    const min = parseDay(minDay);\n    const max = parseDay(maxDay);\n    if (min) zeroOffset = min.year;\n    // Never negative: a `maxDate` before `minDate` made `Array(length)` throw during render.\n    if (max) length = Math.max(0, max.year - zeroOffset + 1);\n    return Array.from(Array(length).keys()).map((i) => ({ key: i + zeroOffset, label: i + zeroOffset }));\n  };\n\n  const currValue = getCurrValue();\n  const hasErr = hasError();\n\n  const header = noHeader ? null : (\n    <div className={cx(styles.datePickerHeader, classNames?.header)} style={slotStyles?.header}>\n      <ActionIcon icon={faChevronLeft} aria-label={text.previousMonth} onClick={() => changeMonth(-1)} />\n      <div className={styles.datePickerHeaderMonthAndYearContainer}>\n        <div className={styles.datePickerHeaderMonthAndYear}>\n          <Dropdown\n            plainStyle\n            scrollToSelected\n            aria-label={text.month}\n            onChange={(v) => setCurrentMonth(v as number)}\n            className={cx(styles.datePickerHeaderMonth, classNames?.monthDropdown)}\n            style={slotStyles?.monthDropdown}\n            value={currentMonth}\n            items={getMonthNames(locale).map((month, index) => ({ key: index + 1, label: month }))}\n          />\n          <Dropdown\n            plainStyle\n            scrollToSelected\n            useMatLabelStyle={false}\n            aria-label={text.year}\n            className={cx(styles.datePickerHeaderYear, classNames?.yearDropdown)}\n            style={slotStyles?.yearDropdown}\n            onChange={(v) => setCurrentYear(v as number)}\n            value={currentYear}\n            items={getYearDropdownOptions()}\n          />\n        </div>\n      </div>\n      <ActionIcon icon={faChevronRight} aria-label={text.nextMonth} onClick={() => changeMonth(1)} />\n    </div>\n  );\n\n  const pickerContent = (\n    <>\n      {header}\n      <DatePickerMonth\n        className={cx(styles.datePickerMonth, classNames?.calendar)}\n        style={slotStyles?.calendar}\n        month={currentMonth}\n        year={currentYear}\n        onSelect={handleSelect}\n        selectionStart={asDay(startDate) ?? currValue}\n        selectionEnd={asDay(endDate) ?? currValue}\n        minDate={minDay}\n        maxDate={maxDay}\n        onNavigateMonth={changeMonth}\n        locale={locale}\n        weekStartsOn={weekStartsOn}\n      />\n      {showClearButton && (\n        <Button onClick={() => handleSelect(null)} className={classNames?.clearButton} style={slotStyles?.clearButton}>\n          {text.clear}\n        </Button>\n      )}\n      {children}\n    </>\n  );\n\n  const input = noInput ? null : (\n    <Input\n      autoComplete='off'\n      className={classNames?.input}\n      style={slotStyles?.input}\n      readOnly={isModal}\n      classNames={isModal ? { input: styles.datePickerModalInput } : undefined}\n      label={`${label}${computedIsRequired() ? ' *' : ''}`}\n      value={currentDateString}\n      onChange={(v) => setCurrentDateString(v as string)}\n      onClick={() => setIsPickerOpen(true)}\n      customInput={<MaskedInput mask='99.99.9999' beforeMaskedStateChange={beforeMaskedStateChange} />}\n      onBlur={() => {\n        if (fvRef.current) fvRef.current.touched = true;\n        setIsFocused(false);\n        // Only an edit is written back. A value the field cannot show, a leftover Date or a\n        // timestamp, shows as empty, and writing that back would erase it on a mere tab-through.\n        if (currentDateString !== getDateString(getCurrValue())) handleChange(tryParseDateString(currentDateString));\n      }}\n      onFocus={() => setIsFocused(true)}\n      onMouseEnter={() => setIsHovered(true)}\n      onMouseLeave={() => setIsHovered(false)}\n      isValid={!hasErr}\n    />\n  );\n\n  const picker = (() => {\n    if (isInline) return pickerContent;\n    if (isModal) {\n      return (\n        <Modal hideHeader isOpen={isPickerOpen} onClose={() => setIsPickerOpen(false)}>\n          {pickerContent}\n        </Modal>\n      );\n    }\n    return (\n      <Popover\n        isOpen={isPickerOpen}\n        onOpenChange={(open: boolean) => setIsPickerOpen(open)}\n        content={pickerContent}\n        placement={PopoverPlacement.Bottom}\n        align={PopoverAlign.Start}\n        // A click to place the caret in the field must not take the panel away.\n        // The focus stays there on its own: Popover only follows a keyboard open.\n        closeOnTriggerClick={false}\n        constrainWidth={false}\n      >\n        {input}\n      </Popover>\n    );\n  })();\n\n  return (\n    <div\n      {...rest}\n      className={cx(styles.datePicker, className, classNames?.root)}\n      style={{ ...style, ...slotStyles?.root }}\n    >\n      {(isModal || isInline) && input}\n      {picker}\n      <Tooltip\n        variant='error'\n        message={formValidator ? formValidator.getCurrentErrorMessage() : errorMessage || ''}\n        isVisible={hasErr && (isFocused || isHovered)}\n      />\n    </div>\n  );\n}\n","import type { CSSProperties, ReactNode } from 'react';\nimport type { SlotClassNames, SlotStyles, HtmlProps } from '../types/slots.types';\n\nexport enum DividerTextPosition {\n  Left = 'left',\n  Center = 'center',\n  Right = 'right',\n}\n\nexport enum DividerOrientation {\n  Horizontal = 'horizontal',\n  Vertical = 'vertical',\n}\n\nexport type DividerSlots = 'root' | 'line' | 'content';\n\nexport interface DividerProps extends HtmlProps {\n  text?: ReactNode;\n  textPosition?: DividerTextPosition;\n  orientation?: DividerOrientation;\n  className?: string;\n  style?: CSSProperties;\n  classNames?: SlotClassNames<DividerSlots>;\n  styles?: SlotStyles<DividerSlots>;\n}\n",".divider {\n  display: flex;\n  align-items: center;\n  width: 100%;\n\n  &-line {\n    flex: 1 1 auto;\n    height: 1px;\n    background-color: var(--border-color);\n  }\n\n  &-content {\n    flex: 0 0 auto;\n    padding: 0 0.75em;\n    color: var(--text-dark);\n    font-size: 0.875em;\n    white-space: nowrap;\n  }\n\n  &-text-left &-line:first-child {\n    flex-grow: 0;\n    flex-basis: 1.5em;\n  }\n\n  &-text-right &-line:last-child {\n    flex-grow: 0;\n    flex-basis: 1.5em;\n  }\n\n  &-vertical {\n    flex-direction: column;\n    width: auto;\n    align-self: stretch;\n  }\n\n  &-vertical &-line {\n    width: 1px;\n    height: auto;\n  }\n\n  &-vertical &-content {\n    padding: 0.75em 0;\n  }\n}\n","import type { ReactElement } from 'react';\nimport cx from 'classnames';\n\nimport { DividerTextPosition, DividerOrientation } from './divider.types';\nimport type { DividerProps } from './divider.types';\nimport styles from './divider.module.pcss';\nimport { useComponentDefaults } from '../configProvider';\n\nconst CLASS_BY_TEXT_POSITION: Record<DividerTextPosition, string> = {\n  [DividerTextPosition.Left]: styles.dividerTextLeft,\n  [DividerTextPosition.Center]: styles.dividerTextCenter,\n  [DividerTextPosition.Right]: styles.dividerTextRight,\n};\n\nfunction Divider(props: DividerProps): ReactElement {\n  const {\n    text,\n    textPosition = DividerTextPosition.Center,\n    orientation = DividerOrientation.Horizontal,\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    ...rest\n  } = useComponentDefaults('Divider', props);\n\n  const hasText = text != null && text !== '';\n  const isVertical = orientation === DividerOrientation.Vertical;\n\n  return (\n    <div\n      {...rest}\n      role='separator'\n      aria-orientation={isVertical ? 'vertical' : undefined}\n      className={cx(\n        styles.divider,\n        isVertical && styles.dividerVertical,\n        hasText && CLASS_BY_TEXT_POSITION[textPosition],\n        className,\n        classNames?.root,\n      )}\n      style={{ ...style, ...slotStyles?.root }}\n    >\n      <span className={cx(styles.dividerLine, classNames?.line)} style={slotStyles?.line} />\n      {hasText && (\n        <span className={cx(styles.dividerContent, classNames?.content)} style={slotStyles?.content}>\n          {text}\n        </span>\n      )}\n      {hasText && <span className={cx(styles.dividerLine, classNames?.line)} style={slotStyles?.line} />}\n    </div>\n  );\n}\n\nexport default Divider;\n",".drawer {\n  height: 100%;\n  overflow-x: hidden;\n  width: var(--drawer-closed-width, 0);\n  transition: width 0.3s ease;\n  background: var(--background-accent);\n\n  &-open {\n    width: var(--drawer-opened-width, var(--drawer-content-width, max-content));\n  }\n\n  &-content {\n    width: max-content;\n    height: 100%;\n    pointer-events: all;\n\n    &-inner {\n      height: 100%;\n    }\n  }\n}\n\n/* Opening and closing then snaps to the new width instead of sliding to it. */\n@media (prefers-reduced-motion: reduce) {\n  .drawer {\n    transition: none;\n  }\n}\n","import type { ReactElement, CSSProperties } from 'react';\nimport { useRef, useEffect } from 'react';\nimport { DrawerProps } from './drawer.types';\nimport cx from 'classnames';\n\nimport styles from './drawer.module.pcss';\n\nfunction Drawer({\n  className,\n  isOpen = false,\n  closedWidth,\n  openedWidth,\n  children,\n  style,\n  classNames,\n  styles: slotStyles,\n  ...rest\n}: DrawerProps): ReactElement {\n  const containerRef = useRef<HTMLDivElement>(null);\n  const contentRef = useRef<HTMLDivElement>(null);\n\n  useEffect(() => {\n    const observer = new ResizeObserver(() => {\n      if (containerRef.current && contentRef.current) {\n        containerRef.current.style.setProperty('--drawer-content-width', `${contentRef.current.scrollWidth}px`);\n      }\n    });\n    if (contentRef.current) observer.observe(contentRef.current);\n    return () => observer.disconnect();\n  }, []);\n\n  return (\n    <div\n      {...rest}\n      ref={containerRef}\n      className={cx(styles.drawer, isOpen && styles.drawerOpen, className, classNames?.root)}\n      style={\n        {\n          ...(closedWidth !== undefined && { '--drawer-closed-width': closedWidth }),\n          ...(openedWidth !== undefined && { '--drawer-opened-width': openedWidth }),\n          ...style,\n          ...slotStyles?.root,\n        } as CSSProperties\n      }\n    >\n      <div ref={contentRef} className={cx(styles.drawerContent, classNames?.content)} style={slotStyles?.content}>\n        <div className={cx(styles.drawerContentInner, classNames?.inner)} style={slotStyles?.inner}>\n          {children}\n        </div>\n      </div>\n    </div>\n  );\n}\n\nexport default Drawer;\n","import type React from 'react';\nimport type { SlotClassNames, SlotStyles, HtmlProps } from '../types/slots.types';\n\nexport enum DropZoneStatus {\n  /** Nothing is happening. */\n  Idle = 'idle',\n  /** A file drag is somewhere in the window — the zone is \"armed\". */\n  Dragging = 'dragging',\n  /** A file drag is hovering directly over this zone. */\n  Active = 'active',\n  /** The last drop / selection was rejected. */\n  Error = 'error',\n}\n\nexport enum DropZoneReturnType {\n  /** Emit the raw `File` objects (default). */\n  File = 'file',\n  /** Read each file as a base64 data URL and emit `LoadedFile`. */\n  Base64 = 'base64',\n  /** Read each file as UTF-8 text and emit `LoadedFile`. */\n  Text = 'text',\n}\n\nexport interface LoadedFile {\n  name: string;\n  size: number;\n  type: string;\n  lastModified: number;\n  /** The FileReader result — a base64 data URL or text, depending on `returnType`. */\n  data: string;\n}\n\nexport type DropZoneRejectionReason =\n  /** File extension / mime type not in `acceptedTypes`. */\n  | 'unacceptedType'\n  /** File exceeds `maxSize`. */\n  | 'tooLarge'\n  /** More files than `maxFiles` were provided. */\n  | 'tooMany'\n  /** Reading the file (base64 / text) failed. */\n  | 'readError';\n\nexport interface DropZoneRejection {\n  file: File;\n  reason: DropZoneRejectionReason;\n}\n\nexport interface DropZoneRenderState {\n  status: DropZoneStatus;\n  /** A drag is anywhere in the window (zone armed) or directly over it. */\n  isDragging: boolean;\n  /** A drag is directly over this zone. */\n  isActive: boolean;\n  disabled: boolean;\n}\n\nexport type DropZoneSlots = 'root' | 'content' | 'fileList' | 'fileItem' | 'input';\n\nexport interface DropZoneProps extends Omit<HtmlProps, 'children'> {\n  /** Static content, or a render function that receives the live drop state. */\n  children?: React.ReactNode | ((state: DropZoneRenderState) => React.ReactNode);\n\n  /**\n   * Accepted file types: extensions (`'.png'`), mime types (`'image/png'`),\n   * or wildcards (`'image/*'`). Empty / omitted means accept everything.\n   * Also drives the native picker's `accept` attribute.\n   */\n  acceptedTypes?: string[];\n  /** Output shape for each accepted file. Default: `File`. */\n  returnType?: DropZoneReturnType;\n\n  /** Open the native file picker when the zone is clicked. */\n  openPickerOnClick?: boolean;\n  /** Allow more than one file. Default: `true`. */\n  multiple?: boolean;\n  /** Reject files beyond this count (extras reported via `onReject`). */\n  maxFiles?: number;\n  /** Reject files larger than this many bytes. */\n  maxSize?: number;\n  /** Accept files pasted from the clipboard while the zone is focused. */\n  enablePaste?: boolean;\n  /** Recurse into dropped directories. Default: `true`. */\n  allowDirectories?: boolean;\n\n  /** Native `capture` attribute (mobile camera / mic). */\n  capture?: boolean | 'user' | 'environment';\n  disabled?: boolean;\n\n  /** Drop the built-in border / background styling; keep only base layout. */\n  unstyled?: boolean;\n  /** Render the names of `files` beneath the content. */\n  showFileNames?: boolean;\n  /** Files whose names are listed when `showFileNames` is set. */\n  files?: (File | LoadedFile)[];\n\n  onFilesChanged?: (files: (File | LoadedFile)[]) => void;\n  onReject?: (rejections: DropZoneRejection[]) => void;\n  onStatusChanged?: (status: DropZoneStatus) => void;\n\n  className?: string;\n  style?: React.CSSProperties;\n  classNames?: SlotClassNames<DropZoneSlots>;\n  styles?: SlotStyles<DropZoneSlots>;\n}\n",".dropZone {\n  position: relative;\n  box-sizing: border-box;\n  width: 100%;\n  height: 100%;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  outline: none;\n\n  &-clickable {\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n  }\n\n  &-disabled {\n    opacity: 0.5;\n    cursor: not-allowed;\n    pointer-events: none;\n  }\n\n  &-default {\n    padding: var(--spacing-l, 24px);\n    border: 1px dashed var(--border-color);\n    border-radius: var(--default-border-radius);\n    background: transparent;\n    color: var(--text-dark);\n    transition:\n      border-color 0.2s,\n      background 0.2s,\n      color 0.2s;\n  }\n\n  &-default&-clickable:hover {\n    border-color: var(--primary-color);\n    background: var(--background-accent-light);\n  }\n\n  /* A drag is somewhere in the window — the zone is armed. */\n  &-dragging {\n    border-color: var(--primary-color);\n  }\n\n  /* A drag is hovering directly over this zone. */\n  &-active {\n    border-width: 2px;\n    border-color: var(--primary-color);\n    background: var(--background-accent-light);\n    color: var(--text-color);\n  }\n\n  &-error {\n    border-color: var(--error-color);\n    color: var(--error-color);\n  }\n\n  &-error&-clickable:hover {\n    border-color: var(--error-color);\n    background: var(--error-lighter);\n  }\n\n  &-content {\n    width: 100%;\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    justify-content: center;\n\n    /* While dragging, let drop events fall through to the root instead of child elements. */\n    &-dragging {\n      pointer-events: none;\n    }\n  }\n\n  &-fileList {\n    width: 100%;\n    margin-top: var(--spacing-s, 8px);\n    pointer-events: none;\n  }\n\n  &-fileItem {\n    font-size: var(--text-small-font-size);\n    color: var(--text-dark);\n    text-overflow: ellipsis;\n    overflow: hidden;\n    white-space: nowrap;\n  }\n\n  &-input {\n    display: none;\n  }\n}\n","import React, { useCallback, useEffect, useId, useRef, useState } from 'react';\nimport cx from 'classnames';\n\nimport {\n  DropZoneProps,\n  DropZoneReturnType,\n  DropZoneStatus,\n  DropZoneRejection,\n  DropZoneRenderState,\n  LoadedFile,\n} from './dropZone.types';\n\nimport styles from './dropZone.module.pcss';\nimport { isSelfActivation } from '../utils/activationKey';\n\n// ── Pure helpers ──────────────────────────────────────────────────────────────\n\n/** True when a file matches one of the accepted tokens (extension, mime, or wildcard). */\nfunction matchesAccepted(file: File, acceptedTypes: string[]): boolean {\n  if (!acceptedTypes.length) return true;\n  const name = file.name.toLowerCase();\n  const type = file.type.toLowerCase();\n  return acceptedTypes.some((raw) => {\n    const accept = raw.trim().toLowerCase();\n    if (!accept) return false;\n    if (accept.startsWith('.')) return name.endsWith(accept); // extension\n    if (accept.endsWith('/*')) return type.startsWith(accept.slice(0, -1)); // wildcard e.g. image/\n    return type === accept; // exact mime type\n  });\n}\n\ntype ReadResult = { ok: true; value: File | LoadedFile } | { ok: false };\n\n/** Read a single file into the requested return shape. Never rejects — failures resolve to `{ ok: false }`. */\nfunction readFile(file: File, returnType: DropZoneReturnType): Promise<ReadResult> {\n  if (returnType === DropZoneReturnType.File) {\n    return Promise.resolve({ ok: true, value: file });\n  }\n  return new Promise<ReadResult>((resolve) => {\n    const reader = new FileReader();\n    reader.onload = (event) => {\n      const data = event.target?.result;\n      if (typeof data !== 'string') {\n        resolve({ ok: false });\n        return;\n      }\n      resolve({\n        ok: true,\n        value: { name: file.name, size: file.size, type: file.type, lastModified: file.lastModified, data },\n      });\n    };\n    reader.onerror = () => resolve({ ok: false });\n    if (returnType === DropZoneReturnType.Base64) reader.readAsDataURL(file);\n    else reader.readAsText(file);\n  });\n}\n\n/** Recursively flatten a dropped file-system entry into a list of Files. */\nfunction readEntry(entry: FileSystemEntry): Promise<File[]> {\n  return new Promise((resolve) => {\n    if (entry.isFile) {\n      (entry as FileSystemFileEntry).file(\n        (file) => resolve([file]),\n        () => resolve([]),\n      );\n    } else if (entry.isDirectory) {\n      const reader = (entry as FileSystemDirectoryEntry).createReader();\n      const collected: FileSystemEntry[] = [];\n      const readBatch = () => {\n        // readEntries yields entries in chunks — keep calling until it returns empty.\n        reader.readEntries(\n          (batch) => {\n            if (!batch.length) {\n              Promise.all(collected.map(readEntry)).then((nested) => resolve(nested.flat()));\n            } else {\n              collected.push(...batch);\n              readBatch();\n            }\n          },\n          () => resolve([]),\n        );\n      };\n      readBatch();\n    } else {\n      resolve([]);\n    }\n  });\n}\n\n/**\n * Extract Files from a drop's DataTransfer. `webkitGetAsEntry()` must be called\n * synchronously within the drop handler, so all entries are captured before any await.\n */\nfunction gatherFiles(dataTransfer: DataTransfer, allowDirectories: boolean): Promise<File[]> {\n  const items = dataTransfer.items;\n  if (items && items.length) {\n    if (allowDirectories && typeof items[0].webkitGetAsEntry === 'function') {\n      const entries: FileSystemEntry[] = [];\n      for (let i = 0; i < items.length; i += 1) {\n        if (items[i].kind === 'file') {\n          const entry = items[i].webkitGetAsEntry();\n          if (entry) entries.push(entry);\n        }\n      }\n      if (entries.length) {\n        return Promise.all(entries.map(readEntry)).then((nested) => nested.flat());\n      }\n    }\n    const files: File[] = [];\n    for (let i = 0; i < items.length; i += 1) {\n      if (items[i].kind === 'file') {\n        const file = items[i].getAsFile();\n        if (file) files.push(file);\n      }\n    }\n    return Promise.resolve(files);\n  }\n  return Promise.resolve(Array.from(dataTransfer.files || []));\n}\n\n// ── Component ───────────────────────────────────────────────────────────────\n\nfunction DropZone({\n  children,\n  acceptedTypes = [],\n  returnType = DropZoneReturnType.File,\n  openPickerOnClick = false,\n  multiple = true,\n  maxFiles,\n  maxSize,\n  enablePaste = false,\n  allowDirectories = true,\n  capture,\n  disabled = false,\n  unstyled = false,\n  showFileNames = false,\n  files,\n  onFilesChanged,\n  onReject,\n  onStatusChanged,\n  className,\n  style,\n  classNames,\n  styles: slotStyles,\n  ...rest\n}: DropZoneProps) {\n  const [status, setStatus] = useState<DropZoneStatus>(DropZoneStatus.Idle);\n\n  const inputRef = useRef<HTMLInputElement>(null);\n  const inputId = useId();\n  const statusRef = useRef(status);\n  const windowDragDepth = useRef(0);\n  const zoneDragDepth = useRef(0);\n\n  // Latest props/callbacks, so the once-mounted window listeners never read stale values.\n  const cfg = useRef({\n    acceptedTypes,\n    returnType,\n    multiple,\n    maxFiles,\n    maxSize,\n    allowDirectories,\n    disabled,\n    onFilesChanged,\n    onReject,\n    onStatusChanged,\n  });\n  useEffect(() => {\n    cfg.current = {\n      acceptedTypes,\n      returnType,\n      multiple,\n      maxFiles,\n      maxSize,\n      allowDirectories,\n      disabled,\n      onFilesChanged,\n      onReject,\n      onStatusChanged,\n    };\n  });\n\n  const changeStatus = useCallback((next: DropZoneStatus) => {\n    if (statusRef.current === next) return;\n    statusRef.current = next;\n    setStatus(next);\n    cfg.current.onStatusChanged?.(next);\n  }, []);\n\n  const processFiles = useCallback(\n    async (incoming: File[]) => {\n      const c = cfg.current;\n      const rejections: DropZoneRejection[] = [];\n      let accepted: File[] = [];\n\n      for (const file of incoming) {\n        if (!matchesAccepted(file, c.acceptedTypes)) {\n          rejections.push({ file, reason: 'unacceptedType' });\n        } else if (c.maxSize != null && file.size > c.maxSize) {\n          rejections.push({ file, reason: 'tooLarge' });\n        } else {\n          accepted.push(file);\n        }\n      }\n\n      if (!c.multiple) accepted = accepted.slice(0, 1);\n      if (c.maxFiles != null && accepted.length > c.maxFiles) {\n        accepted.slice(c.maxFiles).forEach((file) => rejections.push({ file, reason: 'tooMany' }));\n        accepted = accepted.slice(0, c.maxFiles);\n      }\n\n      // Read in input order — Promise.all preserves index regardless of completion order.\n      const results = await Promise.all(accepted.map((file) => readFile(file, c.returnType)));\n      const loaded: (File | LoadedFile)[] = [];\n      results.forEach((result, index) => {\n        if (result.ok) loaded.push(result.value);\n        else rejections.push({ file: accepted[index], reason: 'readError' });\n      });\n\n      if (rejections.length) c.onReject?.(rejections);\n      changeStatus(rejections.length && !loaded.length ? DropZoneStatus.Error : DropZoneStatus.Idle);\n      if (loaded.length) c.onFilesChanged?.(loaded);\n    },\n    [changeStatus],\n  );\n\n  // Window-level drag tracking via an enter/leave depth counter (robust against bubbling).\n  useEffect(() => {\n    const hasFiles = (dt: DataTransfer | null) => !dt || Array.from(dt.types || []).includes('Files');\n\n    const onWindowDragEnter = (e: DragEvent) => {\n      if (cfg.current.disabled || !hasFiles(e.dataTransfer)) return;\n      windowDragDepth.current += 1;\n      if (statusRef.current === DropZoneStatus.Idle || statusRef.current === DropZoneStatus.Error) {\n        changeStatus(DropZoneStatus.Dragging);\n      }\n    };\n    const onWindowDragLeave = () => {\n      windowDragDepth.current = Math.max(0, windowDragDepth.current - 1);\n      if (windowDragDepth.current === 0 && statusRef.current === DropZoneStatus.Dragging) {\n        changeStatus(DropZoneStatus.Idle);\n      }\n    };\n    const onWindowDrop = () => {\n      windowDragDepth.current = 0;\n      zoneDragDepth.current = 0;\n      if (statusRef.current === DropZoneStatus.Dragging) changeStatus(DropZoneStatus.Idle);\n    };\n\n    window.addEventListener('dragenter', onWindowDragEnter);\n    window.addEventListener('dragleave', onWindowDragLeave);\n    window.addEventListener('drop', onWindowDrop);\n    return () => {\n      window.removeEventListener('dragenter', onWindowDragEnter);\n      window.removeEventListener('dragleave', onWindowDragLeave);\n      window.removeEventListener('drop', onWindowDrop);\n    };\n  }, [changeStatus]);\n\n  const onDragOver = useCallback(\n    (e: React.DragEvent<HTMLDivElement>) => {\n      e.preventDefault();\n      e.stopPropagation();\n      if (e.dataTransfer) e.dataTransfer.dropEffect = disabled ? 'none' : 'copy';\n    },\n    [disabled],\n  );\n\n  const onDragEnter = useCallback(\n    (e: React.DragEvent<HTMLDivElement>) => {\n      e.preventDefault();\n      e.stopPropagation();\n      if (disabled) return;\n      zoneDragDepth.current += 1;\n      changeStatus(DropZoneStatus.Active);\n    },\n    [disabled, changeStatus],\n  );\n\n  const onDragLeave = useCallback(\n    (e: React.DragEvent<HTMLDivElement>) => {\n      e.preventDefault();\n      e.stopPropagation();\n      zoneDragDepth.current = Math.max(0, zoneDragDepth.current - 1);\n      if (zoneDragDepth.current === 0) {\n        // Left the zone, but a drag may still be active elsewhere in the window.\n        changeStatus(windowDragDepth.current > 0 ? DropZoneStatus.Dragging : DropZoneStatus.Idle);\n      }\n    },\n    [changeStatus],\n  );\n\n  const onDrop = useCallback(\n    (e: React.DragEvent<HTMLDivElement>) => {\n      e.preventDefault();\n      e.stopPropagation();\n      zoneDragDepth.current = 0;\n      windowDragDepth.current = 0;\n      if (disabled || !e.dataTransfer) {\n        changeStatus(DropZoneStatus.Idle);\n        return;\n      }\n      changeStatus(DropZoneStatus.Idle);\n      // gatherFiles captures any directory entries synchronously before awaiting.\n      gatherFiles(e.dataTransfer, cfg.current.allowDirectories).then((collected) => {\n        if (collected.length) processFiles(collected);\n      });\n    },\n    [disabled, changeStatus, processFiles],\n  );\n\n  const openPicker = useCallback(() => {\n    if (disabled || !openPickerOnClick) return;\n    inputRef.current?.click();\n  }, [disabled, openPickerOnClick]);\n\n  const onInputChange = useCallback(\n    (e: React.ChangeEvent<HTMLInputElement>) => {\n      const selected = Array.from(e.target.files || []);\n      // Reset so picking the same file again still fires onChange.\n      e.target.value = '';\n      if (selected.length) processFiles(selected);\n    },\n    [processFiles],\n  );\n\n  const onPaste = useCallback(\n    (e: React.ClipboardEvent<HTMLDivElement>) => {\n      if (!enablePaste || disabled) return;\n      const pasted = Array.from(e.clipboardData?.files || []);\n      if (pasted.length) {\n        e.preventDefault();\n        processFiles(pasted);\n      }\n    },\n    [enablePaste, disabled, processFiles],\n  );\n\n  const onKeyDown = useCallback(\n    (e: React.KeyboardEvent<HTMLDivElement>) => {\n      if (!openPickerOnClick || disabled) return;\n      if (!isSelfActivation(e)) return;\n      e.preventDefault();\n      openPicker();\n    },\n    [openPickerOnClick, disabled, openPicker],\n  );\n\n  const isDragging = status === DropZoneStatus.Dragging || status === DropZoneStatus.Active;\n  const renderState: DropZoneRenderState = {\n    status,\n    isDragging,\n    isActive: status === DropZoneStatus.Active,\n    disabled,\n  };\n\n  const acceptAttr = acceptedTypes.length ? acceptedTypes.join(',') : undefined;\n\n  return (\n    <div\n      {...rest}\n      className={cx(\n        styles.dropZone,\n        openPickerOnClick && styles.dropZoneClickable,\n        disabled && styles.dropZoneDisabled,\n        !unstyled && styles.dropZoneDefault,\n        !unstyled && status === DropZoneStatus.Dragging && styles.dropZoneDragging,\n        !unstyled && status === DropZoneStatus.Active && styles.dropZoneActive,\n        !unstyled && status === DropZoneStatus.Error && styles.dropZoneError,\n        classNames?.root,\n        className,\n      )}\n      style={{ ...slotStyles?.root, ...style }}\n      onDragEnter={onDragEnter}\n      onDragLeave={onDragLeave}\n      onDragOver={onDragOver}\n      onDrop={onDrop}\n      onClick={openPicker}\n      onKeyDown={onKeyDown}\n      onPaste={enablePaste ? onPaste : undefined}\n      role={openPickerOnClick ? 'button' : undefined}\n      tabIndex={openPickerOnClick && !disabled ? 0 : undefined}\n      aria-disabled={disabled || undefined}\n    >\n      <div\n        className={cx(styles.dropZoneContent, isDragging && styles.dropZoneContentDragging, classNames?.content)}\n        style={slotStyles?.content}\n      >\n        {typeof children === 'function' ? children(renderState) : children}\n      </div>\n\n      {showFileNames && files && files.length > 0 && (\n        <div className={cx(styles.dropZoneFileList, classNames?.fileList)} style={slotStyles?.fileList}>\n          {files.map((file, index) => (\n            <div\n              key={`${file.name}-${index}`}\n              className={cx(styles.dropZoneFileItem, classNames?.fileItem)}\n              style={slotStyles?.fileItem}\n            >\n              {file.name}\n            </div>\n          ))}\n        </div>\n      )}\n\n      <input\n        ref={inputRef}\n        id={inputId}\n        type='file'\n        className={cx(styles.dropZoneInput, classNames?.input)}\n        style={slotStyles?.input}\n        accept={acceptAttr}\n        multiple={multiple}\n        capture={capture}\n        disabled={disabled}\n        onChange={onInputChange}\n        tabIndex={-1}\n        aria-hidden='true'\n      />\n    </div>\n  );\n}\n\nexport default DropZone;\n","@keyframes errorBoundaryBadgeIn {\n  from {\n    opacity: 0;\n    transform: scale(0.6);\n  }\n\n  to {\n    opacity: 1;\n    transform: scale(1);\n  }\n}\n\n@keyframes errorBoundaryFadeUp {\n  from {\n    opacity: 0;\n    transform: translateY(6px);\n  }\n\n  to {\n    opacity: 1;\n    transform: translateY(0);\n  }\n}\n\n.errorBoundary {\n  position: relative;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 20px;\n  padding: 36px 28px;\n  border-radius: var(--default-border-radius);\n  background: var(--background-accent);\n  border: 1px solid var(--border-color);\n  box-shadow: var(--card-shadow);\n  color: var(--text-color);\n  font-family: var(--default-font);\n  box-sizing: border-box;\n  overflow: hidden;\n  isolation: isolate;\n\n  &::before {\n    content: '';\n    position: absolute;\n    top: -30px;\n    left: 50%;\n    width: 260px;\n    height: 180px;\n    transform: translateX(-50%);\n    background: radial-gradient(closest-side, color-mix(in srgb, var(--error-color) 16%, transparent), transparent);\n    pointer-events: none;\n    z-index: -1;\n  }\n\n  &Header {\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    gap: 8px;\n    text-align: center;\n    max-width: 420px;\n  }\n\n  &Badge {\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    width: 52px;\n    height: 52px;\n    border-radius: 50%;\n    background: var(--error-lighter);\n    box-shadow: 0 0 0 6px color-mix(in srgb, var(--error-color) 8%, transparent);\n    margin-bottom: 4px;\n    animation: errorBoundaryBadgeIn 0.35s ease-out;\n  }\n\n  &Icon {\n    font-size: 20px;\n    color: var(--error-color, #ef4444);\n  }\n\n  &Title {\n    font-weight: var(--header-font-weight, 700);\n    font-size: 17px;\n    animation: errorBoundaryFadeUp 0.3s ease-out 0.05s backwards;\n  }\n\n  &Description {\n    color: var(--text-dark);\n    font-size: 14px;\n    line-height: 1.5;\n    word-break: break-word;\n    animation: errorBoundaryFadeUp 0.3s ease-out 0.1s backwards;\n  }\n\n  &Stack {\n    align-self: stretch;\n    width: 100%;\n    text-align: left;\n    animation: errorBoundaryFadeUp 0.3s ease-out 0.15s backwards;\n  }\n\n  &StackHeader {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    gap: 10px;\n    margin-bottom: 10px;\n  }\n\n  &StackLabel {\n    font-family: var(--monospace-font, monospace);\n    font-size: 11px;\n    font-weight: 700;\n    letter-spacing: 0.02em;\n    text-transform: uppercase;\n    color: var(--error-color, #ef4444);\n    background: var(--error-lighter);\n    padding: 3px 8px;\n    border-radius: 999px;\n  }\n\n  &CopyButton {\n    flex-shrink: 0;\n  }\n\n  &StackTrace {\n    margin: 0;\n    padding: 12px;\n    border-radius: var(--default-border-radius);\n    background: var(--background);\n    color: var(--text-dark);\n    font-family: var(--monospace-font, monospace);\n    font-size: 12px;\n    line-height: 1.6;\n    white-space: pre-wrap;\n    word-break: break-word;\n    max-height: 320px;\n    overflow: auto;\n  }\n\n  &Actions {\n    display: flex;\n    justify-content: center;\n    animation: errorBoundaryFadeUp 0.3s ease-out 0.2s backwards;\n  }\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .errorBoundaryBadge,\n  .errorBoundaryTitle,\n  .errorBoundaryDescription,\n  .errorBoundaryStack,\n  .errorBoundaryActions {\n    animation: none;\n  }\n}\n","import React from 'react';\nimport cx from 'classnames';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faTriangleExclamation, faArrowRotateRight } from '@fortawesome/free-solid-svg-icons';\n\nimport Button from '../button';\nimport Accordion from '../accordion';\nimport { showToast } from '../toast';\nimport CopyButton from '../copyButton';\nimport type { ErrorBoundaryLabels, ErrorBoundaryProps, ErrorBoundaryState } from './errorBoundary.types';\nimport styles from './errorBoundary.module.pcss';\nimport { ThemeColor } from '../themeProvider/color.types';\n\nconst DEFAULT_LABELS: Required<ErrorBoundaryLabels> = {\n  stackTrace: 'Stack trace',\n  copy: 'Copy',\n  copied: 'Copied',\n  reset: 'Try again',\n  copySuccessToast: 'Stack trace copied to clipboard',\n  copyErrorToast: 'Could not copy stack trace',\n};\n\nfunction formatStackTrace(error: Error, errorInfo: React.ErrorInfo | null): string {\n  const parts = [error.stack ?? `${error.name}: ${error.message}`];\n  if (errorInfo?.componentStack) parts.push(`Component stack:${errorInfo.componentStack}`);\n  return parts.join('\\n\\n');\n}\n\nclass ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {\n  state: ErrorBoundaryState = { error: null, errorInfo: null };\n\n  static getDerivedStateFromError(error: Error): Partial<ErrorBoundaryState> {\n    return { error };\n  }\n\n  componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void {\n    this.setState({ errorInfo });\n    this.props.onError?.(error, errorInfo);\n  }\n\n  componentDidUpdate(prevProps: ErrorBoundaryProps): void {\n    if (!this.state.error) return;\n    const prevKeys = prevProps.resetKeys ?? [];\n    const nextKeys = this.props.resetKeys ?? [];\n    const changed = nextKeys.length !== prevKeys.length || nextKeys.some((key, i) => key !== prevKeys[i]);\n    if (changed) this.reset();\n  }\n\n  private reset = (): void => {\n    this.setState({ error: null, errorInfo: null });\n    this.props.onReset?.();\n  };\n\n  render(): React.ReactNode {\n    const { error, errorInfo } = this.state;\n    // onError/onReset/resetKeys are consumed elsewhere (getDerivedStateFromError,\n    // componentDidCatch, componentDidUpdate) and must not land on the root element via `rest`.\n    /* eslint-disable @typescript-eslint/no-unused-vars */\n    const {\n      children = null,\n      title = 'Something went wrong',\n      description,\n      labels: labelOverrides,\n      fallback,\n      hideStack = false,\n      className,\n      style,\n      classNames,\n      styles: slotStyles,\n      onError,\n      onReset,\n      resetKeys,\n      role = 'alert',\n      ...rest\n    } = this.props;\n\n    if (!error) return children;\n\n    const labels = { ...DEFAULT_LABELS, ...labelOverrides };\n\n    if (fallback) {\n      return typeof fallback === 'function' ? fallback({ error, errorInfo, resetErrorBoundary: this.reset }) : fallback;\n    }\n\n    return (\n      <div\n        {...rest}\n        role={role}\n        className={cx(styles.errorBoundary, className, classNames?.root)}\n        style={{ ...style, ...slotStyles?.root }}\n      >\n        <div className={cx(styles.errorBoundaryHeader, classNames?.header)} style={slotStyles?.header}>\n          <div className={cx(styles.errorBoundaryBadge, classNames?.badge)} style={slotStyles?.badge}>\n            <FontAwesomeIcon\n              icon={faTriangleExclamation}\n              className={cx(styles.errorBoundaryIcon, classNames?.icon)}\n              style={slotStyles?.icon as React.CSSProperties & Record<`--fa-font-${string}`, string>}\n            />\n          </div>\n          <div className={cx(styles.errorBoundaryTitle, classNames?.title)} style={slotStyles?.title}>\n            {title}\n          </div>\n          <div className={cx(styles.errorBoundaryDescription, classNames?.description)} style={slotStyles?.description}>\n            {description ?? error.message}\n          </div>\n        </div>\n\n        {!hideStack && (\n          <Accordion\n            title={labels.stackTrace}\n            className={cx(styles.errorBoundaryStack, classNames?.stack)}\n            style={slotStyles?.stack}\n          >\n            <div\n              className={cx(styles.errorBoundaryStackHeader, classNames?.stackHeader)}\n              style={slotStyles?.stackHeader}\n            >\n              <span\n                className={cx(styles.errorBoundaryStackLabel, classNames?.stackLabel)}\n                style={slotStyles?.stackLabel}\n              >\n                {error.name || 'Error'}\n              </span>\n              <CopyButton\n                value={formatStackTrace(error, errorInfo)}\n                labels={{ copy: labels.copy, copied: labels.copied }}\n                onCopy={() => showToast(labels.copySuccessToast, { color: ThemeColor.Success })}\n                onError={() => showToast(labels.copyErrorToast, { color: ThemeColor.Error })}\n                className={cx(styles.errorBoundaryCopyButton, classNames?.copyButton)}\n                style={slotStyles?.copyButton}\n              />\n            </div>\n            <pre className={cx(styles.errorBoundaryStackTrace, classNames?.stackTrace)} style={slotStyles?.stackTrace}>\n              {formatStackTrace(error, errorInfo)}\n            </pre>\n          </Accordion>\n        )}\n\n        <div className={cx(styles.errorBoundaryActions, classNames?.actions)} style={slotStyles?.actions}>\n          <Button\n            onClick={this.reset}\n            icon={faArrowRotateRight}\n            className={cx(styles.errorBoundaryResetButton, classNames?.resetButton)}\n            style={slotStyles?.resetButton}\n          >\n            {labels.reset}\n          </Button>\n        </div>\n      </div>\n    );\n  }\n}\n\nexport default ErrorBoundary;\n",".fab {\n  /* Deliberately not tied to --default-font-size: a FAB is a touch target holding an icon,\n     sized by fingers rather than by reading comfort, and it floats over the content — scaling\n     it with the text scale would only occlude more of the page. Set these to couple them if\n     that is what you want. */\n  --custom-fab-size: var(--fab-size, 56px);\n  --custom-fab-mini-size: var(--fab-mini-size, 40px);\n  --custom-fab-icon-size: var(--fab-icon-size, 18px);\n\n  width: var(--custom-fab-size);\n  height: var(--custom-fab-size);\n  border-radius: 50%;\n  display: flex;\n  align-items: stretch;\n  padding: 0;\n\n  /* Make the Ripple wrapper fill the full circular button */\n  & > div {\n    width: 100%;\n    border-radius: 50%;\n    display: flex;\n    justify-content: center;\n    align-items: center;\n  }\n\n  &-content {\n    width: var(--custom-fab-icon-size);\n    height: var(--custom-fab-icon-size);\n    display: flex;\n    align-items: center;\n    justify-content: center;\n\n    svg {\n      /* !important: the published stylesheet is wrapped in @layer, so an unlayered\n         consumer rule (e.g. FontAwesome's own .svg-inline--fa width/height) would\n         otherwise win regardless of specificity and break this icon's square sizing. */\n      width: var(--custom-fab-icon-size) !important;\n      height: var(--custom-fab-icon-size) !important;\n    }\n\n    &-container {\n      display: flex;\n      justify-content: center;\n      align-items: center;\n    }\n  }\n\n  &-size {\n    &-mini {\n      width: var(--custom-fab-mini-size);\n      height: var(--custom-fab-mini-size);\n      background: var(--background-accent-light);\n    }\n  }\n}\n","import cx from 'classnames';\n\nimport Button from 'components/common/button';\nimport { ButtonSize, ButtonStyleType } from 'components/common/button/button.types';\nimport type { FabBaseProps, FabBaseSize } from './fabBase.types';\n\nimport styles from './fabBase.module.pcss';\nimport { ThemeColor } from '../../../themeProvider/color.types';\n\nconst sizeClass: Record<FabBaseSize, string | null> = {\n  default: null,\n  mini: styles.fabSizeMini,\n};\n\nfunction FabBase({\n  style,\n  children = null,\n  size = 'default',\n  className = '',\n  styleType = ButtonStyleType.Filled,\n  color = ThemeColor.Primary,\n  onClick = () => {},\n}: FabBaseProps) {\n  return (\n    /* Icon is the only size that adds no padding. Any other one pushes past the width set\n       below through Button's `min-width: min-content`, and the circle becomes an ellipse. */\n    <Button\n      styleType={styleType}\n      color={color}\n      size={ButtonSize.Icon}\n      className={cx(styles.fab, sizeClass[size], className)}\n      style={style}\n      onClick={onClick}\n    >\n      <div className={styles.fabContentContainer}>\n        <div className={styles.fabContent}>{children}</div>\n      </div>\n    </Button>\n  );\n}\n\nexport default FabBase;\n",".fab {\n  animation: appear 0.3s forwards;\n  opacity: 0;\n  transform: scale(0);\n  box-shadow:\n    0 3px 5px -1px rgb(0 0 0 / 20%),\n    0 6px 10px 0 rgb(0 0 0 / 14%),\n    0 1px 18px 0 rgb(0 0 0 / 12%);\n  border-radius: 50%;\n\n  &-container {\n    position: fixed;\n    bottom: 16px;\n    right: 16px;\n  }\n\n  &-menu {\n    position: absolute;\n    top: 0;\n    transform: translateY(calc(-100% - 16px));\n    width: 100%;\n    display: flex;\n    flex-flow: column-reverse;\n    gap: 16px;\n    align-items: center;\n    pointer-events: none;\n    user-select: none;\n\n    &-backdrop {\n      width: 0%;\n      height: 0%;\n      position: fixed;\n      top: 0;\n      left: 0;\n      background: black;\n      opacity: 0;\n      transition: opacity 0.4s;\n\n      &-open {\n        width: 100%;\n        height: 100%;\n        opacity: 0.5;\n      }\n    }\n\n    &-entry {\n      position: relative;\n      cursor: pointer;\n      -webkit-tap-highlight-color: transparent;\n      opacity: 0;\n      transform: scale(0);\n      transition: all 0.2s;\n      pointer-events: all;\n\n      &:nth-child(2) {\n        transition-delay: 0.05s;\n      }\n\n      &:nth-child(3) {\n        transition-delay: 0.1s;\n      }\n\n      &:nth-child(4) {\n        transition-delay: 0.15s;\n      }\n\n      &:nth-child(5) {\n        transition-delay: 0.2s;\n      }\n\n      &:nth-child(6) {\n        transition-delay: 0.25s;\n      }\n\n      &:nth-child(7) {\n        transition-delay: 0.3s;\n      }\n\n      &-shown {\n        opacity: 1;\n        transform: scale(1);\n      }\n\n      &-label {\n        background: var(--background-accent-light);\n        border-radius: var(--default-border-radius);\n        padding: 4px 8px;\n\n        &-container {\n          position: absolute;\n          width: max-content;\n          transform: translateX(-100%);\n          height: 100%;\n          margin-left: -8px;\n          align-items: center;\n          display: flex;\n        }\n      }\n    }\n  }\n\n  &-icon {\n    transition: all 0.4s;\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    position: relative;\n    width: 100%;\n    height: 100%;\n\n    &-menuOpen {\n      transform: rotate(225deg);\n    }\n\n    &-content {\n      position: absolute;\n      width: var(--custom-fab-icon-size);\n      height: var(--custom-fab-icon-size);\n      display: flex;\n      align-items: center;\n      justify-content: center;\n      opacity: 0;\n      transition: opacity 0.3s;\n\n      &-shown {\n        opacity: 1;\n      }\n    }\n  }\n\n  @keyframes appear {\n    to {\n      opacity: 1;\n      transform: scale(1);\n    }\n  }\n}\n\n/* `.fab` is opacity 0 and scale 0 until `appear` runs, so removing the animation has to put it\n   at the end state by hand or the button never shows up at all. */\n@media (prefers-reduced-motion: reduce) {\n  .fab {\n    animation: none;\n    opacity: 1;\n    transform: none;\n  }\n\n  .fab-menu-entry {\n    transition: none;\n  }\n}\n","import { useState } from 'react';\nimport cx from 'classnames';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faPlus as addIcon } from '@fortawesome/free-solid-svg-icons';\nimport { ButtonStyleType } from '../button/button.types';\n\nimport FabBase from './components/fabBase';\nimport type { FabProps } from './fab.types';\n\nimport styles from './fab.module.pcss';\nimport { renderIcon } from '../utils/renderIcon';\nimport { ThemeColor } from '../themeProvider/color.types';\n\nfunction Fab({\n  children = null,\n  size = 'default',\n  menuEntries = [],\n  onMenuEntryClicked = () => {},\n  onClick = () => {},\n  closeMenuOnEntryClicked = true,\n  className,\n  style,\n  classNames,\n  styles: slotStyles,\n  ...rest\n}: FabProps) {\n  const [menuOpen, setMenuOpen] = useState(false);\n\n  function handleFabClick() {\n    if (menuEntries.length > 0) setMenuOpen((prev) => !prev);\n    onClick();\n  }\n\n  function handleEntryClick(e: React.MouseEvent, id: string) {\n    e.stopPropagation();\n    if (closeMenuOnEntryClicked) setMenuOpen(false);\n    onMenuEntryClicked(id);\n  }\n\n  return (\n    <div\n      {...rest}\n      className={cx(styles.fabContainer, className, classNames?.root)}\n      style={{ ...style, ...slotStyles?.root }}\n    >\n      <div\n        className={cx(styles.fabMenuBackdrop, menuOpen && styles.fabMenuBackdropOpen, classNames?.backdrop)}\n        style={slotStyles?.backdrop}\n        onClick={(e) => {\n          e.stopPropagation();\n          setMenuOpen(false);\n        }}\n      />\n      <div onClick={(e) => e.stopPropagation()}>\n        <FabBase\n          className={cx(styles.fab, classNames?.button)}\n          style={slotStyles?.button}\n          size={size}\n          onClick={handleFabClick}\n        >\n          <div\n            className={cx(styles.fabIcon, menuOpen && styles.fabIconMenuOpen, classNames?.icon)}\n            style={slotStyles?.icon}\n          >\n            <div className={cx(styles.fabIconContent, !menuOpen && styles.fabIconContentShown)}>{children}</div>\n            <div className={cx(styles.fabIconContent, menuOpen && styles.fabIconContentShown)}>\n              <FontAwesomeIcon icon={addIcon} />\n            </div>\n          </div>\n        </FabBase>\n      </div>\n      <div className={cx(styles.fabMenu, classNames?.menu)} style={slotStyles?.menu}>\n        {menuEntries.map(({ id, label, icon }) => (\n          <div\n            key={id}\n            className={cx(styles.fabMenuEntry, menuOpen && styles.fabMenuEntryShown, classNames?.menuEntry)}\n            style={slotStyles?.menuEntry}\n            onClick={(e) => handleEntryClick(e, id)}\n          >\n            {label && (\n              <div className={styles.fabMenuEntryLabelContainer}>\n                <div\n                  className={cx(styles.fabMenuEntryLabel, classNames?.menuEntryLabel)}\n                  style={slotStyles?.menuEntryLabel}\n                >\n                  {label}\n                </div>\n              </div>\n            )}\n            <FabBase size='mini' styleType={ButtonStyleType.Plain} color={ThemeColor.Neutral}>\n              {renderIcon(icon)}\n            </FabBase>\n          </div>\n        ))}\n      </div>\n    </div>\n  );\n}\n\nexport default Fab;\n",".flip {\n  display: inline-grid;\n\n  &-clickable {\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n  }\n\n  &-perspective {\n    display: grid;\n    min-width: 0;\n    min-height: 0;\n  }\n\n  &-surface {\n    display: grid;\n    min-width: 0;\n    min-height: 0;\n    transform-style: preserve-3d;\n    transition: transform var(--flip-duration, 0.6s) ease;\n    will-change: transform;\n\n    @media (prefers-reduced-motion: reduce) {\n      transition: none;\n    }\n  }\n\n  &-front,\n  &-back {\n    grid-area: 1 / 1;\n    min-width: 0;\n    min-height: 0;\n    backface-visibility: hidden;\n    -webkit-backface-visibility: hidden;\n    will-change: transform;\n  }\n}\n","import { useEffect, useImperativeHandle, useRef, useState, type CSSProperties, type KeyboardEvent } from 'react';\nimport cx from 'classnames';\n\nimport { useComponentDefaults } from '../configProvider';\nimport type { FlipProps } from './flip.types';\nimport styles from './flip.module.pcss';\nimport { isSelfActivation } from '../utils/activationKey';\nimport { isDev } from '../utils/isDev';\n\nfunction prefersReducedMotion(): boolean {\n  return typeof window !== 'undefined' && window.matchMedia?.('(prefers-reduced-motion: reduce)').matches === true;\n}\n\nfunction Flip(props: FlipProps) {\n  const {\n    ref,\n    front,\n    back,\n    flipped,\n    defaultFlipped = false,\n    flipOnClick = false,\n    flipAxis = 'y',\n    reverse = false,\n    flipDuration = 600,\n    perspective = 800,\n    disabled = false,\n    onFlip,\n    onFlipEnd,\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    ...rest\n  } = useComponentDefaults('Flip', props);\n\n  const isControlled = flipped !== undefined;\n  const [internalFlipped, setInternalFlipped] = useState(defaultFlipped);\n  const effectiveFlipped = isControlled ? flipped : internalFlipped;\n\n  function doFlip(nextFlipped: boolean) {\n    if (!isControlled) setInternalFlipped(nextFlipped);\n    onFlip?.(nextFlipped);\n  }\n\n  const handleClick = () => {\n    if (!flipOnClick || disabled) return;\n    doFlip(!effectiveFlipped);\n  };\n\n  const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n    if (!flipOnClick || disabled) return;\n    if (!isSelfActivation(event)) return;\n    event.preventDefault();\n    doFlip(!effectiveFlipped);\n  };\n\n  const mountedRef = useRef(false);\n  useEffect(() => {\n    if (!mountedRef.current) {\n      mountedRef.current = true;\n      return;\n    }\n    if (prefersReducedMotion()) onFlipEnd?.(effectiveFlipped);\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [effectiveFlipped]);\n\n  const handleTransitionEnd = (event: React.TransitionEvent<HTMLDivElement>) => {\n    if (event.target !== event.currentTarget || event.propertyName !== 'transform') return;\n    onFlipEnd?.(effectiveFlipped);\n  };\n\n  useImperativeHandle(ref, () => ({\n    flip() {\n      if (isControlled) {\n        if (isDev()) {\n          console.warn('Flip: flip() called on a controlled instance, update the `flipped` prop instead.');\n        }\n        return;\n      }\n      doFlip(!effectiveFlipped);\n    },\n    setFlipped(value: boolean) {\n      if (isControlled) {\n        if (isDev()) {\n          console.warn('Flip: setFlipped() called on a controlled instance, update the `flipped` prop instead.');\n        }\n        return;\n      }\n      doFlip(value);\n    },\n    reset() {\n      if (!isControlled) setInternalFlipped(false);\n    },\n  }));\n\n  // `reverse` flips the rotation sign on the same hinge axis (+180deg vs -180deg), so adjacent\n  // flips can sweep opposite ways without changing which edge they hinge on. The back face's\n  // own static rotation (below) always uses this same sign, unconditionally: two rotations of\n  // the same sign about the same axis (surface's + back's own) sum to +/-360deg, which is a full\n  // turn back to a right-reading, forward-facing back, regardless of whether `reverse` is set.\n  const turn = reverse ? -180 : 180;\n  const flipOffsetX = flipAxis === 'x' && effectiveFlipped ? turn : 0;\n  const flipOffsetY = flipAxis === 'y' && effectiveFlipped ? turn : 0;\n  const backOffsetX = flipAxis === 'x' ? turn : 0;\n  const backOffsetY = flipAxis === 'y' ? turn : 0;\n\n  return (\n    <div\n      {...rest}\n      className={cx(styles.flip, flipOnClick && styles.flipClickable, className, classNames?.root)}\n      style={{ ...style, ...slotStyles?.root }}\n      onClick={handleClick}\n      onKeyDown={flipOnClick ? handleKeyDown : undefined}\n      role={flipOnClick ? 'button' : undefined}\n      tabIndex={flipOnClick ? 0 : undefined}\n      aria-pressed={flipOnClick ? effectiveFlipped : undefined}\n    >\n      <div\n        className={cx(styles.flipPerspective, classNames?.perspective)}\n        style={{\n          perspective: perspective === 'none' ? 'none' : `${perspective}px`,\n          ...slotStyles?.perspective,\n        }}\n      >\n        <div\n          className={cx(styles.flipSurface, classNames?.surface)}\n          style={\n            {\n              transform: `rotateX(${flipOffsetX}deg) rotateY(${flipOffsetY}deg)`,\n              '--flip-duration': `${flipDuration}ms`,\n              ...slotStyles?.surface,\n            } as CSSProperties & Record<'--flip-duration', string>\n          }\n          data-flipped={effectiveFlipped}\n          onTransitionEnd={handleTransitionEnd}\n        >\n          <div className={cx(styles.flipFront, classNames?.front)} style={slotStyles?.front}>\n            {front}\n          </div>\n          <div\n            className={cx(styles.flipBack, classNames?.back)}\n            style={{\n              transform: `rotateX(${backOffsetX}deg) rotateY(${backOffsetY}deg)`,\n              ...slotStyles?.back,\n            }}\n          >\n            {back}\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n}\n\nexport default Flip;\n",".iconPicker {\n  border-width: 1px;\n  border-radius: var(--default-border-radius);\n  border-color: transparent;\n  border-style: solid;\n\n  &-label {\n    font-size: var(--text-small-font-size);\n    margin-bottom: 0.5em;\n  }\n\n  &-item {\n    display: flex;\n    justify-content: center;\n    align-items: center;\n\n    /* em, because the icon inside is 1em: a fixed box let the glyph outgrow its own circle. */\n    padding: 0.5em;\n    width: 1.5em;\n    height: 1.5em;\n    background: var(--background-accent);\n    border-radius: 200px;\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n    stroke: var(--text-color);\n    position: relative;\n\n    &:focus-visible {\n      outline: 1px solid var(--focus-border-color, var(--primary-color));\n    }\n\n    &-container {\n      display: flex;\n      flex-wrap: wrap;\n      justify-content: space-around;\n      gap: 0.5em 0.875em;\n    }\n\n    &-selected {\n      background: var(--primary-color);\n\n      /* The inherited --text-color is the page's, not this background's, so the same selected\n         state came out white on one theme and near-black on another. */\n      stroke: var(--text-on-primary);\n\n      /* The selected item's own background is --primary-color, which is also the\n         default --focus-border-color — use the on-primary color instead so the ring\n         stays visible on whichever item roving tabindex focuses by default. */\n      &:focus-visible {\n        outline-color: var(--text-on-primary);\n      }\n    }\n  }\n\n  &-error {\n    border-color: var(--error-color);\n  }\n}\n","import React, { useRef } from 'react';\nimport cx from 'classnames';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport styles from './iconPicker.module.pcss';\nimport Ripple from '../ripple';\nimport type { RippleHandle } from '../ripple';\nimport type { IconPickerItem, IconPickerProps } from './iconPicker.types';\n\nfunction hasError(props: IconPickerProps): boolean {\n  const { formValidator } = props;\n  return formValidator ? formValidator.shouldShowError : false;\n}\n\nfunction isRequired(props: IconPickerProps): boolean {\n  const { isRequired: reqProp, formValidator } = props;\n  return !!(\n    reqProp || formValidator?.validators.find((v) => (v as { validatorId?: string }).validatorId === 'required')\n  );\n}\n\nexport default function IconPicker({\n  data = [],\n  selectedId = null,\n  label = '',\n  onChange,\n  formValidator = null,\n  isRequired: _isRequired,\n  className,\n  style,\n  classNames,\n  styles: slotStyles,\n  ...rest\n}: IconPickerProps) {\n  const currentValue = formValidator ? ((formValidator.value as string) ?? null) : selectedId;\n  const showError = hasError({ formValidator, isRequired: _isRequired });\n  const required = isRequired({ formValidator, isRequired: _isRequired });\n\n  const itemRefs = useRef<(HTMLDivElement | null)[]>([]);\n  const rippleRefs = useRef<(RippleHandle | null)[]>([]);\n  const selectedIndex = data.findIndex((item) => item.id === currentValue);\n  const rovingIndex = selectedIndex >= 0 ? selectedIndex : 0;\n\n  function handleSelect(id: string) {\n    if (formValidator) formValidator.set(id);\n    onChange?.(id);\n  }\n\n  function moveTo(step: 1 | -1, fromIndex: number) {\n    if (data.length === 0) return;\n    const next = (fromIndex + step + data.length) % data.length;\n    itemRefs.current[next]?.focus();\n    handleSelect(data[next].id);\n  }\n\n  function handleKeyDown(event: React.KeyboardEvent, index: number, id: string) {\n    switch (event.key) {\n      case 'ArrowDown':\n      case 'ArrowRight':\n        event.preventDefault();\n        moveTo(1, index);\n        break;\n      case 'ArrowUp':\n      case 'ArrowLeft':\n        event.preventDefault();\n        moveTo(-1, index);\n        break;\n      case ' ':\n      case 'Enter':\n        event.preventDefault();\n        // No DOM click is dispatched here, so Ripple's own listener never sees the activation.\n        rippleRefs.current[index]?.addRipple();\n        handleSelect(id);\n        break;\n      default:\n        break;\n    }\n  }\n\n  return (\n    <div\n      {...rest}\n      className={cx(styles.iconPicker, { [styles.iconPickerError]: showError }, classNames?.root, className)}\n      style={{ ...slotStyles?.root, ...style }}\n    >\n      {label && (\n        <div className={cx(styles.iconPickerLabel, classNames?.label)} style={slotStyles?.label}>\n          {label}\n          {required && ' *'}\n        </div>\n      )}\n      <div\n        role='radiogroup'\n        aria-label={label || undefined}\n        className={cx(styles.iconPickerItemContainer, classNames?.itemContainer)}\n        style={slotStyles?.itemContainer}\n      >\n        {data.map(({ id, label: itemLabel, icon, customIcon }: IconPickerItem, index) => {\n          const checked = currentValue === id;\n          return (\n            <div\n              key={id}\n              ref={(el) => {\n                itemRefs.current[index] = el;\n              }}\n              className={cx(styles.iconPickerItem, { [styles.iconPickerItemSelected]: checked }, classNames?.item)}\n              style={slotStyles?.item}\n              title={itemLabel}\n              tabIndex={index === rovingIndex ? 0 : -1}\n              role='radio'\n              aria-checked={checked}\n              onClick={() => handleSelect(id)}\n              onKeyDown={(event) => handleKeyDown(event, index, id)}\n            >\n              {icon && <FontAwesomeIcon icon={icon} />}\n              {customIcon ?? null}\n              <Ripple\n                ref={(el) => {\n                  rippleRefs.current[index] = el;\n                }}\n              />\n            </div>\n          );\n        })}\n      </div>\n    </div>\n  );\n}\n","import { useState, useEffect, useRef, useCallback } from 'react';\nimport type { IdleManagerProps } from './idleManager.types';\n\nconst ACTIVITY_EVENTS = ['click', 'mousemove', 'keydown', 'scroll', 'touchstart'] as const;\n\nfunction IdleManager({ children = null, idleAfterSeconds = 600, renderCondition = true }: IdleManagerProps) {\n  const [isIdleContentShown, setIsIdleContentShown] = useState(false);\n  const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n  const scheduleIdleTimeout = useCallback(() => {\n    if (timeoutRef.current !== null) clearTimeout(timeoutRef.current);\n    timeoutRef.current = setTimeout(() => {\n      setIsIdleContentShown(true);\n    }, idleAfterSeconds * 1000);\n  }, [idleAfterSeconds]);\n\n  // Only activity hides idle content again. Kept out of the effect below so that mounting, or a\n  // change of `idleAfterSeconds`, restarts the countdown without dismissing what is on screen.\n  const resetIdleTimeout = useCallback(() => {\n    setIsIdleContentShown(false);\n    scheduleIdleTimeout();\n  }, [scheduleIdleTimeout]);\n\n  useEffect(() => {\n    scheduleIdleTimeout();\n  }, [scheduleIdleTimeout]);\n\n  // Register / unregister activity listeners\n  useEffect(() => {\n    for (const event of ACTIVITY_EVENTS) {\n      document.addEventListener(event, resetIdleTimeout);\n    }\n    return () => {\n      for (const event of ACTIVITY_EVENTS) {\n        document.removeEventListener(event, resetIdleTimeout);\n      }\n      // Clear any pending timeout on unmount so it never fires after we're gone\n      if (timeoutRef.current !== null) clearTimeout(timeoutRef.current);\n    };\n  }, [resetIdleTimeout]);\n\n  return <>{renderCondition && isIdleContentShown && children}</>;\n}\n\nexport default IdleManager;\n",".inputDropdown {\n  position: relative;\n}\n\n.inputDropdownStatus {\n  position: absolute;\n  width: 1px;\n  height: 1px;\n  padding: 0;\n  border: 0;\n  clip-path: inset(50%);\n  overflow: hidden;\n  white-space: nowrap;\n}\n\n/* The list brings its own padding and its rows reach the panel edge. */\n.inputDropdownPanel {\n  padding: 0;\n}\n\n/* Chrome, placement and the portal come from the Popover this renders inside; the\n   width is this component's own, since the list sizes to the longest suggestion\n   rather than to the field. */\n.dropdown {\n  width: fit-content;\n  max-width: 15em;\n  box-sizing: border-box;\n  overflow: hidden;\n  border-radius: inherit;\n\n  &-scroll {\n    overflow: auto;\n    max-height: 12.5em;\n  }\n\n  &-empty {\n    padding: 0.5em;\n    color: var(--text-dark);\n  }\n\n  &-item {\n    padding: 0.5em;\n    z-index: 0;\n    outline: none;\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n\n    &:hover,\n    &:focus {\n      background: var(--background-accent);\n    }\n\n    &-highlighted {\n      background: var(--background-accent);\n    }\n  }\n}\n","import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport cx from 'classnames';\nimport styles from './inputDropdown.module.pcss';\nimport Input from '../input';\nimport Popover from '../popover';\nimport { PopoverAlign, PopoverPlacement } from '../popover/popover.types';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport { Presentation } from '../configProvider/presentation.types';\nimport type { InputDropdownItem, InputDropdownLabels, InputDropdownProps } from './inputDropdown.types';\nimport { nextIndex, scrollOptionIntoView } from '../utils/listNavigation';\n\nfunction getKeyFromProps(value: string | undefined, formValidator: InputDropdownProps['formValidator']): string {\n  return formValidator ? ((formValidator.value as string) ?? '') : (value ?? '');\n}\n\nfunction labelForKey(key: string, items: InputDropdownItem[]): string {\n  const match = items.find((item) => item.value === key);\n  return match ? (match.label ?? match.value) : key;\n}\n\n/**\n * The suggestions do not wrap: the list is filtered by what is being typed, so running off one\n * end and reappearing at the other reads as the list having changed under the caret.\n */\nconst NO_WRAP = { wrap: false };\n\nconst DEFAULT_LABELS: Required<InputDropdownLabels> = { empty: 'No matches' };\n\nexport default function InputDropdown(props: InputDropdownProps) {\n  const {\n    label = '',\n    value = '',\n    formValidator = null,\n    error,\n    isRequired = false,\n    items = [],\n    allowCustomValue = false,\n    maxItems = 100,\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    onSelect = () => null,\n    onChange,\n    placeholder = '',\n    readOnly = false,\n    labels,\n    ...rest\n  } = useComponentDefaults('InputDropdown', props);\n  const text = useLabels('InputDropdown', labels, DEFAULT_LABELS);\n  const currentKey = getKeyFromProps(value, formValidator);\n  const [, forceUpdate] = useState(0);\n  const fvRef = useRef(formValidator);\n\n  useEffect(() => {\n    fvRef.current = formValidator;\n    if (!formValidator) return;\n    const listener = () => forceUpdate((n) => n + 1);\n    formValidator.registerOnUpdateListener(listener);\n    return () => {\n      formValidator.removeOnUpdateListener(listener);\n    };\n  }, [formValidator]);\n\n  const computedIsRequired =\n    isRequired || !!formValidator?.validators.find((v) => (v as { validatorId?: string }).validatorId === 'required');\n\n  const [inputText, setInputText] = useState(() => labelForKey(currentKey, items));\n  const [prevKey, setPrevKey] = useState(currentKey);\n  const [isMenuShown, setIsMenuShown] = useState(false);\n  const [highlightedIndex, setHighlightedIndex] = useState<number | null>(null);\n  const hideTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n  const triggerRef = useRef<HTMLDivElement>(null);\n  const scrollRef = useRef<HTMLDivElement>(null);\n\n  // Sync display text when the controlled key changes externally (e.g. parent reset).\n  if (currentKey !== prevKey) {\n    setPrevKey(currentKey);\n    const match = items.find((item) => item.value === currentKey);\n    if (match) setInputText(match.label ?? match.value);\n    else if (allowCustomValue) setInputText(currentKey);\n  }\n\n  // `items` often arrives after the value, leaving the raw key on display. Not while open, or a key\n  // being typed would turn into its label under the caret.\n  if (!isMenuShown && !allowCustomValue && inputText === currentKey) {\n    const selectedLabel = labelForKey(currentKey, items);\n    if (selectedLabel !== inputText) setInputText(selectedLabel);\n  }\n\n  const currItems = useMemo(() => {\n    const needle = inputText.toLowerCase();\n    const matches: InputDropdownItem[] = [];\n    for (const item of items) {\n      if (!(item.label ?? item.value).toLowerCase().includes(needle)) continue;\n      matches.push(item);\n      if (maxItems > 0 && matches.length >= maxItems) break;\n    }\n    return matches;\n  }, [items, inputText, maxItems]);\n\n  // Only text that blur swaps for the selection's label needs a warning. Comparing with the label\n  // rather than '' keeps a preset value missing from `items` from showing \"No matches\" unprompted.\n  const isDiscardedOnBlur = !allowCustomValue && inputText !== '' && inputText !== labelForKey(currentKey, items);\n  const showEmpty = currItems.length === 0 && isDiscardedOnBlur;\n  // Closed rather than hidden when empty: a mounted panel stays on the overlay stack, so inside a\n  // Modal an invisible list would take the Escape meant for the modal.\n  const isOpen = isMenuShown && !readOnly && (currItems.length > 0 || showEmpty);\n\n  // A ref callback rather than a layout effect: the list is portaled out of the field's font size,\n  // and Popover positions it in its own layout effect, which runs before this component's would.\n  const menuFontSizeOverride = slotStyles?.dropdown?.fontSize;\n  const menuRef = useCallback(\n    (menu: HTMLDivElement | null) => {\n      const trigger = triggerRef.current;\n      if (!menu || !trigger || menuFontSizeOverride != null) return;\n      menu.style.fontSize = window.getComputedStyle(trigger).fontSize;\n    },\n    [menuFontSizeOverride],\n  );\n\n  const handleFocus = () => {\n    if (readOnly) return;\n    if (hideTimeoutRef.current) {\n      clearTimeout(hideTimeoutRef.current);\n      hideTimeoutRef.current = null;\n    }\n    setIsMenuShown(true);\n  };\n\n  const handleBlur = () => {\n    if (fvRef.current) fvRef.current.touched = true;\n    hideTimeoutRef.current = setTimeout(() => {\n      setIsMenuShown(false);\n      setHighlightedIndex(null);\n      hideTimeoutRef.current = null;\n      // Without custom values, uncommitted typed text is invalid — snap the\n      // display back to the currently selected item's label.\n      if (!allowCustomValue) setInputText(labelForKey(currentKey, items));\n    }, 100);\n  };\n\n  useEffect(() => {\n    if (!isOpen || highlightedIndex === null) return;\n    scrollOptionIntoView(scrollRef.current, scrollRef.current?.children[highlightedIndex]);\n  }, [isOpen, highlightedIndex]);\n\n  const handleSelect = useCallback(\n    (item: InputDropdownItem) => {\n      setInputText(item.label ?? item.value);\n      setIsMenuShown(false);\n      setHighlightedIndex(null);\n      if (formValidator) formValidator.set(item.value);\n      if (onChange) onChange(item.value);\n      onSelect(item.value);\n    },\n    [formValidator, onChange, onSelect],\n  );\n\n  const onKeyDown = useCallback(\n    (event: React.KeyboardEvent) => {\n      if (readOnly) return;\n      const isArrow = event.key === 'ArrowUp' || event.key === 'ArrowDown';\n      // Enter belongs to the form while closed, or it would pick a suggestion the user never saw.\n      if (!isOpen) {\n        if (isArrow) {\n          event.preventDefault();\n          setIsMenuShown(true);\n        }\n        return;\n      }\n      if (isArrow) {\n        event.preventDefault();\n        const step = event.key === 'ArrowDown' ? 1 : -1;\n        setHighlightedIndex((idx) => {\n          // The text being typed is part of the cycle, so both ends lead back to it and on round\n          // to the far end: last → text → first, first → text → last. That is how a browser's own\n          // address bar behaves, and it is why this list does not wrap straight around the way\n          // Dropdown and MultiSelect do — they have no typed text to pass through.\n          const atFarEnd = idx !== null && idx === (step === 1 ? currItems.length - 1 : 0);\n          return atFarEnd ? null : nextIndex(idx, step, currItems.length, NO_WRAP);\n        });\n      } else if (event.key === 'Enter') {\n        if (highlightedIndex !== null && currItems[highlightedIndex]) {\n          // This Enter picked a suggestion, so it must not also reach the form around the field.\n          // Without this the browser's implicit submission runs as well, and choosing from the\n          // list submitted the form underneath the user. The blank state deliberately does not\n          // do this: Enter there means the text as typed, and submitting is a fair reading of it.\n          event.preventDefault();\n          handleSelect(currItems[highlightedIndex]);\n        } else if (allowCustomValue) {\n          setIsMenuShown(false);\n          setHighlightedIndex(null);\n        }\n      }\n    },\n    [currItems, highlightedIndex, handleSelect, allowCustomValue, readOnly, isOpen],\n  );\n\n  const menu = (\n    <div ref={menuRef} className={cx(styles.dropdown, classNames?.dropdown)} style={slotStyles?.dropdown}>\n      {showEmpty && (\n        <div\n          className={cx(styles.dropdownEmpty, classNames?.empty)}\n          style={slotStyles?.empty}\n          // A blur would throw the typed text away.\n          onMouseDown={(e) => e.preventDefault()}\n        >\n          {text.empty}\n        </div>\n      )}\n      <div ref={scrollRef} className={styles.dropdownScroll}>\n        {currItems.map((item, key) => (\n          <div\n            key={`${key}_${item.label}_${item.value}`}\n            className={cx(\n              styles.dropdownItem,\n              highlightedIndex === key && styles.dropdownItemHighlighted,\n              classNames?.item,\n            )}\n            style={slotStyles?.item}\n            // Prevent the input from blurring before the click resolves.\n            onMouseDown={(e) => e.preventDefault()}\n            onClick={() => handleSelect(item)}\n          >\n            {item.label ?? item.value}\n          </div>\n        ))}\n      </div>\n    </div>\n  );\n\n  return (\n    <div\n      {...rest}\n      ref={triggerRef}\n      className={cx(styles.inputDropdown, classNames?.root, className)}\n      style={{ ...slotStyles?.root, ...style }}\n      onFocus={handleFocus}\n      onBlur={handleBlur}\n      onKeyDown={onKeyDown}\n    >\n      <Popover\n        // The field owns the open state: it opens on typing and closes on blur, on\n        // a pick, or on Escape. Only a close is taken from Popover, which is the\n        // outside click its portaled panel would otherwise not see.\n        isOpen={isOpen}\n        onOpenChange={(next) => {\n          if (next) return;\n          setIsMenuShown(false);\n          setHighlightedIndex(null);\n        }}\n        // Never a sheet, whatever the app-wide `presentation`: its backdrop would cover the field\n        // being typed into.\n        presentation={Presentation.Default}\n        placement={PopoverPlacement.Bottom}\n        align={PopoverAlign.Start}\n        offset={0}\n        withArrow={false}\n        constrainWidth={false}\n        closeOnTriggerClick={false}\n        focusOnOpen={false}\n        classNames={{ panel: styles.inputDropdownPanel }}\n        content={menu}\n      >\n        <Input\n          // A combobox renders its own suggestion list; the browser's autofill dropdown\n          // opens on top of it, so two lists compete for the same space.\n          autoComplete='off'\n          label={label}\n          value={inputText}\n          readOnly={readOnly}\n          formValidator={null}\n          isRequired={computedIsRequired}\n          isValid={formValidator ? !formValidator.shouldShowError : !error}\n          errorMessage={\n            formValidator\n              ? formValidator.shouldShowError\n                ? (formValidator.getCurrentErrorMessage() ?? '')\n                : ''\n              : (error ?? '')\n          }\n          onChange={(typed) => {\n            setInputText(typed);\n            setHighlightedIndex(null);\n            setIsMenuShown(true);\n            if (allowCustomValue) {\n              if (formValidator) formValidator.set(typed);\n              if (onChange) onChange(typed);\n            } else if (typed === '') {\n              if (formValidator) formValidator.set('');\n              if (onChange) onChange('');\n              onSelect('');\n            } else {\n              // Emit when the typed text exactly matches an item's label/value.\n              const match = items.find((item) => (item.label ?? item.value).toLowerCase() === typed.toLowerCase());\n              if (match) {\n                if (formValidator) formValidator.set(match.value);\n                if (onChange) onChange(match.value);\n                onSelect(match.value);\n              }\n            }\n          }}\n          placeholder={placeholder}\n        />\n      </Popover>\n      {/* Outside the portal and always mounted: a live region that mounts already filled is not\n          reliably announced. */}\n      <span className={styles.inputDropdownStatus} role='status' aria-live='polite'>\n        {isOpen && showEmpty ? text.empty : ''}\n      </span>\n    </div>\n  );\n}\n","/* Columns stretch to the tallest one, so the space below a column's cards is still its drop zone.\n   Sized to its content, an empty column was a short box, and a card dropped below it landed in\n   the neighbouring column. */\n.board {\n  display: flex;\n  gap: var(--spacing-s);\n  overflow-x: auto;\n  width: 100%;\n}\n\n.column {\n  background: var(--background-accent-light);\n  border-radius: var(--default-border-radius);\n  box-shadow: var(--card-shadow);\n  flex: 1 1 0;\n  display: flex;\n  flex-direction: column;\n  border: 2px solid transparent;\n  transition:\n    border-color 0.15s ease,\n    background 0.15s ease;\n\n  &-collapsed {\n    flex: 0 1 0;\n    min-width: var(--kanban-empty-column-width, 140px);\n  }\n\n  /* Too narrow for the title beside the count and actions, which cut it to \"I…\": they wrap below. */\n  &-collapsed &-header {\n    flex-wrap: wrap;\n  }\n\n  &-collapsed &-header-title {\n    white-space: normal;\n  }\n\n  &-over {\n    border-color: var(--primary-lighter);\n    background: var(--background-accent);\n  }\n\n  &-over-limit {\n    border-color: var(--warn-color);\n  }\n\n  /* A whole column being dragged over this one, reordering — distinct from -over (a card being\n     dropped into this column's card list), even though both are dashed-free solid borders here;\n     kept as a separate class so consumers can restyle one without affecting the other. */\n  &-drop-target {\n    border-color: var(--primary-color);\n    background: var(--background-accent);\n  }\n\n  /* Dims only the cards area, not the header — header actions (e.g. an \"add card\" button) are\n     consumer-controlled and often meant to stay fully interactive even while the column itself\n     is frozen for dragging, and opacity on an ancestor can't be undone by a child's own opacity. */\n  &-disabled &-items {\n    opacity: 0.6;\n  }\n\n  &-dragging {\n    opacity: 0.4;\n  }\n\n  &-drag-overlay {\n    cursor: grabbing;\n    box-shadow:\n      0 10px 30px rgb(0 0 0 / 25%),\n      var(--card-shadow);\n    min-width: 220px;\n  }\n\n  &-header {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    gap: var(--spacing-s);\n    padding: var(--spacing-m) var(--spacing-l);\n    background: var(--background-accent);\n    border-radius: var(--default-border-radius) var(--default-border-radius) 0 0;\n    font-size: var(--header-small-font-size);\n    font-weight: var(--header-font-weight);\n    color: var(--text-color);\n    border-bottom: 1px solid var(--border-color);\n\n    &-reorderable {\n      cursor: grab;\n      -webkit-tap-highlight-color: transparent;\n      touch-action: none;\n      user-select: none;\n\n      &:active {\n        cursor: grabbing;\n      }\n    }\n\n    &-title {\n      overflow: hidden;\n      text-overflow: ellipsis;\n      white-space: nowrap;\n\n      &:focus-visible {\n        outline: 2px solid var(--focus-border-color, var(--primary-color));\n        outline-offset: 2px;\n        border-radius: var(--default-border-radius);\n      }\n    }\n\n    &-count {\n      flex-shrink: 0;\n      font-size: var(--default-font-size);\n      font-weight: var(--default-font-weight);\n      color: var(--text-dark);\n\n      &-over {\n        color: var(--warn-color);\n        font-weight: var(--header-font-weight);\n      }\n    }\n\n    &-trailing {\n      display: flex;\n      align-items: center;\n      gap: var(--spacing-s);\n      flex-shrink: 0;\n    }\n\n    &-actions {\n      display: flex;\n      align-items: center;\n      gap: var(--spacing-s);\n    }\n  }\n\n  /* Scrolls once something bounds it: `columnMaxHeight`, or a height given to the board. A board\n     that grows with its cards never makes it overflow. `position` makes its cards' `offsetTop`\n     relative to it, which the load-more probe measures. */\n  &-items {\n    position: relative;\n    padding: var(--spacing-m);\n    display: flex;\n    flex-direction: column;\n    gap: var(--spacing-m);\n    min-height: 80px;\n    flex: 1;\n    overflow-y: auto;\n  }\n\n  &-placeholder {\n    flex: 1;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    text-align: center;\n    color: var(--text-dark);\n    font-size: var(--default-font-size);\n  }\n\n  &-loading-indicator {\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    padding: var(--spacing-s) 0;\n  }\n}\n\n/* A press that can start a text selection lets Chromium scroll the page while the button is held\n   near its edge, however far past the board. Where a press starts a drag, it cannot select. */\n.item {\n  cursor: grab;\n  touch-action: none;\n  user-select: none;\n\n  &:focus-visible {\n    outline: 2px solid var(--focus-border-color, var(--primary-color));\n    outline-offset: 2px;\n    border-radius: var(--default-border-radius);\n  }\n\n  &:active {\n    cursor: grabbing;\n  }\n\n  &-dragging {\n    opacity: 0.4;\n  }\n\n  &-handled {\n    cursor: default;\n    touch-action: auto;\n    user-select: auto;\n\n    &:active {\n      cursor: default;\n    }\n\n    [data-drag-handle] {\n      cursor: grab;\n      -webkit-tap-highlight-color: transparent;\n      touch-action: none;\n      user-select: none;\n\n      &:focus-visible {\n        outline: 2px solid var(--focus-border-color, var(--primary-color));\n        outline-offset: 2px;\n        border-radius: var(--default-border-radius);\n      }\n    }\n\n    [data-drag-handle]:active {\n      cursor: grabbing;\n    }\n  }\n\n  &-disabled {\n    cursor: not-allowed;\n\n    &:active {\n      cursor: not-allowed;\n    }\n\n    [data-drag-handle] {\n      cursor: not-allowed;\n    }\n\n    [data-drag-handle]:active {\n      cursor: not-allowed;\n    }\n  }\n}\n\n.dragOverlay {\n  border-radius: var(--default-border-radius);\n  cursor: grabbing;\n  box-shadow:\n    0 10px 30px rgb(0 0 0 / 25%),\n    var(--card-shadow);\n}\n","import type { SyntheticEvent } from 'react';\nimport { useCallback, useLayoutEffect, useRef } from 'react';\nimport { useSortable } from '@dnd-kit/sortable';\nimport { CSS } from '@dnd-kit/utilities';\nimport cx from 'classnames';\n\nimport type { KanbanItemProps } from './kanbanBoard.types';\nimport styles from './kanbanBoard.module.pcss';\n\n// Hoisted so these are referentially stable across renders — an inline object literal here would\n// give `useSortable` a new `disabled` value every render (every card re-renders on every\n// `dragOver` while a drag is in progress, via the board's `placement`), which can spin dnd-kit's own\n// registration effect into a render loop that eventually trips React's \"Maximum update depth\n// exceeded\" guard. Same fix as the sensor options in `KanbanBoard.tsx` and `data`/`disabled` in\n// `KanbanColumn.tsx`. Only two shapes are ever needed, so both are hoisted rather than memoized.\nconst ITEM_DISABLED_DRAG_AND_DROP = { draggable: true, droppable: true };\nconst ITEM_DISABLED_DRAG_ONLY = { draggable: true, droppable: false };\n\nfunction KanbanItem({ id, label, dragHandle, disabled, preventAsDropTarget, children }: KanbanItemProps) {\n  const { attributes, listeners, setNodeRef, setActivatorNodeRef, transform, transition, isDragging } = useSortable({\n    id,\n    // The boolean shorthand only disables dragging *out* of the card — dnd-kit hard-codes\n    // `droppable: false` for it either way (\"backwards compatibility\"). That's actually what we\n    // want for a card pinned individually (`getItemDisabled`): it can't be dragged, but other\n    // cards should still be able to land before/after it normally. Only a frozen *column*\n    // (`preventAsDropTarget`) also disables droppable, so nothing can be dropped into it at all.\n    disabled: disabled ? (preventAsDropTarget ? ITEM_DISABLED_DRAG_AND_DROP : ITEM_DISABLED_DRAG_ONLY) : undefined,\n  });\n\n  // Without an activator, dnd-kit starts a drag for a Space or Enter bubbling up from anything\n  // inside the card, and cancels it: a button in the card could not be pressed, nor a space typed.\n  const nodeRef = useRef<HTMLDivElement | null>(null);\n  const setRefs = useCallback(\n    (node: HTMLDivElement | null) => {\n      nodeRef.current = node;\n      setNodeRef(node);\n      if (!dragHandle) setActivatorNodeRef(node);\n    },\n    [setNodeRef, setActivatorNodeRef, dragHandle],\n  );\n\n  // With `dragHandle`, the handle is the keyboard handle too, and the card a plain container: as a\n  // button, the card wrapped its own controls, which a screen reader then reads as text. The\n  // handle is the consumer's element, so it gets the drag semantics here rather than as props.\n  // No dependencies: the consumer can re-render the handle at any time.\n  useLayoutEffect(() => {\n    if (!dragHandle) return;\n    const handle = nodeRef.current?.querySelector<HTMLElement>('[data-drag-handle]') ?? null;\n    setActivatorNodeRef(handle);\n    if (!handle) return;\n    if (handle.tabIndex < 0) handle.tabIndex = 0;\n    if (handle.tagName !== 'BUTTON' && !handle.hasAttribute('role')) handle.setAttribute('role', 'button');\n    const namedByUs = handle.hasAttribute('data-kanban-label');\n    if (label && (namedByUs || !(handle.hasAttribute('aria-label') || handle.hasAttribute('aria-labelledby')))) {\n      handle.setAttribute('aria-label', label);\n      handle.setAttribute('data-kanban-label', '');\n    }\n    handle.setAttribute('data-kanban-card', '');\n    for (const [name, value] of Object.entries(attributes)) {\n      if (name === 'role' || name === 'tabIndex') continue;\n      if (value === undefined) handle.removeAttribute(name);\n      else handle.setAttribute(name, String(value));\n    }\n  });\n\n  // dnd-kit's listeners are a new object every render, so wrapping them per render costs nothing.\n  const handleListeners =\n    dragHandle && listeners\n      ? Object.fromEntries(\n          Object.entries(listeners).map(([name, listener]) => [\n            name,\n            (event: SyntheticEvent) => {\n              if ((event.target as HTMLElement).closest('[data-drag-handle]')) listener(event);\n            },\n          ]),\n        )\n      : listeners;\n\n  return (\n    <div\n      ref={setRefs}\n      data-kanban-card={dragHandle ? undefined : ''}\n      style={{ transform: CSS.Transform.toString(transform), transition }}\n      className={cx(\n        styles.item,\n        dragHandle && styles.itemHandled,\n        isDragging && styles.itemDragging,\n        disabled && styles.itemDisabled,\n      )}\n      {...(dragHandle ? undefined : attributes)}\n      {...handleListeners}\n    >\n      {children}\n    </div>\n  );\n}\n\nexport default KanbanItem;\n","import type { HTMLAttributes, ReactNode, Ref, UIEvent } from 'react';\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { useDroppable } from '@dnd-kit/core';\nimport { SortableContext, useSortable, verticalListSortingStrategy } from '@dnd-kit/sortable';\nimport { CSS } from '@dnd-kit/utilities';\nimport cx from 'classnames';\n\nimport type { DataProps, SlotClassNames, SlotStyles } from '../types/slots.types';\nimport type { KanbanColumnProps, KanbanColumnSlots } from './kanbanBoard.types';\nimport styles from './kanbanBoard.module.pcss';\nimport KanbanItem from './KanbanItem';\nimport Loader from '../loader';\n\n// Hoisted so these are referentially stable across renders — inline literals here would give\n// `useSortable` a new `data`/`disabled` object every render (every column re-renders on every\n// `dragOver` while a drag is in progress, via the board's `placement`), which can spin dnd-kit's own\n// registration effect into a render loop that eventually trips React's \"Maximum update depth\n// exceeded\" guard. Same fix as the sensor options in `KanbanBoard.tsx`.\nconst COLUMN_SORTABLE_DATA = { type: 'column' };\nconst COLUMN_SORTABLE_DISABLED = { draggable: true, droppable: true };\n\ninterface KanbanColumnInternalProps<T> extends KanbanColumnProps {\n  itemIds: string[];\n  items: Record<string, T>;\n  renderItem: (item: T, id: string) => ReactNode;\n  dragHandle?: boolean;\n  /** Shrink to a narrow strip. The board decides when, see `KanbanBoardProps.collapseEmptyColumns`. */\n  collapsed?: boolean;\n  /** Work-in-progress limit — see `KanbanColumnDef.maxItems`. Display-only. */\n  maxItems?: number;\n  /** Freezes the column — see `KanbanColumnDef.disabled`. */\n  disabled?: boolean;\n  /** Returns whether a specific card can't be picked up — see `KanbanBoardProps.getItemDisabled`. */\n  getItemDisabled?: (item: T, id: string) => boolean;\n  /** Names a card's drag handle — see `KanbanBoardProps.getItemLabel`. */\n  getItemLabel?: (item: T, id: string) => string;\n  /** Custom content shown when the column has no cards — see `KanbanBoardProps.renderEmptyColumn`. */\n  emptyPlaceholder?: ReactNode;\n  /** Custom header actions — see `KanbanBoardProps.renderColumnActions`. */\n  actions?: ReactNode;\n  /** Allow the whole column to be dragged (by its header) to reorder it — see `KanbanBoardProps.reorderableColumns`. */\n  reorderable?: boolean;\n  /**\n   * Whether the card currently being dragged (if any) is actually a column. Suppresses the\n   * cards drop-target highlight, which otherwise lights up on every column a dragged column\n   * passes over — that highlight means \"drop a card here\", which doesn't apply mid-column-drag.\n   */\n  activeIsColumn?: boolean;\n  /** Bounds the column's height and makes its card list scroll — see `KanbanBoardProps.columnMaxHeight`. */\n  columnMaxHeight?: string;\n  /** Whether more items exist for this column — see `KanbanColumnDef.hasMore`. */\n  hasMore?: boolean;\n  /** Requests the next page for this column — see `KanbanBoardProps.onLoadMore`. */\n  onLoadMore?: () => void | Promise<void>;\n  /** Scroll distance from the bottom that triggers `onLoadMore` — see `KanbanBoardProps.loadMoreThreshold`. */\n  loadMoreThreshold?: number;\n}\n\ninterface ColumnHeaderContentProps {\n  title: string;\n  count: number;\n  maxItems?: number;\n  actions?: ReactNode;\n  /** Makes the title the column's keyboard drag handle. */\n  titleProps?: HTMLAttributes<HTMLSpanElement> & DataProps & { ref?: Ref<HTMLSpanElement> };\n  classNames?: SlotClassNames<KanbanColumnSlots>;\n  styles?: SlotStyles<KanbanColumnSlots>;\n}\n\n// Also rendered by the drag overlay: with the title alone, the overlay was shorter than the header\n// it covers, and the faded original showed below it.\nexport function ColumnHeaderContent({\n  title,\n  count,\n  maxItems,\n  actions,\n  titleProps,\n  classNames,\n  styles: slotStyles,\n}: ColumnHeaderContentProps) {\n  const isOverLimit = maxItems != null && count > maxItems;\n  return (\n    <>\n      <span {...titleProps} className={styles.columnHeaderTitle}>\n        {title}\n      </span>\n      <div className={styles.columnHeaderTrailing}>\n        {maxItems != null && (\n          <span\n            className={cx(styles.columnHeaderCount, isOverLimit && styles.columnHeaderCountOver, classNames?.count)}\n            style={slotStyles?.count}\n          >\n            {count}/{maxItems}\n          </span>\n        )}\n        {actions != null && (\n          <div className={cx(styles.columnHeaderActions, classNames?.actions)} style={slotStyles?.actions}>\n            {actions}\n          </div>\n        )}\n      </div>\n    </>\n  );\n}\n\nfunction KanbanColumn<T>({\n  id,\n  title,\n  itemIds,\n  items,\n  renderItem,\n  dragHandle,\n  collapsed,\n  maxItems,\n  disabled,\n  getItemDisabled,\n  getItemLabel,\n  emptyPlaceholder,\n  actions,\n  reorderable,\n  activeIsColumn,\n  columnMaxHeight,\n  hasMore,\n  onLoadMore,\n  loadMoreThreshold = 100,\n  className,\n  style,\n  classNames,\n  styles: slotStyles,\n  ...rest\n}: KanbanColumnInternalProps<T>) {\n  const droppableId = `column-${id}`;\n  const { setNodeRef, isOver } = useDroppable({ id: droppableId, disabled });\n  const isOverLimit = maxItems != null && itemIds.length > maxItems;\n\n  const itemsRef = useRef<HTMLDivElement | null>(null);\n  const setItemsRef = useCallback(\n    (node: HTMLDivElement | null) => {\n      itemsRef.current = node;\n      setNodeRef(node);\n    },\n    [setNodeRef],\n  );\n  const isLoadingMoreRef = useRef(false);\n  const [isLoadingMore, setIsLoadingMore] = useState(false);\n\n  const triggerLoadMore = useCallback(() => {\n    if (!onLoadMore || isLoadingMoreRef.current) return;\n    isLoadingMoreRef.current = true;\n    setIsLoadingMore(true);\n    Promise.resolve(onLoadMore()).finally(() => {\n      isLoadingMoreRef.current = false;\n      setIsLoadingMore(false);\n    });\n  }, [onLoadMore]);\n\n  const handleItemsScroll = useCallback(\n    (event: UIEvent<HTMLDivElement>) => {\n      if (!hasMore) return;\n      const el = event.currentTarget;\n      if (el.scrollTop + el.clientHeight >= el.scrollHeight - loadMoreThreshold) triggerLoadMore();\n    },\n    [hasMore, loadMoreThreshold, triggerLoadMore],\n  );\n\n  // The scroll trigger above can only fire once the list actually overflows and scrolls. A batch\n  // smaller than the column's own visible height never generates a scroll event, which would\n  // otherwise dead-end pagination after the first page. Re-probes at most once per (item count,\n  // visible height) pair, so settled content can't re-ask a source that already ran dry.\n  //\n  // It asks while the cards leave empty space below them, not while the list fails to overflow:\n  // on a board that grows with its cards no list ever overflows, and that rule loaded every page.\n  // This one fills a short column up to the tallest and stops.\n  const fillProbeRef = useRef('');\n  useEffect(() => {\n    if (!hasMore || isLoadingMore) return;\n    const el = itemsRef.current;\n    if (!el || el.clientHeight === 0) return;\n    const last = el.lastElementChild as HTMLElement | null;\n    const paddingBottom = parseFloat(getComputedStyle(el).paddingBottom) || 0;\n    if ((last ? last.offsetTop + last.offsetHeight : 0) + paddingBottom >= el.clientHeight - 1) return;\n    const probe = `${itemIds.length}:${el.clientHeight}`;\n    if (fillProbeRef.current === probe) return;\n    fillProbeRef.current = probe;\n    triggerLoadMore();\n  }, [hasMore, isLoadingMore, itemIds.length, triggerLoadMore]);\n\n  const {\n    attributes: columnAttributes,\n    listeners: columnListeners,\n    setNodeRef: setColumnNodeRef,\n    setActivatorNodeRef: setColumnActivatorRef,\n    transform: columnTransform,\n    transition: columnTransition,\n    isDragging: isColumnDragging,\n    isOver: isColumnOver,\n  } = useSortable({\n    id,\n    data: COLUMN_SORTABLE_DATA,\n    disabled: reorderable ? false : COLUMN_SORTABLE_DISABLED,\n  });\n\n  return (\n    <div\n      {...rest}\n      ref={setColumnNodeRef}\n      id={id}\n      data-kanban-column=''\n      className={cx(\n        styles.column,\n        isOver && !activeIsColumn && styles.columnOver,\n        isColumnOver && styles.columnDropTarget,\n        collapsed && styles.columnCollapsed,\n        isOverLimit && styles.columnOverLimit,\n        disabled && styles.columnDisabled,\n        isColumnDragging && styles.columnDragging,\n        className,\n      )}\n      style={{ transform: CSS.Transform.toString(columnTransform), transition: columnTransition, ...style }}\n    >\n      {/* The whole header is the pointer grab area, but only the title is the keyboard handle: a\n          focusable header was a button around the actions' own buttons, and the Enter meant for\n          one of them picked the column up instead. */}\n      <div\n        className={cx(styles.columnHeader, reorderable && styles.columnHeaderReorderable, classNames?.header)}\n        style={slotStyles?.header}\n        {...(reorderable ? columnListeners : undefined)}\n      >\n        <ColumnHeaderContent\n          title={title}\n          count={itemIds.length}\n          maxItems={maxItems}\n          actions={actions}\n          titleProps={\n            reorderable ? { ...columnAttributes, ref: setColumnActivatorRef, 'data-kanban-handle': '' } : undefined\n          }\n          classNames={classNames}\n          styles={slotStyles}\n        />\n      </div>\n      <SortableContext id={id} items={itemIds} strategy={verticalListSortingStrategy}>\n        <div\n          ref={setItemsRef}\n          className={cx(styles.columnItems, classNames?.items)}\n          style={{ maxHeight: columnMaxHeight, ...slotStyles?.items }}\n          onScroll={handleItemsScroll}\n        >\n          {itemIds.length === 0 && emptyPlaceholder != null ? (\n            <div className={cx(styles.columnPlaceholder, classNames?.placeholder)} style={slotStyles?.placeholder}>\n              {emptyPlaceholder}\n            </div>\n          ) : null}\n          {itemIds.map((itemId) => (\n            <KanbanItem\n              key={itemId}\n              id={itemId}\n              label={items[itemId] !== undefined ? getItemLabel?.(items[itemId], itemId) : undefined}\n              dragHandle={dragHandle}\n              disabled={\n                disabled || (items[itemId] !== undefined && (getItemDisabled?.(items[itemId], itemId) ?? false))\n              }\n              preventAsDropTarget={disabled}\n            >\n              {items[itemId] !== undefined ? renderItem(items[itemId], itemId) : null}\n            </KanbanItem>\n          ))}\n          {isLoadingMore && (\n            <div\n              className={cx(styles.columnLoadingIndicator, classNames?.loadingIndicator)}\n              style={slotStyles?.loadingIndicator}\n            >\n              <Loader size='2.6em' />\n            </div>\n          )}\n        </div>\n      </SortableContext>\n    </div>\n  );\n}\n\nexport default KanbanColumn;\n","import { arrayMove, hasSortableData } from '@dnd-kit/sortable';\nimport {\n  closestCorners,\n  KeyboardCode,\n  type Announcements,\n  type ClientRect,\n  type CollisionDetection,\n  type DroppableContainer,\n  type KeyboardCoordinateGetter,\n  type UniqueIdentifier,\n} from '@dnd-kit/core';\n\nimport type { KanbanColumnDef } from './kanbanBoard.types';\n\nexport function columnsEqual(a: KanbanColumnDef[], b: KanbanColumnDef[]): boolean {\n  if (a.length !== b.length) return false;\n  return a.every((col, i) => {\n    const other = b[i];\n    return (\n      col.id === other.id &&\n      col.itemIds.length === other.itemIds.length &&\n      col.itemIds.every((itemId, j) => itemId === other.itemIds[j])\n    );\n  });\n}\n\nexport function findColumnForItem(columns: KanbanColumnDef[], itemId: string): number {\n  return columns.findIndex((col) => col.itemIds.includes(itemId));\n}\n\n/** Whether a drag's `active`/`over` participant is a whole column (vs. a card) — see `reorderableColumns`. */\nexport function isColumnDragData(data: unknown): boolean {\n  return Boolean(data && typeof data === 'object' && (data as { type?: string }).type === 'column');\n}\n\n/**\n * Reorders whole columns: moves `activeColumnId` to `overColumnId`'s position. Returns the same\n * `columns` reference, unchanged, when either id doesn't resolve to a column or they're the same\n * — callers can use that to skip a state update.\n */\nexport function moveColumn(\n  columns: KanbanColumnDef[],\n  activeColumnId: string,\n  overColumnId: string,\n): KanbanColumnDef[] {\n  const oldIndex = columns.findIndex((col) => col.id === activeColumnId);\n  const overIndex = columns.findIndex((col) => col.id === overColumnId);\n  if (oldIndex === -1 || overIndex === -1 || oldIndex === overIndex) return columns;\n\n  return arrayMove(columns, oldIndex, overIndex);\n}\n\n/**\n * Collision detection for the board's shared `DndContext`, which has to pick between two very\n * differently-scaled sets of droppables at once: individual cards / a column's own card list\n * (small, numerous) and whole columns (large, few). Plain `closestCorners` weighs every\n * registered droppable together regardless of what's actually being dragged, so while dragging a\n * column it competes against every card and card-list droppable it happens to fly over too — the\n * column-level target only wins when the pointer lands somewhere that geometrically favors it,\n * which reads as needing to \"wiggle it around\" before the right drop target lights up. This\n * restricts the candidates to only the same kind as whatever is being dragged: a column drag only\n * considers other columns, a card drag only considers cards and card lists.\n *\n * A card drag also lets the pointer pick the column, and `closestCorners` only the position within\n * it. Corners alone compare the dragged card's whole rect, so a wide card over a collapsed column\n * had its corners nearer the neighbours' cards, and landed beside the column under the pointer.\n * Without a pointer (the keyboard) or between columns, every column is a candidate as before.\n */\nexport const kanbanCollisionDetection: CollisionDetection = (args) => {\n  const draggingColumn = isColumnDragData(args.active.data.current);\n  const droppableContainers = args.droppableContainers.filter((container) =>\n    draggingColumn ? isColumnDragData(container.data.current) : !isColumnDragData(container.data.current),\n  );\n  const pointerX = args.pointerCoordinates?.x;\n  const lane =\n    draggingColumn || pointerX === undefined\n      ? undefined\n      : droppableContainers.find((container) => {\n          const rect = args.droppableRects.get(container.id);\n          return (\n            resolveColumnDropId(String(container.id)) !== null &&\n            rect &&\n            rect.left <= pointerX &&\n            pointerX <= rect.right\n          );\n        });\n  if (!lane) return closestCorners({ ...args, droppableContainers });\n\n  const laneColumnId = resolveColumnDropId(String(lane.id));\n  return closestCorners({\n    ...args,\n    droppableContainers: droppableContainers.filter(\n      (container) =>\n        container === lane ||\n        (hasSortableData(container) && container.data.current.sortable.containerId === laneColumnId),\n    ),\n  });\n};\n\n/**\n * Whether the auto-scroll may move `scroller`, an ancestor of the board such as the page, while\n * something is dragged: only toward a part of the board that is out of view. Nothing beyond the\n * board takes a drop, and a page left free to scroll carried the whole board off screen.\n */\nexport function scrollsTowardBoard(\n  scroller: { top: number; bottom: number },\n  board: { top: number; bottom: number },\n  dragged: { top: number; height: number },\n): boolean {\n  const goingDown = dragged.top + dragged.height / 2 > (scroller.top + scroller.bottom) / 2;\n  return goingDown ? board.bottom > scroller.bottom + 1 : board.top < scroller.top - 1;\n}\n\n/** Extracts the column id from a column droppable's `column-<id>` identifier, or `null` for a card id. */\nexport function resolveColumnDropId(overId: string): string | null {\n  return overId.startsWith('column-') ? overId.slice('column-'.length) : null;\n}\n\nexport interface DragGeometry {\n  /** The dragged card's current rect (translated by the drag delta), if measured. */\n  activeRect: ClientRect | null;\n  /** The rect of whatever `overId` refers to — a card, or a column's own droppable. */\n  overRect: ClientRect;\n}\n\n/**\n * Computes the columns that result from dragging `activeItemId` over `overId` (a card id, or a\n * `column-<id>` droppable id for empty column space). Returns the same `columns` reference,\n * unchanged, when the drag doesn't resolve to a valid source/destination — callers can use that\n * to skip a state update.\n *\n * `geometry` resolves cross-column drops that land on a card or gap with more than one possible\n * position (see the two branches below for what each one decides).\n */\nexport function moveItem(\n  columns: KanbanColumnDef[],\n  activeItemId: string,\n  overId: string,\n  geometry: DragGeometry,\n): KanbanColumnDef[] {\n  // Only the source/destination column(s) actually change, so only those get new object/array\n  // references — every other column keeps its exact `columns` reference. Cloning every column's\n  // `itemIds` up front (as this used to) cost O(total cards across the whole board) on *every*\n  // drag-over event, not just the couple of columns actually involved in the move.\n  const sourceColIndex = findColumnForItem(columns, activeItemId);\n  if (sourceColIndex === -1) return columns;\n\n  const destColId = resolveColumnDropId(overId);\n  const destColIndex =\n    destColId !== null ? columns.findIndex((col) => col.id === destColId) : findColumnForItem(columns, overId);\n  if (destColIndex === -1) return columns;\n\n  if (sourceColIndex === destColIndex) {\n    const col = columns[sourceColIndex];\n    const oldIndex = col.itemIds.indexOf(activeItemId);\n    if (oldIndex === -1) return columns;\n\n    if (destColId !== null) {\n      // over is the column's own droppable, not a card: nothing else claimed the collision,\n      // which happens hovering the empty gap above/below the cards, or — critically — an edge\n      // next to a disabled/pinned card with no other card nearby, since a disabled card's own\n      // droppable is excluded from collision candidates entirely. Without this branch, that\n      // case fell through to `col.itemIds.indexOf(overId)` below, which is always -1 for a\n      // `column-<id>` id (it's not a card id), silently no-op-ing instead of reordering.\n      const { activeRect, overRect } = geometry;\n      const isBelowCenter = Boolean(\n        activeRect && activeRect.top + activeRect.height / 2 > overRect.top + overRect.height / 2,\n      );\n      const targetIndex = isBelowCenter ? col.itemIds.length - 1 : 0;\n      if (targetIndex === oldIndex) return columns;\n\n      const next = [...columns];\n      next[sourceColIndex] = { ...col, itemIds: arrayMove(col.itemIds, oldIndex, targetIndex) };\n      return next;\n    }\n\n    const newIndex = col.itemIds.indexOf(overId);\n    if (newIndex === -1 || oldIndex === newIndex) return columns;\n\n    const next = [...columns];\n    next[sourceColIndex] = { ...col, itemIds: arrayMove(col.itemIds, oldIndex, newIndex) };\n    return next;\n  }\n\n  const sourceCol = columns[sourceColIndex];\n  const destCol = columns[destColIndex];\n  const destItemIds = [...destCol.itemIds];\n  const { activeRect, overRect } = geometry;\n\n  let insertAt: number;\n  if (destColId !== null) {\n    // Hovering the column's own droppable directly, rather than a specific card: the empty gap\n    // above the first card, the empty gap below the last card, or the whole area when the column\n    // has no cards at all. Comparing centers against the *container's* full-height rect is a\n    // coarse but reliable way to tell the top gap (insert at the start) from the bottom gap\n    // (append at the end) — treating every container hover as \"append\" put a card dropped just\n    // above the first item at the end of the column instead.\n    const isBelowCenter = Boolean(\n      activeRect && activeRect.top + activeRect.height / 2 > overRect.top + overRect.height / 2,\n    );\n    insertAt = isBelowCenter ? destItemIds.length : 0;\n  } else {\n    const overIndex = destItemIds.indexOf(overId);\n\n    // Only the last card's own drop zone extends past its bottom edge with nothing after it to\n    // compete for the collision, so it's the one case where the dragged card can end up \"after\"\n    // it. Requiring the dragged rect's *center* (not its top edge) to pass the hovered card's\n    // bottom edge is deliberately more forgiving than requiring the whole dragged card to clear it\n    // — that stricter version needs the user to drag a full card's height past the last item\n    // before it registers, which in practice almost never happens before the drop, silently\n    // landing the card second-to-last every time despite the live preview showing it last. A pure\n    // center-to-center comparison is too lenient in the other direction: the dragged rect's center\n    // still carries the grab offset from wherever in the source card it was originally picked up,\n    // so a low enough grab point reads as \"past\" well before the drag is visually past the card at\n    // all. Anchoring to the target's *bottom* edge (not its center) keeps that grab-offset problem\n    // from creeping back in while still being achievable in a normal drag.\n    const isLastItem = overIndex !== -1 && overIndex === destItemIds.length - 1;\n    const hasClearedItem = Boolean(\n      activeRect && activeRect.top + activeRect.height / 2 > overRect.top + overRect.height,\n    );\n    insertAt = overIndex === -1 ? destItemIds.length : isLastItem && hasClearedItem ? overIndex + 1 : overIndex;\n  }\n\n  destItemIds.splice(insertAt, 0, activeItemId);\n\n  const next = [...columns];\n  next[sourceColIndex] = { ...sourceCol, itemIds: sourceCol.itemIds.filter((id) => id !== activeItemId) };\n  next[destColIndex] = { ...destCol, itemIds: destItemIds };\n  return next;\n}\n\n/**\n * Where a drag has put the dragged card or column so far. The board keeps this rather than a copy\n * of `columns`, so a change the consumer makes mid-drag (a page appended by `onLoadMore`) still\n * shows, and is still there in what `onChange` receives on drop.\n */\nexport interface DragPlacement {\n  /** The dragged card's or column's id. */\n  id: string;\n  /** The column the card sits in, or `null` while a whole column is dragged. */\n  columnId: string | null;\n  index: number;\n}\n\n/** The position `id` holds in `columns`, as a placement to lay over a later `columns`. */\nexport function findPlacement(columns: KanbanColumnDef[], id: string, isColumn: boolean): DragPlacement | null {\n  if (isColumn) {\n    const index = columns.findIndex((col) => col.id === id);\n    return index === -1 ? null : { id, columnId: null, index };\n  }\n  const columnIndex = findColumnForItem(columns, id);\n  if (columnIndex === -1) return null;\n  const col = columns[columnIndex];\n  return { id, columnId: col.id, index: col.itemIds.indexOf(id) };\n}\n\n/**\n * Moves the dragged card or column to its placement in `columns`. Returns `columns` itself when\n * there is no placement, or when the card, the column, or its destination is no longer there.\n */\nexport function applyPlacement(columns: KanbanColumnDef[], placement: DragPlacement | null): KanbanColumnDef[] {\n  if (!placement) return columns;\n  const { id, columnId, index } = placement;\n\n  if (columnId === null) {\n    const oldIndex = columns.findIndex((col) => col.id === id);\n    return oldIndex === -1 ? columns : arrayMove(columns, oldIndex, index);\n  }\n\n  const sourceColIndex = findColumnForItem(columns, id);\n  const destColIndex = columns.findIndex((col) => col.id === columnId);\n  if (sourceColIndex === -1 || destColIndex === -1) return columns;\n\n  const next = [...columns];\n  const sourceCol = columns[sourceColIndex];\n  next[sourceColIndex] = { ...sourceCol, itemIds: sourceCol.itemIds.filter((itemId) => itemId !== id) };\n  const destItemIds = [...next[destColIndex].itemIds];\n  destItemIds.splice(index, 0, id);\n  next[destColIndex] = { ...next[destColIndex], itemIds: destItemIds };\n  return next;\n}\n\nconst FOCUS_STOP = '[data-kanban-handle], [data-kanban-card]';\n\n/**\n * Where an arrow key moves focus from a focused card or column handle while nothing is picked up.\n * It mirrors what the same keys do to a picked-up card: Up/Down within the column, Left/Right to\n * the nearest card in the next column with anything to focus, or from a handle to the next handle.\n * Returns `null` when `from` is not a card or handle, or there is nowhere to go.\n */\nexport function findArrowFocusTarget(board: HTMLElement, from: HTMLElement, key: string): HTMLElement | null {\n  if (!from.matches(FOCUS_STOP)) return null;\n  const columns = [...board.querySelectorAll<HTMLElement>(':scope > [data-kanban-column]')];\n  const columnIndex = columns.findIndex((column) => column.contains(from));\n  if (columnIndex === -1) return null;\n  const stopsIn = (column: HTMLElement) => [...column.querySelectorAll<HTMLElement>(FOCUS_STOP)];\n\n  if (key === 'ArrowUp' || key === 'ArrowDown') {\n    const stops = stopsIn(columns[columnIndex]);\n    return stops[stops.indexOf(from) + (key === 'ArrowDown' ? 1 : -1)] ?? null;\n  }\n  if (key !== 'ArrowLeft' && key !== 'ArrowRight') return null;\n\n  const direction = key === 'ArrowRight' ? 1 : -1;\n  const top = from.getBoundingClientRect().top;\n  const distance = (el: HTMLElement) => Math.abs(el.getBoundingClientRect().top - top);\n  for (let i = columnIndex + direction; i >= 0 && i < columns.length; i += direction) {\n    const stops = stopsIn(columns[i]);\n    const handle = stops.find((stop) => stop.hasAttribute('data-kanban-handle'));\n    const cards = stops.filter((stop) => stop.hasAttribute('data-kanban-card'));\n    if (handle && (from.hasAttribute('data-kanban-handle') || cards.length === 0)) return handle;\n    if (cards.length > 0) return cards.reduce((closest, card) => (distance(card) < distance(closest) ? card : closest));\n  }\n  return null;\n}\n\n/**\n * Builds screen-reader drag announcements against the columns layout as currently understood\n * (the live layout while a drag is in progress, or the settled `columns` prop otherwise).\n */\nexport function buildAnnouncements<T>(\n  columns: KanbanColumnDef[],\n  items: Record<string, T>,\n  getItemLabel: (item: T, id: string) => string,\n): Announcements {\n  function labelFor(id: string): string {\n    const item = items[id];\n    return item !== undefined ? getItemLabel(item, id) : id;\n  }\n\n  function describeOver(overId: string): string {\n    const destColId = resolveColumnDropId(overId);\n    if (destColId !== null) {\n      const col = columns.find((c) => c.id === destColId);\n      return col ? `the ${col.title} column` : 'a column';\n    }\n\n    const col = columns.find((c) => c.itemIds.includes(overId));\n    return col ? `${labelFor(overId)}, in the ${col.title} column` : labelFor(overId);\n  }\n\n  function columnTitle(id: string): string {\n    return columns.find((c) => c.id === id)?.title ?? id;\n  }\n\n  return {\n    onDragStart({ active }) {\n      if (isColumnDragData(active.data.current)) return `Picked up column ${columnTitle(String(active.id))}.`;\n      return `Picked up card ${labelFor(String(active.id))}.`;\n    },\n    onDragOver({ active, over }) {\n      if (isColumnDragData(active.data.current)) {\n        if (!over) return `Column ${columnTitle(String(active.id))} is no longer over a droppable area.`;\n        return `Column ${columnTitle(String(active.id))} is over column ${columnTitle(String(over.id))}.`;\n      }\n      if (!over) return `Card ${labelFor(String(active.id))} is no longer over a droppable area.`;\n      return `Card ${labelFor(String(active.id))} is over ${describeOver(String(over.id))}.`;\n    },\n    onDragEnd({ active, over }) {\n      if (isColumnDragData(active.data.current)) {\n        if (!over) return `Column ${columnTitle(String(active.id))} was dropped.`;\n        return `Column ${columnTitle(String(active.id))} was dropped over column ${columnTitle(String(over.id))}.`;\n      }\n      if (!over) return `Card ${labelFor(String(active.id))} was dropped.`;\n      return `Card ${labelFor(String(active.id))} was dropped over ${describeOver(String(over.id))}.`;\n    },\n    onDragCancel({ active }) {\n      if (isColumnDragData(active.data.current)) {\n        return `Dragging cancelled. Column ${columnTitle(String(active.id))} was returned to its original position.`;\n      }\n      return `Dragging cancelled. Card ${labelFor(String(active.id))} was returned to its original position.`;\n    },\n  };\n}\n\ninterface ContainerRect {\n  container: DroppableContainer;\n  rect: ClientRect;\n}\n\nfunction collectColumnItemRects(\n  containers: DroppableContainer[],\n  droppableRects: Map<UniqueIdentifier, ClientRect>,\n  containerId: UniqueIdentifier,\n): ContainerRect[] {\n  const entries: ContainerRect[] = [];\n  for (const container of containers) {\n    if (!hasSortableData(container)) continue;\n    if (container.data.current.sortable.containerId !== containerId) continue;\n    const rect = droppableRects.get(container.id);\n    if (!rect) continue;\n    entries.push({ container, rect });\n  }\n  return entries.sort((a, b) => a.rect.top - b.rect.top);\n}\n\n/**\n * Custom keyboard coordinate getter for the board's multi-column, multi-`SortableContext`\n * layout. dnd-kit's default `sortableKeyboardCoordinates` picks the geometrically closest\n * droppable across the *entire* board — cards and column containers alike — which is unreliable\n * once cards live inside independent per-column `SortableContext`s: up/down and left/right end up\n * interchangeable depending on incidental rect positions. This instead restricts up/down to\n * reordering within the active card's own column, and left/right to moving into the adjacent\n * column (landing on the closest card there by vertical position, or the column itself when\n * empty). Requires each `KanbanColumn`'s `SortableContext` to be given an explicit `id` matching\n * its column id, so a card's `sortable.containerId` can be correlated with its `column-<id>`\n * droppable.\n */\nexport const kanbanKeyboardCoordinateGetter: KeyboardCoordinateGetter = (event, { context }) => {\n  const { active, collisionRect, droppableRects, droppableContainers } = context;\n\n  if (!active || !collisionRect || !hasSortableData(active)) return undefined;\n\n  const isVertical = event.code === KeyboardCode.Down || event.code === KeyboardCode.Up;\n  const isHorizontal = event.code === KeyboardCode.Left || event.code === KeyboardCode.Right;\n  if (!isVertical && !isHorizontal) return undefined;\n\n  event.preventDefault();\n\n  const activeContainerId = active.data.current.sortable.containerId;\n  const allContainers = droppableContainers.getEnabled();\n\n  if (isColumnDragData(active.data.current)) {\n    // Reordering a whole column: only left/right matters, moving it within the top-level\n    // column `SortableContext` (siblings share its containerId, same as cards share their\n    // own column's). Up/down has nothing to do — columns don't have a \"column above/below\".\n    if (!isHorizontal) return undefined;\n\n    const direction = event.code === KeyboardCode.Right ? 1 : -1;\n    const siblings = collectColumnItemRects(allContainers, droppableRects, activeContainerId).sort(\n      (a, b) => a.rect.left - b.rect.left,\n    );\n    const currentIndex = siblings.findIndex((s) => s.container.id === active.id);\n    if (currentIndex === -1) return undefined;\n\n    const target = siblings[currentIndex + direction];\n    if (!target) return undefined;\n\n    return { x: target.rect.left, y: collisionRect.top };\n  }\n\n  if (isVertical) {\n    const direction = event.code === KeyboardCode.Down ? 1 : -1;\n    const siblings = collectColumnItemRects(allContainers, droppableRects, activeContainerId);\n    const currentIndex = siblings.findIndex((s) => s.container.id === active.id);\n    if (currentIndex === -1) return undefined;\n\n    const target = siblings[currentIndex + direction];\n    if (!target) return undefined;\n\n    return { x: collisionRect.left, y: target.rect.top };\n  }\n\n  const direction = event.code === KeyboardCode.Right ? 1 : -1;\n\n  const columns: ContainerRect[] = [];\n  for (const container of allContainers) {\n    const columnId = resolveColumnDropId(String(container.id));\n    if (columnId === null) continue;\n    const rect = droppableRects.get(container.id);\n    if (!rect) continue;\n    columns.push({ container, rect });\n  }\n  columns.sort((a, b) => a.rect.left - b.rect.left);\n\n  const activeColumnIndex = columns.findIndex((c) => resolveColumnDropId(String(c.container.id)) === activeContainerId);\n  if (activeColumnIndex === -1) return undefined;\n\n  const targetColumn = columns[activeColumnIndex + direction];\n  if (!targetColumn) return undefined;\n\n  const targetContainerId = resolveColumnDropId(String(targetColumn.container.id)) as UniqueIdentifier;\n  const targetItems = collectColumnItemRects(allContainers, droppableRects, targetContainerId);\n\n  let targetRect = targetColumn.rect;\n  if (targetItems.length > 0) {\n    targetRect = targetItems.reduce((closest, item) =>\n      Math.abs(item.rect.top - collisionRect.top) < Math.abs(closest.rect.top - collisionRect.top) ? item : closest,\n    ).rect;\n  }\n\n  return { x: targetRect.left, y: targetRect.top };\n};\n","import type { KeyboardEvent } from 'react';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport {\n  DndContext,\n  DragOverlay,\n  KeyboardSensor,\n  MouseSensor,\n  TouchSensor,\n  useSensor,\n  useSensors,\n  type DragStartEvent,\n  type DragOverEvent,\n  type DragEndEvent,\n  type Active,\n  type ScreenReaderInstructions,\n} from '@dnd-kit/core';\nimport { SortableContext, horizontalListSortingStrategy } from '@dnd-kit/sortable';\nimport cx from 'classnames';\n\nimport type { KanbanBoardProps } from './kanbanBoard.types';\nimport styles from './kanbanBoard.module.pcss';\nimport KanbanColumn, { ColumnHeaderContent } from './KanbanColumn';\nimport {\n  applyPlacement,\n  buildAnnouncements,\n  columnsEqual,\n  findArrowFocusTarget,\n  findPlacement,\n  isColumnDragData,\n  kanbanCollisionDetection,\n  kanbanKeyboardCoordinateGetter,\n  moveColumn,\n  moveItem,\n  scrollsTowardBoard,\n  type DragPlacement,\n} from './kanbanBoard.utils';\n\nconst defaultGetItemLabel = <T,>(_item: T, id: string): string => id;\n\n// Hoisted so these are referentially stable across renders — inline literals here would give\n// `useSensor` a new options object every render (including every dragOver during an active\n// drag), which defeats its own memoization and makes `sensors` a new array each time too.\n//\n// Mouse and touch are split (rather than one combined `PointerSensor`) because they need\n// different activation constraints: a small movement threshold for mouse starts a drag\n// immediately without interfering with clicks, but the same threshold on a touchscreen would\n// hijack an ordinary scroll gesture as soon as the finger moves a few pixels. Touch instead\n// requires a brief press-and-hold before a drag arms, so a normal swipe-to-scroll passes through\n// untouched — the same pattern `VirtualList`'s `longPressDelay` uses.\nconst MOUSE_SENSOR_OPTIONS = { activationConstraint: { distance: 8 } };\nconst TOUCH_SENSOR_OPTIONS = { activationConstraint: { delay: 250, tolerance: 5 } };\nconst KEYBOARD_SENSOR_OPTIONS = { coordinateGetter: kanbanKeyboardCoordinateGetter };\n\nconst SCREEN_READER_INSTRUCTIONS: ScreenReaderInstructions = {\n  draggable: `\n    To pick up a card or column, press space or enter.\n    Use the arrow keys to move between cards and columns.\n    While dragging, use the arrow keys to move it — for a card, between positions and columns;\n    for a column, left/right to reorder it.\n    Press space or enter again to drop it, or press escape to cancel.\n  `,\n};\n\nfunction KanbanBoard<T = unknown>({\n  columns,\n  items,\n  renderItem,\n  onChange,\n  dragHandle,\n  getItemLabel = defaultGetItemLabel,\n  getItemDisabled,\n  renderEmptyColumn,\n  renderColumnActions,\n  reorderableColumns,\n  collapseEmptyColumns = false,\n  columnMaxHeight,\n  onLoadMore,\n  loadMoreThreshold,\n  onKeyDown,\n  className,\n  style,\n  classNames,\n  styles: slotStyles,\n  ...rest\n}: KanbanBoardProps<T>) {\n  const [activeId, setActiveId] = useState<string | null>(null);\n  const [activeIsColumn, setActiveIsColumn] = useState(false);\n  const [placement, setPlacement] = useState<DragPlacement | null>(null);\n\n  // Guards against a self-sustaining reorder loop: applying a card move reflows the columns\n  // involved, which can shift a *different* card under an otherwise-stationary pointer. dnd-kit\n  // then re-fires `dragOver` with that new `over`, even though the user hasn't moved anything —\n  // and if the geometry decision for that new target flips back once applied, the two states\n  // (or more) can bounce forever, purely from our own layout changes, eventually tripping React's\n  // \"Maximum update depth exceeded\" guard. `activeRect` only changes when the dragged card's own\n  // position actually moves (real pointer/keyboard input), so it's used here as a proxy for \"did\n  // anything the user did actually change\" — a reorder is only applied once per distinct\n  // `activeRect`, which lets a drag settle and then holds still instead of oscillating.\n  const lastAppliedActiveRectKeyRef = useRef<string | null>(null);\n\n  const boardRef = useRef<HTMLDivElement>(null);\n\n  // Lets an ancestor of the board (the page) auto-scroll only toward a part of the board out of\n  // view. Left to dnd-kit, a card held near the bottom of the viewport scrolled the page on until\n  // the board was gone; refused, dnd-kit moves on to the column's own list.\n  const draggedRectRef = useRef<Active['rect'] | null>(null);\n  const canScroll = useCallback((element: Element) => {\n    const board = boardRef.current;\n    const dragged = draggedRectRef.current?.current.translated;\n    if (!board || !dragged || board.contains(element)) return true;\n    const scroller =\n      element === document.scrollingElement ? { top: 0, bottom: window.innerHeight } : element.getBoundingClientRect();\n    return scrollsTowardBoard(scroller, board.getBoundingClientRect(), dragged);\n  }, []);\n  // dnd-kit asks `canScroll` only when it re-plans, which WebKit never prompts while the pointer\n  // holds still, so the page scrolled on past the board there. A render hands dnd-kit a new\n  // `canScroll`, which makes it re-plan: the scroll listener below asks for one when the page\n  // should stop.\n  const [, replanAutoScroll] = useState(0);\n\n  // A scroll is input too: it moves the cards under a pointer that holds still, which is what the\n  // auto-scroll at a column's edge does. Only a scroll that moves this board counts, so a scroller\n  // elsewhere on the page cannot switch the guard off for the whole drag.\n  const isDragging = activeId !== null;\n  useEffect(() => {\n    if (!isDragging) return;\n    const handleScroll = ({ target }: Event) => {\n      const board = boardRef.current;\n      if (!board || !(target instanceof Node) || !(target.contains(board) || board.contains(target))) return;\n      lastAppliedActiveRectKeyRef.current = null;\n      const scroller = target === document ? document.scrollingElement : target;\n      if (scroller instanceof Element && !canScroll(scroller)) replanAutoScroll((n) => n + 1);\n    };\n    // Capture, because a scroll event does not bubble, and the scroller can be any ancestor.\n    window.addEventListener('scroll', handleScroll, { capture: true, passive: true });\n    return () => window.removeEventListener('scroll', handleScroll, { capture: true });\n  }, [isDragging, canScroll]);\n\n  const sensors = useSensors(\n    useSensor(MouseSensor, MOUSE_SENSOR_OPTIONS),\n    useSensor(TouchSensor, TOUCH_SENSOR_OPTIONS),\n    useSensor(KeyboardSensor, KEYBOARD_SENSOR_OPTIONS),\n  );\n\n  // Rebuilt from the live `columns` on every change rather than snapshotted at drag start: a page\n  // `onLoadMore` appended mid-drag stayed invisible in the snapshot, so the column kept asking for\n  // more, and the drop then handed the snapshot to `onChange`, discarding every page loaded since.\n  const displayColumns = useMemo(() => applyPlacement(columns, placement), [columns, placement]);\n\n  // `SortableContext` memoizes its own derived `items`/context value keyed on this array's\n  // *reference*, not its contents — an inline `.map()` here would hand it a new array every\n  // render (including every `dragOver` while a drag is in progress, via `placement`), forcing\n  // every column's own `useSortable` to recompute and its whole subtree to re-render on every\n  // board render, not just when the column order actually changes. Same class of bug as the\n  // sensor options and the `data`/`disabled` objects in `KanbanColumn.tsx`/`KanbanItem.tsx`.\n  const columnIds = useMemo(() => displayColumns.map((col) => col.id), [displayColumns]);\n\n  // Read from `columns`, not `displayColumns`, so no column grows or shrinks during a drag: a\n  // width change under the pointer moves the drop targets, and a column that shrank mid-drag once\n  // fed a reorder loop. A column the drag empties or fills changes width once, after the drop.\n  const collapsedColumnIds = useMemo(\n    () => new Set(collapseEmptyColumns ? columns.filter((col) => col.itemIds.length === 0).map((col) => col.id) : []),\n    [columns, collapseEmptyColumns],\n  );\n\n  const announcements = useMemo(\n    () => buildAnnouncements(displayColumns, items, getItemLabel),\n    [displayColumns, items, getItemLabel],\n  );\n\n  const accessibility = useMemo(\n    () => ({ announcements, screenReaderInstructions: SCREEN_READER_INSTRUCTIONS }),\n    [announcements],\n  );\n\n  function handleKeyDown(event: KeyboardEvent<HTMLDivElement>) {\n    onKeyDown?.(event);\n    if (event.defaultPrevented || activeId !== null || !boardRef.current || !(event.target instanceof HTMLElement))\n      return;\n    if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) return;\n    const target = findArrowFocusTarget(boardRef.current, event.target, event.key);\n    if (!target) return;\n    event.preventDefault();\n    target.focus();\n  }\n\n  function handleDragStart({ active }: DragStartEvent) {\n    setActiveId(String(active.id));\n    setActiveIsColumn(isColumnDragData(active.data.current));\n    draggedRectRef.current = active.rect;\n    lastAppliedActiveRectKeyRef.current = null;\n  }\n\n  function handleDragOver({ active, over }: DragOverEvent) {\n    if (!over) return;\n\n    if (isColumnDragData(active.data.current)) {\n      const activeColumnId = String(active.id);\n      const overColumnId = String(over.id);\n\n      setPlacement((prev) => {\n        const base = applyPlacement(columns, prev);\n        const next = moveColumn(base, activeColumnId, overColumnId);\n        return next === base ? prev : findPlacement(next, activeColumnId, true);\n      });\n      return;\n    }\n\n    const activeItemId = String(active.id);\n    const overId = String(over.id);\n    const activeRect = active.rect.current.translated ?? active.rect.current.initial;\n\n    const activeRectKey = activeRect ? `${activeRect.top}:${activeRect.left}` : null;\n    if (activeRectKey !== null && activeRectKey === lastAppliedActiveRectKeyRef.current) {\n      return;\n    }\n    lastAppliedActiveRectKeyRef.current = activeRectKey;\n\n    setPlacement((prev) => {\n      const base = applyPlacement(columns, prev);\n      const next = moveItem(base, activeItemId, overId, { activeRect, overRect: over.rect });\n      return next === base ? prev : findPlacement(next, activeItemId, false);\n    });\n  }\n\n  function handleDragEnd({ over }: DragEndEvent) {\n    handleDragCancel();\n    if (over && !columnsEqual(displayColumns, columns)) onChange(displayColumns);\n  }\n\n  function handleDragCancel() {\n    setActiveId(null);\n    setActiveIsColumn(false);\n    setPlacement(null);\n  }\n\n  const activeColumn = activeId && activeIsColumn ? displayColumns.find((col) => col.id === activeId) : undefined;\n\n  return (\n    <DndContext\n      sensors={sensors}\n      collisionDetection={kanbanCollisionDetection}\n      onDragStart={handleDragStart}\n      onDragOver={handleDragOver}\n      onDragEnd={handleDragEnd}\n      onDragCancel={handleDragCancel}\n      accessibility={accessibility}\n      // A new `canScroll` every render on purpose, see `replanAutoScroll`.\n      autoScroll={{ canScroll: (element) => canScroll(element) }}\n    >\n      <SortableContext id='kanban-columns' items={columnIds} strategy={horizontalListSortingStrategy}>\n        <div {...rest} ref={boardRef} className={cx(styles.board, className)} style={style} onKeyDown={handleKeyDown}>\n          {displayColumns.map((col) => (\n            <KanbanColumn\n              key={col.id}\n              id={col.id}\n              title={col.title}\n              itemIds={col.itemIds}\n              maxItems={col.maxItems}\n              disabled={col.disabled}\n              getItemDisabled={getItemDisabled}\n              getItemLabel={getItemLabel}\n              emptyPlaceholder={renderEmptyColumn?.(col)}\n              actions={renderColumnActions?.(col)}\n              reorderable={reorderableColumns}\n              activeIsColumn={activeId !== null && activeIsColumn}\n              collapsed={collapsedColumnIds.has(col.id)}\n              columnMaxHeight={columnMaxHeight}\n              hasMore={col.hasMore}\n              onLoadMore={onLoadMore ? () => onLoadMore(col.id) : undefined}\n              loadMoreThreshold={loadMoreThreshold}\n              items={items}\n              renderItem={renderItem}\n              dragHandle={dragHandle}\n            />\n          ))}\n        </div>\n      </SortableContext>\n      <DragOverlay>\n        {activeColumn ? (\n          <div className={cx(styles.column, styles.columnDragOverlay)} inert>\n            <div className={styles.columnHeader}>\n              <ColumnHeaderContent\n                title={activeColumn.title}\n                count={activeColumn.itemIds.length}\n                maxItems={activeColumn.maxItems}\n                actions={renderColumnActions?.(activeColumn)}\n              />\n            </div>\n          </div>\n        ) : activeId && !activeIsColumn && items[activeId] !== undefined ? (\n          <div className={cx(styles.dragOverlay, classNames?.dragOverlay)} style={slotStyles?.dragOverlay}>\n            {renderItem(items[activeId], activeId)}\n          </div>\n        ) : null}\n      </DragOverlay>\n    </DndContext>\n  );\n}\n\nexport default KanbanBoard;\n","const BASE_URL = 'https://api.klipy.com/v2';\n\nexport interface KlipyMediaFormat {\n  url: string;\n  dims: [number, number];\n  size: number;\n}\n\nexport interface KlipyResult {\n  id: string;\n  title: string;\n  media_formats: {\n    gif: KlipyMediaFormat;\n    tinygif: KlipyMediaFormat;\n    mediumgif: KlipyMediaFormat;\n    mp4: KlipyMediaFormat;\n    tinymp4: KlipyMediaFormat;\n  };\n  tags: string[];\n  url: string;\n}\n\ninterface SearchOptions {\n  limit?: number;\n  contentfilter?: 'off' | 'low' | 'medium' | 'high';\n  pos?: string;\n}\n\nexport default class KlipyService {\n  private key: string;\n\n  constructor(key: string) {\n    this.key = key;\n  }\n\n  async search(query: string, options: SearchOptions = {}): Promise<{ results: KlipyResult[]; next: string }> {\n    const params = new URLSearchParams({ q: query, key: this.key });\n    if (options.limit != null) params.set('limit', String(options.limit));\n    if (options.contentfilter != null) params.set('contentfilter', options.contentfilter);\n    if (options.pos != null) params.set('pos', options.pos);\n\n    const res = await fetch(`${BASE_URL}/search?${params}`);\n    return res.json();\n  }\n\n  async searchSuggestions(query: string): Promise<{ results: string[] }> {\n    const params = new URLSearchParams({ q: query, key: this.key });\n    const res = await fetch(`${BASE_URL}/search_suggestions?${params}`);\n    return res.json();\n  }\n\n  registerShare(query: string, id: string): void {\n    const params = new URLSearchParams({ id, q: query, key: this.key });\n    fetch(`${BASE_URL}/registershare?${params}`).catch(() => null);\n  }\n}\n",".resultCount {\n  margin: 6px 0 0;\n  font-size: 12px;\n  color: var(--text-dark);\n}\n","import { useEffect, useRef, type ReactElement } from 'react';\nimport cx from 'classnames';\nimport Input from 'components/common/input';\nimport { InputType } from 'components/common/input/input.types';\nimport styles from './searchInput.module.pcss';\nimport { useLabels } from '../configProvider';\nimport type { SearchInputLabels, SearchInputProps } from './searchInput.types';\n\nconst DEFAULT_LABELS: Required<SearchInputLabels> = {\n  results: (count) => `${count} ${count === 1 ? 'result' : 'results'}`,\n};\n\nexport default function SearchInput({\n  value,\n  onChange,\n  onDebounce,\n  placeholder,\n  label,\n  debounceMs = 500,\n  totalAmount,\n  showAmount = true,\n  className,\n  width,\n  style,\n  classNames,\n  styles: slotStyles,\n  labels,\n  ref,\n  ...rest\n}: SearchInputProps): ReactElement {\n  const text = useLabels('SearchInput', labels, DEFAULT_LABELS);\n  const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n  useEffect(\n    () => () => {\n      if (timerRef.current !== null) clearTimeout(timerRef.current);\n    },\n    [],\n  );\n\n  function handleChange(val: unknown) {\n    const str = val != null ? String(val) : '';\n    const trimmed = str.trim() === '' ? '' : str;\n    onChange(trimmed);\n    if (timerRef.current !== null) clearTimeout(timerRef.current);\n    timerRef.current = setTimeout(() => {\n      onDebounce?.(trimmed);\n    }, debounceMs);\n  }\n\n  const showCount = showAmount && typeof totalAmount === 'number' && !Number.isNaN(totalAmount);\n\n  return (\n    <div\n      {...rest}\n      className={cx(className, classNames?.root)}\n      style={{ ...(width != null ? { width } : undefined), ...style, ...slotStyles?.root }}\n    >\n      <Input\n        ref={ref}\n        autoComplete='off'\n        className={classNames?.input}\n        style={slotStyles?.input}\n        type={InputType.Search}\n        label={label}\n        placeholder={placeholder}\n        value={value}\n        onChange={handleChange}\n      />\n      {showCount && (\n        <p className={cx(styles.resultCount, classNames?.resultCount)} style={slotStyles?.resultCount}>\n          {text.results(totalAmount)}\n        </p>\n      )}\n    </div>\n  );\n}\n",".gifPreview {\n  width: min(420px, 90vw);\n  aspect-ratio: 1;\n  display: flex;\n  flex-wrap: wrap;\n\n  --gif-preview-gap-size: 5px;\n\n  gap: var(--gif-preview-gap-size);\n  position: relative;\n  justify-content: center;\n  overflow: auto;\n\n  &-image {\n    background-position: center;\n    background-size: cover;\n    width: 100%;\n    height: 100%;\n\n    &-container {\n      aspect-ratio: 1;\n      background: linear-gradient(\n        45deg,\n        var(--primary-color) 10%,\n        var(--primary-lighter),\n        var(--primary-accent),\n        var(--primary-color) 85%\n      );\n      background-size: 500% 500%;\n      animation: gradientBottomLeftToTopRight 1s infinite linear;\n      border-radius: var(--default-border-radius);\n      overflow: hidden;\n      width: min(calc(140px - var(--gif-preview-gap-size)), calc(33.3% - var(--gif-preview-gap-size)));\n    }\n  }\n}\n\n/* The gradient stops sweeping and stays a plain block, which still reads as \"loading\". */\n@media (prefers-reduced-motion: reduce) {\n  .gifPreview-image-container {\n    animation: none;\n  }\n}\n","import cx from 'classnames';\nimport type { ReactElement, UIEvent } from 'react';\nimport InteractableDiv from 'components/common/interactableDiv';\nimport type { GifPreviewProps } from './gifPreview.types';\nimport styles from './gifPreview.module.pcss';\n\nexport default function GifPreview({\n  className,\n  previewItems = [],\n  onSelect,\n  onLoadAdditional,\n}: GifPreviewProps): ReactElement {\n  function handleScroll(event: UIEvent<HTMLDivElement>) {\n    const el = event.currentTarget;\n    const loadAdditionalHeight = el.scrollHeight - el.clientHeight * 1.5;\n    if (el.scrollTop >= loadAdditionalHeight) {\n      onLoadAdditional?.();\n    }\n  }\n\n  return (\n    <>\n      {previewItems.length > 0 && (\n        <div className={cx(styles.gifPreview, className)} onScroll={handleScroll}>\n          {previewItems.map((preview, index) => (\n            <InteractableDiv\n              key={`${preview.id}_${index}`}\n              className={styles.gifPreviewImageContainer}\n              onClick={() => onSelect?.(preview)}\n            >\n              <div\n                className={styles.gifPreviewImage}\n                style={{ backgroundImage: `url(${preview.media_formats.tinygif.url})` }}\n              />\n            </InteractableDiv>\n          ))}\n        </div>\n      )}\n    </>\n  );\n}\n",".gifView {\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  position: relative;\n\n  img {\n    max-width: 100%;\n    border-radius: var(--default-border-radius);\n    overflow: hidden;\n  }\n\n  &-loading {\n    position: absolute;\n    width: 100%;\n    height: 100%;\n    top: 0;\n    left: 0;\n    background: linear-gradient(\n      45deg,\n      var(--primary-color) 10%,\n      var(--primary-lighter),\n      var(--primary-accent),\n      var(--primary-color) 85%\n    );\n    background-size: 500% 500%;\n    animation: gradientBottomLeftToTopRight 1s infinite linear;\n    border-radius: var(--default-border-radius);\n    overflow: hidden;\n  }\n}\n\n/* The gradient stops sweeping and stays a plain block, which still reads as \"loading\". */\n@media (prefers-reduced-motion: reduce) {\n  .gifView-loading {\n    animation: none;\n  }\n}\n","import { useState } from 'react';\nimport cx from 'classnames';\nimport type { ReactElement } from 'react';\nimport type { GifViewProps } from './gifView.types';\nimport styles from './gifView.module.pcss';\n\nexport default function GifView({ className, src = '' }: GifViewProps): ReactElement {\n  const [loadedSrc, setLoadedSrc] = useState<string | null>(null);\n  const isLoading = loadedSrc !== src;\n\n  return (\n    <div className={cx(styles.gifView, className)}>\n      <img className={styles.klipyPickerSelected} src={src} onLoad={() => setLoadedSrc(src)} />\n      {isLoading && <div className={styles.gifViewLoading} />}\n    </div>\n  );\n}\n",".klipyPicker {\n  &-selected {\n    margin-top: 8px;\n  }\n\n  &-modal {\n    &-content {\n      display: flex;\n      flex-flow: column;\n      gap: var(--spacing-m);\n    }\n  }\n\n  &-suggestions {\n    &-container {\n      max-width: 420px;\n    }\n\n    &-item {\n      cursor: pointer;\n      -webkit-tap-highlight-color: transparent;\n    }\n  }\n}\n","import { useMemo, useState } from 'react';\nimport cx from 'classnames';\nimport type { ReactElement, ReactNode } from 'react';\nimport KlipyService from 'services/klipy';\nimport type { KlipyResult } from 'services/klipy';\nimport SearchInput from '../searchInput';\nimport GifPreview from './components/gifPreview';\nimport Popover from '../popover';\nimport Button, { ButtonStyleType } from '../button';\nimport Modal from '../modal';\nimport Chip, { ChipContainer } from '../chip';\nimport GifView from './components/gifView';\nimport type { KlipyPickerLabels, KlipyPickerProps } from './klipyPicker.types';\nimport { useLabels } from '../configProvider';\n\nconst DEFAULT_LABELS: Required<KlipyPickerLabels> = {\n  search: 'Pick gif',\n};\nimport styles from './klipyPicker.module.pcss';\nimport actionButtonStyles from 'components/common/actionButtons/actionButtons.module.pcss';\nimport { PopoverAlign, PopoverPlacement } from '../popover/popover.types';\nimport { ThemeColor } from '../themeProvider/color.types';\n\nexport default function KlipyPicker({\n  className,\n  token = '',\n  isModal = false,\n  onSelect = () => undefined,\n  selected = null,\n  labels,\n}: KlipyPickerProps): ReactElement {\n  const text = useLabels('KlipyPicker', labels, DEFAULT_LABELS);\n  const klipyService = useMemo(() => new KlipyService(token), [token]);\n\n  const [searchText, setSearchText] = useState('');\n  const [currentPreviews, setCurrentPreviews] = useState<KlipyResult[]>([]);\n  const [currentSuggestions, setCurrentSuggestions] = useState<string[]>([]);\n  const [lastNext, setLastNext] = useState<string | null>(null);\n  const [isMenuOpen, setIsMenuOpen] = useState(false);\n\n  async function searchFor(query: string) {\n    setSearchText(query);\n    if (!query.trim()) {\n      setCurrentPreviews([]);\n      setCurrentSuggestions([]);\n      setLastNext(null);\n      return;\n    }\n    const [{ results: searchResults = [], next = null }, { results: suggestionResults = [] }] = await Promise.all([\n      klipyService.search(query, { limit: 18, contentfilter: 'medium' }),\n      klipyService.searchSuggestions(query),\n    ]);\n    setCurrentPreviews(searchResults);\n    setLastNext(next);\n    setCurrentSuggestions(suggestionResults);\n  }\n\n  async function handleLoadAdditional() {\n    const { results: searchResults = [], next = null } = await klipyService.search(searchText, {\n      limit: 36,\n      pos: lastNext ?? undefined,\n      contentfilter: 'medium',\n    });\n    setLastNext(next);\n    setCurrentPreviews((prev) => [...prev, ...searchResults]);\n  }\n\n  async function handlePreviewSelect(preview: KlipyResult | null) {\n    await onSelect(preview);\n    setIsMenuOpen(false);\n    if (preview) klipyService.registerShare(searchText, preview.id);\n  }\n\n  function renderSuggestions(): ReactNode {\n    return (\n      <ChipContainer inline className={styles.klipyPickerSuggestionsContainer}>\n        {currentSuggestions.map((suggestion) => (\n          <Chip\n            className={styles.klipyPickerSuggestionsItem}\n            key={suggestion}\n            text={suggestion}\n            onClick={() => searchFor(suggestion)}\n          />\n        ))}\n      </ChipContainer>\n    );\n  }\n\n  function renderPreviewPicker(): ReactNode {\n    return (\n      <GifPreview\n        previewItems={currentPreviews}\n        onSelect={handlePreviewSelect}\n        onLoadAdditional={handleLoadAdditional}\n      />\n    );\n  }\n\n  function renderSearchInput(): ReactNode {\n    return <SearchInput label={text.search} value={searchText} onDebounce={searchFor} onChange={setSearchText} />;\n  }\n\n  if (isModal) {\n    return (\n      <div className={cx(styles.klipyPicker, className)}>\n        <div className={actionButtonStyles.actionButtons}>\n          <Button onClick={() => setIsMenuOpen(true)}>Pick gif</Button>\n          {selected && (\n            <Button\n              styleType={ButtonStyleType.Filled}\n              color={ThemeColor.Error}\n              onClick={() => handlePreviewSelect(null)}\n            >\n              Remove gif\n            </Button>\n          )}\n        </div>\n        {selected && <GifView className={styles.klipyPickerSelected} src={selected} />}\n        <Modal isOpen={isMenuOpen} onClose={() => setIsMenuOpen(false)}>\n          <div className={styles.klipyPickerModalContent}>\n            {renderSearchInput()}\n            {renderSuggestions()}\n            {renderPreviewPicker()}\n          </div>\n        </Modal>\n      </div>\n    );\n  }\n\n  return (\n    <div className={cx(styles.klipyPicker, className)}>\n      <Popover\n        placement={PopoverPlacement.Bottom}\n        align={PopoverAlign.Start}\n        isOpen={currentPreviews.length > 0 && isMenuOpen}\n        onOpenChange={setIsMenuOpen}\n        // The panel opens while the user is typing in the search field, so a\n        // click back into the field must not close it again.\n        closeOnTriggerClick={false}\n        // The grid sets its own 420px cap.\n        constrainWidth={false}\n        content={\n          <>\n            {renderSuggestions()}\n            {renderPreviewPicker()}\n          </>\n        }\n      >\n        {renderSearchInput()}\n      </Popover>\n      {selected && (\n        <Button styleType={ButtonStyleType.Filled} color={ThemeColor.Error} onClick={() => handlePreviewSelect(null)}>\n          Remove gif\n        </Button>\n      )}\n      {selected && <GifView className={styles.klipyPickerSelected} src={selected} />}\n    </div>\n  );\n}\n","/** CSS `translate(x, y) scale(scale)` with `transform-origin: 0 0`. */\nexport interface PanZoomTransform {\n  x: number;\n  y: number;\n  scale: number;\n}\n\nexport interface Point {\n  x: number;\n  y: number;\n}\n\nexport interface Size {\n  width: number;\n  height: number;\n}\n\n/** A box in the viewport's own coordinates, before any transform. */\nexport interface Box {\n  left: number;\n  top: number;\n  width: number;\n  height: number;\n}\n\nexport const IDENTITY: PanZoomTransform = { x: 0, y: 0, scale: 1 };\n\n/** Below this, a scale counts as the fitted one. */\nexport const EPSILON = 0.001;\n\n/** Scales to `scale` while the content point under `focus` stays where it is on screen. */\nexport function zoomAt(transform: PanZoomTransform, scale: number, focus: Point): PanZoomTransform {\n  const ratio = scale / transform.scale;\n  return {\n    x: focus.x - (focus.x - transform.x) * ratio,\n    y: focus.y - (focus.y - transform.y) * ratio,\n    scale,\n  };\n}\n\n/** Larger than the viewport on an axis, its edges stay outside the viewport's; smaller, it is centred. */\nexport function clampTransform(transform: PanZoomTransform, viewport: Size, content: Box): PanZoomTransform {\n  return {\n    x: clampAxis(transform.x, transform.scale, viewport.width, content.left, content.width),\n    y: clampAxis(transform.y, transform.scale, viewport.height, content.top, content.height),\n    scale: transform.scale,\n  };\n}\n\n/**\n * At the fitted size the layout has centred the content already. Centring it again from measured\n * rects only adds their rounding: Chromium left 1/128 px, which counted as zoomed.\n */\nexport function settleTransform(transform: PanZoomTransform, viewport: Size, content: Box): PanZoomTransform {\n  if (transform.scale <= 1 + EPSILON && !canPan(1, viewport, content)) return IDENTITY;\n  return clampTransform(transform, viewport, content);\n}\n\nfunction clampAxis(offset: number, scale: number, viewport: number, start: number, size: number): number {\n  const scaledStart = start * scale;\n  const scaledSize = size * scale;\n  if (scaledSize <= viewport) return (viewport - scaledSize) / 2 - scaledStart;\n  return Math.min(-scaledStart, Math.max(viewport - scaledStart - scaledSize, offset));\n}\n\n/** Whether the content is larger than the viewport on either axis, so a drag can move it. */\nexport function canPan(scale: number, viewport: Size, content: Box): boolean {\n  // Half a pixel of slack, so rounding at the fitted size does not read as room to pan.\n  return content.width * scale > viewport.width + 0.5 || content.height * scale > viewport.height + 0.5;\n}\n",".panZoom {\n  position: relative;\n\n  /* `100%` tracks fit a sized root, and fall back to `auto` on an unsized one, which takes the content's height. */\n  display: grid;\n  grid-template: 100% / 100%;\n\n  /* Not `hidden`, which focus on a link in zoomed content scrolls, and the geometry ignores scroll. */\n  overflow: clip;\n\n  /* At the fitted size a one-finger drag still scrolls the page; the pinch is ours either way. */\n  touch-action: pan-x pan-y;\n  user-select: none;\n  -webkit-tap-highlight-color: transparent;\n\n  /* Inset, because the root clips a ring drawn outside. */\n  &:focus-visible {\n    outline: none;\n    box-shadow: inset 0 0 0 2px var(--focus-border-color, var(--primary-color));\n  }\n\n  &-capture {\n    touch-action: none;\n  }\n\n  &-zoomed {\n    cursor: grab;\n\n    &:active {\n      cursor: grabbing;\n    }\n  }\n\n  /* `transform-origin: 0 0` is what `panZoom.geometry.ts` computes against. */\n  &-content {\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    min-width: 0;\n    min-height: 0;\n    transform-origin: 0 0;\n\n    & > :is(img, svg, video, canvas) {\n      min-width: 0;\n      max-width: 100%;\n      min-height: 0;\n      max-height: 100%;\n    }\n\n    & > img {\n      -webkit-user-drag: none;\n    }\n  }\n\n  &-contentAnimated {\n    transition: transform 0.2s ease-out;\n  }\n\n  &-controls {\n    position: absolute;\n    right: var(--spacing-s, 8px);\n    bottom: var(--spacing-s, 8px);\n    z-index: 1;\n    display: flex;\n    gap: var(--spacing-xs, 4px);\n  }\n\n  /* Two classes, to outrank Button and ActionIcon whichever stylesheet loads first. */\n  & &-control {\n    width: 32px;\n    height: 32px;\n    border-radius: 50%;\n    box-shadow: var(--card-shadow);\n    transition: opacity 0.15s;\n  }\n\n  /* The click lands on the controls' box, which gestures ignore, not on the content behind. */\n  &-controlDisabled {\n    opacity: 0.35;\n    pointer-events: none;\n  }\n}\n\n/* Only the animated steps go; a pinch or a drag follows the finger. */\n@media (prefers-reduced-motion: reduce) {\n  .panZoom-contentAnimated {\n    transition: none;\n  }\n}\n","import {\n  useCallback,\n  useEffect,\n  useImperativeHandle,\n  useLayoutEffect,\n  useRef,\n  useState,\n  type KeyboardEvent as ReactKeyboardEvent,\n  type PointerEvent as ReactPointerEvent,\n} from 'react';\nimport cx from 'classnames';\nimport { faMagnifyingGlassMinus, faMagnifyingGlassPlus, faRotateLeft } from '@fortawesome/free-solid-svg-icons';\n\nimport ActionIcon from '../actionIcon';\nimport { ButtonStyleType } from '../button/button.types';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport { isInteractiveTarget } from '../utils/interactiveTarget';\nimport { canPan, EPSILON, IDENTITY, settleTransform, zoomAt } from './panZoom.geometry';\nimport type { Box, PanZoomTransform, Point, Size } from './panZoom.geometry';\nimport type { PanZoomLabels, PanZoomProps } from './panZoom.types';\nimport styles from './panZoom.module.pcss';\n\n// Opaque, as the buttons sit over the content. Carousel's arrows do the same.\nconst CONTROL_COLOR = 'var(--background-accent)';\n\nconst ZOOM_STEP = 1.5;\nconst DOUBLE_TAP_SCALE = 2;\n/** Carousel's swipe lock distance. */\nconst TAP_SLOP = 10;\nconst DOUBLE_TAP_MS = 300;\nconst DOUBLE_TAP_DISTANCE = 30;\nconst KEY_PAN_RATIO = 0.1;\n\n/** For content with no pixel size of its own, such as an SVG. */\nconst DEFAULT_MAX_SCALE = 4;\n/** So an image already shown at its own size still zooms. */\nconst MIN_IMAGE_MAX_SCALE = 2;\n\n// Calendar's flick window. A burst of samples within one frame says nothing about speed.\nconst VELOCITY_WINDOW_MS = 100;\nconst MIN_VELOCITY_SPAN_MS = 20;\n/** px/ms. */\nconst GLIDE_MIN_SPEED = 0.3;\nconst GLIDE_STOP_SPEED = 0.02;\n/** Speed kept per 16ms frame. */\nconst GLIDE_FRICTION = 0.95;\n\nconst ARROWS: Record<string, Point> = {\n  ArrowLeft: { x: -1, y: 0 },\n  ArrowRight: { x: 1, y: 0 },\n  ArrowUp: { x: 0, y: -1 },\n  ArrowDown: { x: 0, y: 1 },\n};\n\nconst DEFAULT_LABELS: Required<PanZoomLabels> = {\n  zoomIn: 'Zoom in',\n  zoomOut: 'Zoom out',\n  reset: 'Reset zoom',\n};\n\ninterface Geometry {\n  viewport: Size;\n  content: Box;\n  /** The viewport's top left corner, in client coordinates. */\n  origin: Point;\n}\n\ntype Gesture =\n  | { kind: 'pan'; start: Point; from: PanZoomTransform; geometry: Geometry; captured: boolean }\n  | { kind: 'pinch'; distance: number; midpoint: Point; from: PanZoomTransform; geometry: Geometry };\n\nfunction PanZoom(props: PanZoomProps) {\n  const {\n    children,\n    maxScale,\n    scrollToZoom = false,\n    showControls = true,\n    disabled = false,\n    onScaleChange,\n    onTap,\n    ref,\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    labels,\n    onPointerDown,\n    onPointerMove,\n    onPointerUp,\n    onPointerCancel,\n    onKeyDown,\n    onDragStart,\n    ...rest\n  } = useComponentDefaults('PanZoom', props);\n  const text = useLabels('PanZoom', labels, DEFAULT_LABELS);\n\n  const rootRef = useRef<HTMLDivElement>(null);\n  const contentRef = useRef<HTMLDivElement>(null);\n  const controlsRef = useRef<HTMLDivElement>(null);\n\n  const [transform, setTransform] = useState<PanZoomTransform>(IDENTITY);\n  const [animated, setAnimated] = useState(false);\n  // Two move events can arrive before React renders.\n  const transformRef = useRef(transform);\n\n  const pointers = useRef(new Map<number, Point>());\n  const gesture = useRef<Gesture | null>(null);\n  const press = useRef<{ start: Point; moved: boolean } | null>(null);\n  const lastTap = useRef<{ point: Point; time: number } | null>(null);\n  const tapTimer = useRef<ReturnType<typeof setTimeout>>(undefined);\n  const panSamples = useRef<{ point: Point; time: number }[]>([]);\n  const glideFrame = useRef<number | null>(null);\n\n  const [contentInfo, setContentInfo] = useState<ContentInfo>({ imageScale: null, isLoading: false });\n  // Nothing to zoom while an image loads, not even its thumbnail: zoomed in, that shows only mush.\n  const highestOf = (info: ContentInfo) =>\n    info.isLoading ? 1 : Math.max(maxScale ?? info.imageScale ?? DEFAULT_MAX_SCALE, 1);\n  const highestScale = highestOf(contentInfo);\n  const clampScale = (scale: number) => Math.min(Math.max(scale, 1), highestScale);\n\n  const commit = useCallback((next: PanZoomTransform, animate = false) => {\n    transformRef.current = next;\n    setTransform(next);\n    setAnimated(animate);\n  }, []);\n\n  /** `justLoaded`: an Image still marks it busy until it renders again. */\n  const updateContent = useCallback(\n    (justLoaded: EventTarget | null = null) => {\n      const content = contentRef.current;\n      if (!content) return;\n      const info = readContent(content, justLoaded);\n      setContentInfo((current) =>\n        current.imageScale === info.imageScale && current.isLoading === info.isLoading ? current : info,\n      );\n      // A new source has nothing to stay zoomed into.\n      if (info.isLoading && transformRef.current !== IDENTITY) commit(IDENTITY);\n    },\n    [commit],\n  );\n\n  // Every render: a new `src` comes with one, and no event says so.\n  useLayoutEffect(() => updateContent());\n\n  function settle(next: PanZoomTransform, geometry: Geometry, animate = false) {\n    commit(settleTransform(next, geometry.viewport, geometry.content), animate);\n  }\n\n  function stopGlide() {\n    if (glideFrame.current !== null) cancelAnimationFrame(glideFrame.current);\n    glideFrame.current = null;\n  }\n\n  function glide(releasedAt: number, geometry: Geometry) {\n    const recent = panSamples.current.filter((sample) => sample.time >= releasedAt - VELOCITY_WINDOW_MS);\n    const first = recent[0];\n    const last = recent[recent.length - 1];\n    if (!first || last.time - first.time < MIN_VELOCITY_SPAN_MS) return;\n    if (window.matchMedia?.('(prefers-reduced-motion: reduce)').matches) return;\n\n    const span = last.time - first.time;\n    let velocity = { x: (last.point.x - first.point.x) / span, y: (last.point.y - first.point.y) / span };\n    if (Math.hypot(velocity.x, velocity.y) < GLIDE_MIN_SPEED) return;\n\n    let previous = performance.now();\n    const step = (now: number) => {\n      const elapsed = now - previous;\n      previous = now;\n      const kept = GLIDE_FRICTION ** (elapsed / 16);\n      velocity = { x: velocity.x * kept, y: velocity.y * kept };\n      const current = transformRef.current;\n      const next = settleTransform(\n        { x: current.x + velocity.x * elapsed, y: current.y + velocity.y * elapsed, scale: current.scale },\n        geometry.viewport,\n        geometry.content,\n      );\n      // An edge stops the axis it reaches.\n      if (next.x === current.x) velocity.x = 0;\n      if (next.y === current.y) velocity.y = 0;\n      commit(next);\n      glideFrame.current = Math.hypot(velocity.x, velocity.y) > GLIDE_STOP_SPEED ? requestAnimationFrame(step) : null;\n    };\n    glideFrame.current = requestAnimationFrame(step);\n  }\n\n  /** Zooms around `clientPoint`, or around the middle of the viewport. */\n  function zoomTo(scale: number, clientPoint?: Point, animate = true) {\n    stopGlide();\n    const geometry = measure(rootRef.current, contentRef.current);\n    const focus = clientPoint\n      ? toLocal(clientPoint, geometry)\n      : { x: geometry.viewport.width / 2, y: geometry.viewport.height / 2 };\n    settle(zoomAt(transformRef.current, clampScale(scale), focus), geometry, animate);\n  }\n\n  function reset() {\n    stopGlide();\n    commit(IDENTITY, true);\n  }\n\n  function toggle(clientPoint?: Point) {\n    if (transformRef.current.scale > 1 + EPSILON) reset();\n    else zoomTo(DOUBLE_TAP_SCALE, clientPoint);\n  }\n\n  useImperativeHandle(ref, () => {\n    const unlessDisabled = (action: () => void) => () => {\n      if (!disabled) action();\n    };\n    return {\n      zoomIn: unlessDisabled(() => zoomTo(transformRef.current.scale * ZOOM_STEP)),\n      zoomOut: unlessDisabled(() => zoomTo(transformRef.current.scale / ZOOM_STEP)),\n      reset: unlessDisabled(reset),\n      toggle: unlessDisabled(() => toggle()),\n    };\n  });\n\n  const reportedScale = useRef(1);\n  useEffect(() => {\n    if (transform.scale === reportedScale.current) return;\n    reportedScale.current = transform.scale;\n    onScaleChange?.(transform.scale);\n  }, [transform.scale, onScaleChange]);\n\n  // Animated, so an image a Lightbox leaves zooms out as it slides off rather than jumping.\n  useEffect(() => {\n    if (!disabled) return;\n    pointers.current.clear();\n    gesture.current = null;\n    if (glideFrame.current !== null) cancelAnimationFrame(glideFrame.current);\n    glideFrame.current = null;\n    if (transformRef.current !== IDENTITY) commit(IDENTITY, true);\n  }, [disabled, commit]);\n\n  useEffect(\n    () => () => {\n      if (glideFrame.current !== null) cancelAnimationFrame(glideFrame.current);\n      clearTimeout(tapTimer.current);\n    },\n    [],\n  );\n\n  useEffect(() => {\n    const root = rootRef.current;\n    const content = contentRef.current;\n    if (!root || !content) return;\n    // Caught on the way down, as neither bubbles. A failed image is read before an Image replaces\n    // it with its fallback, and so leaves nothing to zoom.\n    const onLoad = (event: Event) => updateContent(event.target);\n    const onError = () => updateContent();\n    content.addEventListener('load', onLoad, true);\n    content.addEventListener('error', onError, true);\n    const observer = new ResizeObserver(() => {\n      updateContent();\n      const geometry = measure(root, content);\n      const next = settleTransform(transformRef.current, geometry.viewport, geometry.content);\n      const current = transformRef.current;\n      if (next.x !== current.x || next.y !== current.y || next.scale !== current.scale) commit(next);\n    });\n    observer.observe(root);\n    return () => {\n      observer.disconnect();\n      content.removeEventListener('load', onLoad, true);\n      content.removeEventListener('error', onError, true);\n    };\n  }, [commit, updateContent]);\n\n  // React's `wheel` is passive, and could not stop the page scrolling or the browser zooming.\n  const wheelHandlerRef = useRef<(event: WheelEvent) => void>(() => {});\n  useEffect(() => {\n    wheelHandlerRef.current = handleWheel;\n  });\n  useEffect(() => {\n    const root = rootRef.current;\n    if (!root) return;\n    const listener = (event: WheelEvent) => wheelHandlerRef.current(event);\n    root.addEventListener('wheel', listener, { passive: false });\n    return () => root.removeEventListener('wheel', listener);\n  }, []);\n\n  function handleWheel(event: WheelEvent) {\n    // A trackpad pinch arrives as a wheel event with `ctrlKey` set, in Safari too.\n    if (disabled || (!event.ctrlKey && !scrollToZoom)) return;\n    event.preventDefault();\n    // d3-zoom's scaling, for pixel, line and page deltas, and a pinch's far smaller steps.\n    const delta =\n      -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002) * (event.ctrlKey ? 10 : 1);\n    // Capped, or one notch of Ctrl + mouse wheel zooms 4x.\n    const factor = 2 ** Math.max(-0.5, Math.min(0.5, delta));\n    zoomTo(transformRef.current.scale * factor, { x: event.clientX, y: event.clientY }, false);\n  }\n\n  function pinchFrom(geometry: Geometry): Gesture {\n    const [a, b] = [...pointers.current.values()];\n    return {\n      kind: 'pinch',\n      distance: Math.hypot(b.x - a.x, b.y - a.y) || 1,\n      midpoint: toLocal(midpointOf(a, b), geometry),\n      from: transformRef.current,\n      geometry,\n    };\n  }\n\n  function panFrom(start: Point, geometry: Geometry, captured: boolean): Gesture | null {\n    if (!canPan(transformRef.current.scale, geometry.viewport, geometry.content)) return null;\n    return { kind: 'pan', start, from: transformRef.current, geometry, captured };\n  }\n\n  function handlePointerDown(event: ReactPointerEvent<HTMLDivElement>) {\n    onPointerDown?.(event);\n    if (disabled || controlsRef.current?.contains(event.target as Node)) return;\n    if (event.pointerType === 'mouse' && event.button !== 0) return;\n    // A press catches a glide, the way a finger stops a scrolling list.\n    stopGlide();\n    panSamples.current = [];\n\n    // No other finger is down, whatever was missed: one lifted outside uncaptured never said so.\n    if (event.isPrimary) pointers.current.clear();\n    const point = { x: event.clientX, y: event.clientY };\n    pointers.current.set(event.pointerId, point);\n    const geometry = measure(rootRef.current, contentRef.current);\n\n    if (pointers.current.size === 1) {\n      press.current = { start: point, moved: false };\n      gesture.current = panFrom(point, geometry, false);\n    } else {\n      // The first finger is a parent's swipe, which captured it, so its position here is stale.\n      if (press.current?.moved && !gesture.current) {\n        pointers.current.delete(event.pointerId);\n        return;\n      }\n      press.current = null;\n      gesture.current = pointers.current.size === 2 ? pinchFrom(geometry) : gesture.current;\n      for (const id of pointers.current.keys()) capture(event.currentTarget, id);\n    }\n\n    // Left alone at the fitted size, so a Carousel around this still gets its swipe.\n    if (gesture.current) {\n      event.stopPropagation();\n      setAnimated(false);\n    }\n  }\n\n  function handlePointerMove(event: ReactPointerEvent<HTMLDivElement>) {\n    onPointerMove?.(event);\n    if (!pointers.current.has(event.pointerId)) return;\n    const point = { x: event.clientX, y: event.clientY };\n    pointers.current.set(event.pointerId, point);\n\n    const current = press.current;\n    // `>=`, because Carousel locks a swipe at exactly this distance.\n    if (current && !current.moved && Math.hypot(point.x - current.start.x, point.y - current.start.y) >= TAP_SLOP) {\n      current.moved = true;\n    }\n\n    const active = gesture.current;\n    if (!active) return;\n    event.stopPropagation();\n\n    if (active.kind === 'pan') {\n      // Not on pointerdown: a captured pointer's click goes to the capture target, not the content.\n      if (!active.captured && current?.moved) {\n        capture(event.currentTarget, event.pointerId);\n        active.captured = true;\n      }\n      const { from, start } = active;\n      settle({ x: from.x + point.x - start.x, y: from.y + point.y - start.y, scale: from.scale }, active.geometry);\n      panSamples.current.push({ point, time: event.timeStamp });\n      if (panSamples.current.length > 20) panSamples.current.shift();\n      return;\n    }\n\n    const [a, b] = [...pointers.current.values()];\n    const scale = clampScale((active.from.scale * Math.hypot(b.x - a.x, b.y - a.y)) / active.distance);\n    const midpoint = toLocal(midpointOf(a, b), active.geometry);\n    const zoomed = zoomAt(active.from, scale, active.midpoint);\n    settle(\n      { x: zoomed.x + midpoint.x - active.midpoint.x, y: zoomed.y + midpoint.y - active.midpoint.y, scale },\n      active.geometry,\n    );\n  }\n\n  function handlePointerEnd(event: ReactPointerEvent<HTMLDivElement>) {\n    if (!pointers.current.delete(event.pointerId)) return;\n    const remaining = pointers.current.size;\n\n    if (remaining > 0) {\n      const geometry = measure(rootRef.current, contentRef.current);\n      const [next] = pointers.current.values();\n      gesture.current = remaining >= 2 ? pinchFrom(geometry) : panFrom(next, geometry, true);\n      return;\n    }\n\n    const ended = gesture.current;\n    gesture.current = null;\n    if (ended?.kind === 'pan' && event.type === 'pointerup') glide(event.timeStamp, ended.geometry);\n    const finished = press.current;\n    press.current = null;\n    if (event.type !== 'pointerup' || !finished || finished.moved) return;\n    if (isInteractiveTarget(event.target, rootRef.current)) return;\n\n    const tap = { point: { x: event.clientX, y: event.clientY }, time: event.timeStamp };\n    const previous = lastTap.current;\n    const isDouble =\n      previous &&\n      tap.time - previous.time < DOUBLE_TAP_MS &&\n      Math.hypot(tap.point.x - previous.point.x, tap.point.y - previous.point.y) < DOUBLE_TAP_DISTANCE;\n    lastTap.current = isDouble ? null : tap;\n    clearTimeout(tapTimer.current);\n    if (!isDouble) {\n      // Reported, it can no longer be half of a double tap: a tap at the edge of the window would do both.\n      if (onTap) {\n        tapTimer.current = setTimeout(() => {\n          lastTap.current = null;\n          onTap(event);\n        }, DOUBLE_TAP_MS);\n      }\n      return;\n    }\n\n    toggle(tap.point);\n  }\n\n  function handleKeyDown(event: ReactKeyboardEvent<HTMLDivElement>) {\n    onKeyDown?.(event);\n    if (disabled || event.defaultPrevented || event.ctrlKey || event.metaKey || event.altKey) return;\n    if (isInteractiveTarget(event.target, rootRef.current)) return;\n\n    stopGlide();\n    const current = transformRef.current;\n    if (event.key === '+' || event.key === '=') {\n      zoomTo(current.scale * ZOOM_STEP);\n    } else if (event.key === '-' || event.key === '_') {\n      zoomTo(current.scale / ZOOM_STEP);\n    } else if (event.key === '0') {\n      reset();\n    } else {\n      const direction = ARROWS[event.key];\n      if (!direction) return;\n      const geometry = measure(rootRef.current, contentRef.current);\n      // Left to a Lightbox around this, while there is nothing to pan.\n      if (!canPan(current.scale, geometry.viewport, geometry.content)) return;\n      const step = { x: geometry.viewport.width * KEY_PAN_RATIO, y: geometry.viewport.height * KEY_PAN_RATIO };\n      settle(\n        { x: current.x - direction.x * step.x, y: current.y - direction.y * step.y, scale: current.scale },\n        geometry,\n        true,\n      );\n    }\n    event.preventDefault();\n  }\n\n  const isZoomed = transform.scale > 1 + EPSILON;\n  const isAtMax = transform.scale >= highestScale - EPSILON;\n  const isIdentity = transform.x === 0 && transform.y === 0 && transform.scale === 1;\n\n  return (\n    <div\n      {...rest}\n      ref={rootRef}\n      tabIndex={disabled ? undefined : (rest.tabIndex ?? 0)}\n      className={cx(\n        styles.panZoom,\n        isZoomed && styles.panZoomZoomed,\n        (isZoomed || scrollToZoom) && styles.panZoomCapture,\n        className,\n        classNames?.root,\n      )}\n      style={{ ...style, ...slotStyles?.root }}\n      onPointerDown={handlePointerDown}\n      onPointerMove={handlePointerMove}\n      onPointerUp={(event) => {\n        onPointerUp?.(event);\n        handlePointerEnd(event);\n      }}\n      onPointerCancel={(event) => {\n        onPointerCancel?.(event);\n        handlePointerEnd(event);\n      }}\n      onKeyDown={handleKeyDown}\n      // The browser's image drag would take the pointer mid-pan.\n      onDragStart={(event) => {\n        onDragStart?.(event);\n        event.preventDefault();\n      }}\n    >\n      <div\n        ref={contentRef}\n        className={cx(styles.panZoomContent, animated && styles.panZoomContentAnimated, classNames?.content)}\n        style={{\n          transform: isIdentity ? undefined : `translate(${transform.x}px, ${transform.y}px) scale(${transform.scale})`,\n          ...slotStyles?.content,\n        }}\n      >\n        {children}\n      </div>\n\n      {showControls && !disabled && (\n        <div\n          ref={controlsRef}\n          className={cx(styles.panZoomControls, classNames?.controls)}\n          style={slotStyles?.controls}\n        >\n          <ActionIcon\n            styleType={ButtonStyleType.Filled}\n            color={CONTROL_COLOR}\n            icon={faMagnifyingGlassMinus}\n            title={text.zoomOut}\n            aria-label={text.zoomOut}\n            aria-disabled={!isZoomed || undefined}\n            onClick={() => {\n              if (isZoomed) zoomTo(transformRef.current.scale / ZOOM_STEP);\n            }}\n            className={cx(styles.panZoomControl, !isZoomed && styles.panZoomControlDisabled, classNames?.zoomOutButton)}\n            style={slotStyles?.zoomOutButton}\n          />\n          <ActionIcon\n            styleType={ButtonStyleType.Filled}\n            color={CONTROL_COLOR}\n            icon={faMagnifyingGlassPlus}\n            title={text.zoomIn}\n            aria-label={text.zoomIn}\n            aria-disabled={isAtMax || undefined}\n            onClick={() => {\n              if (!isAtMax) zoomTo(transformRef.current.scale * ZOOM_STEP);\n            }}\n            className={cx(styles.panZoomControl, isAtMax && styles.panZoomControlDisabled, classNames?.zoomInButton)}\n            style={slotStyles?.zoomInButton}\n          />\n          <ActionIcon\n            styleType={ButtonStyleType.Filled}\n            color={CONTROL_COLOR}\n            icon={faRotateLeft}\n            title={text.reset}\n            aria-label={text.reset}\n            aria-disabled={isIdentity || undefined}\n            onClick={() => {\n              if (!isIdentity) reset();\n            }}\n            className={cx(styles.panZoomControl, isIdentity && styles.panZoomControlDisabled, classNames?.resetButton)}\n            style={slotStyles?.resetButton}\n          />\n        </div>\n      )}\n    </div>\n  );\n}\n\n/**\n * All before any transform. The children's rects are divided by the rendered scale, not the one in\n * state, which halfway through an animated zoom differs.\n */\nfunction measure(root: HTMLElement | null, content: HTMLElement | null): Geometry {\n  if (!root || !content) return { viewport: { width: 0, height: 0 }, content: EMPTY_BOX, origin: { x: 0, y: 0 } };\n\n  const rootRect = root.getBoundingClientRect();\n  const origin = { x: rootRect.left + root.clientLeft, y: rootRect.top + root.clientTop };\n  const viewport = { width: content.offsetWidth, height: content.offsetHeight };\n  const contentRect = content.getBoundingClientRect();\n  const scale = viewport.width > 0 && contentRect.width > 0 ? contentRect.width / viewport.width : 1;\n\n  let left = Infinity;\n  let top = Infinity;\n  let right = -Infinity;\n  let bottom = -Infinity;\n  for (const child of content.children) {\n    const rect = child.getBoundingClientRect();\n    if (rect.width === 0 && rect.height === 0) continue;\n    left = Math.min(left, (rect.left - contentRect.left) / scale);\n    top = Math.min(top, (rect.top - contentRect.top) / scale);\n    right = Math.max(right, (rect.right - contentRect.left) / scale);\n    bottom = Math.max(bottom, (rect.bottom - contentRect.top) / scale);\n  }\n\n  // Nothing measurable, such as bare text: the whole viewport stands in for the content.\n  const box =\n    left === Infinity ? { left: 0, top: 0, ...viewport } : { left, top, width: right - left, height: bottom - top };\n  return { viewport, content: box, origin };\n}\n\nconst EMPTY_BOX: Box = { left: 0, top: 0, width: 0, height: 0 };\n\ninterface ContentInfo {\n  /** From the natural size: the rendered one is still the thumbnail's while a Lightbox image loads. */\n  imageScale: number | null;\n  isLoading: boolean;\n}\n\nfunction readContent(content: HTMLElement, justLoaded: EventTarget | null): ContentInfo {\n  const width = content.offsetWidth;\n  const height = content.offsetHeight;\n  let own = 0;\n  let isLoading = false;\n  for (const child of content.children) {\n    // By tag, not `instanceof`, which can fail across realms.\n    if (child.tagName !== 'IMG') continue;\n    const image = child as HTMLImageElement;\n    const busy = image !== justLoaded && image.getAttribute('aria-busy') === 'true';\n    if (busy || !image.complete || !image.naturalWidth) isLoading = true;\n    else if (width && height) own = Math.max(own, image.naturalWidth / width, image.naturalHeight / height);\n  }\n  return { imageScale: own ? Math.max(MIN_IMAGE_MAX_SCALE, own) : null, isLoading };\n}\n\nfunction capture(element: Element, pointerId: number) {\n  try {\n    element.setPointerCapture?.(pointerId);\n  } catch {\n    // The pointer is already gone.\n  }\n}\n\nfunction midpointOf(a: Point, b: Point): Point {\n  return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };\n}\n\nfunction toLocal(clientPoint: Point, geometry: Geometry): Point {\n  return { x: clientPoint.x - geometry.origin.x, y: clientPoint.y - geometry.origin.y };\n}\n\nexport default PanZoom;\n","/** A box on screen, in CSS pixels. */\nexport interface Rect {\n  left: number;\n  top: number;\n  width: number;\n  height: number;\n}\n\n/** A picture in flight: the box the whole picture fills, the part of it on show, and that part's corner radius. */\nexport interface Frame {\n  box: Rect;\n  clip: Rect;\n  radius: number;\n}\n\nexport function contain(aspect: number, rect: Rect): Rect {\n  return fit(aspect, rect, Math.min);\n}\n\nexport function cover(aspect: number, rect: Rect): Rect {\n  return fit(aspect, rect, Math.max);\n}\n\nfunction fit(aspect: number, rect: Rect, pick: (a: number, b: number) => number): Rect {\n  const width = pick(rect.width, rect.height * aspect);\n  const height = width / aspect;\n  return { left: rect.left + (rect.width - width) / 2, top: rect.top + (rect.height - height) / 2, width, height };\n}\n\nexport function intersect(a: Rect, b: Rect): Rect | null {\n  const left = Math.max(a.left, b.left);\n  const top = Math.max(a.top, b.top);\n  const right = Math.min(a.left + a.width, b.left + b.width);\n  const bottom = Math.min(a.top + a.height, b.top + b.height);\n  return right > left && bottom > top ? { left, top, width: right - left, height: bottom - top } : null;\n}\n\nfunction rectOf(element: Element): Rect {\n  const { left, top, width, height } = element.getBoundingClientRect();\n  return { left, top, width, height };\n}\n\n/**\n * The image on show where it sits on screen, cut to the PanZoom around it, which a zoomed-in image\n * overflows. `figure` is a Lightbox slide, whose first child is that PanZoom.\n */\nexport function pictureFrame(figure: Element): { frame: Frame; src: string; aspect: number } | null {\n  const viewport = figure.firstElementChild;\n  // Image keeps the `<img>` that is not on show at no size.\n  const image = [...(viewport?.querySelectorAll('img') ?? [])].find(\n    (img) => img.naturalWidth > 0 && img.getBoundingClientRect().width > 0,\n  );\n  if (!viewport || !image) return null;\n  const aspect = image.naturalWidth / image.naturalHeight;\n  // A thumbnail standing in fills a box of the screen's shape, `object-fit: contain`.\n  const box = contain(aspect, rectOf(image));\n  const clip = intersect(box, rectOf(viewport));\n  return clip && { frame: { box, clip, radius: 0 }, src: image.currentSrc, aspect };\n}\n\n/** The whole picture around a thumbnail, which is usually a crop (`object-fit: cover`). Null off screen. */\nexport function thumbnailFrame(thumbnail: Element, aspect: number): Frame | null {\n  const clip = rectOf(thumbnail);\n  const screen = { left: 0, top: 0, width: window.innerWidth, height: window.innerHeight };\n  if (!intersect(clip, screen)) return null;\n  return { box: cover(aspect, clip), clip, radius: radiusOf(thumbnail, clip) };\n}\n\n/** The top left corner's, taken for all four. A percentage is of the shorter side, as on a round avatar. */\nfunction radiusOf(element: Element, rect: Rect): number {\n  const radius = getComputedStyle(element).borderTopLeftRadius;\n  const value = parseFloat(radius) || 0;\n  return radius.endsWith('%') ? (value / 100) * Math.min(rect.width, rect.height) : value;\n}\n\nconst hiddenThumbnails = new WeakMap<HTMLElement, { count: number; visibility: string }>();\n\n/** Hides a thumbnail until every caller that hid it has shown it again: the open viewer and a flight overlap. */\nexport function hideThumbnail(thumbnail: HTMLElement): () => void {\n  const entry = hiddenThumbnails.get(thumbnail) ?? { count: 0, visibility: thumbnail.style.visibility };\n  hiddenThumbnails.set(thumbnail, entry);\n  entry.count += 1;\n  thumbnail.style.visibility = 'hidden';\n  return () => {\n    entry.count -= 1;\n    if (entry.count > 0) return;\n    hiddenThumbnails.delete(thumbnail);\n    thumbnail.style.visibility = entry.visibility;\n  };\n}\n\n/** On past the edge of the screen, in the direction a swipe was moving it. */\nexport function flungFrame(frame: Frame, direction: number): Frame {\n  const distance = Math.sign(direction) * window.innerHeight;\n  const moved = (rect: Rect) => ({ ...rect, top: rect.top + distance });\n  return { box: moved(frame.box), clip: moved(frame.clip), radius: frame.radius };\n}\n\n/**\n * For an element laid out at `layout` with `transform-origin: 0 0`. Every box has the picture's\n * shape, so one scale fits it, and the clip is in the element's own pixels, before that scale.\n */\nexport function flightKeyframes(from: Frame, to: Frame, layout: Rect): Keyframe[] {\n  return [from, to].map(({ box, clip, radius }) => {\n    const scale = box.width / layout.width;\n    const inset = [\n      clip.top - box.top,\n      box.left + box.width - clip.left - clip.width,\n      box.top + box.height - clip.top - clip.height,\n      clip.left - box.left,\n    ];\n    return {\n      transform: `translate(${box.left - layout.left}px, ${box.top - layout.top}px) scale(${scale})`,\n      clipPath: `inset(${inset.map((side) => `${side / scale}px`).join(' ')} round ${radius / scale}px)`,\n    };\n  });\n}\n",".lightbox {\n  position: fixed;\n  inset: 0;\n\n  /* Modal's value: shared by every overlay, so the order they opened in decides. */\n  z-index: 1;\n  display: flex;\n  flex-direction: column;\n\n  /* `--custom-lightbox-fade`: how far a swipe to close has gone, 0 to 1. */\n  background: color-mix(\n    in srgb,\n    var(--lightbox-background, var(--background)) calc(100% - var(--custom-lightbox-fade, 0) * 100%),\n    transparent\n  );\n  transition: background-color 0.18s ease-out;\n  color: var(--text-color);\n  font-family: var(--default-font);\n  animation: lightboxFadeIn 0.2s ease-out;\n\n  /* As long as CLOSE_MS in lightbox.tsx. `forwards`, so the last frame holds if that timer runs late. */\n  &-closing {\n    animation: lightboxFadeOut 0.25s ease-in forwards;\n  }\n\n  /* Focused only to catch the focus of a slide that left. */\n  outline: none;\n\n  &-dragging,\n  &-dragging &-toolbar,\n  &-dragging &-panZoom,\n  &-dragging &-glow,\n  &-dragging &-caption {\n    transition: none;\n  }\n\n  &-toolbar {\n    opacity: calc(1 - var(--custom-lightbox-fade, 0));\n    transition: opacity 0.18s ease-out;\n    display: flex;\n    flex: 0 0 auto;\n    align-items: center;\n    gap: var(--spacing-m);\n    padding: var(--spacing-s) var(--spacing-m);\n  }\n\n  /* Its auto margin pushes the buttons right, with or without a count in it. */\n  &-counter {\n    margin-inline-end: auto;\n    font-variant-numeric: tabular-nums;\n  }\n\n  &-unavailable {\n    opacity: 0.35;\n    pointer-events: none;\n  }\n\n  &-actions {\n    display: flex;\n    align-items: center;\n    gap: var(--spacing-xs, 4px);\n  }\n\n  /* Two classes, to outrank Carousel's whichever loads first. Clipped only sideways, so an image\n     swiped up passes over the toolbar. Scaled here, not per figure, which would replay on every\n     mount. `touch-action`, or a swipe from the caption scrolls and cancels the swipe to close. */\n  & &-carousel {\n    flex: 1 1 auto;\n    min-height: 0;\n    border-radius: 0;\n    overflow: clip visible;\n    touch-action: none;\n    animation: lightboxZoomIn 0.2s ease-out;\n  }\n\n  /* After the rule above, which it ties with. */\n  &-closing &-carousel {\n    animation: lightboxZoomOut 0.25s ease-in forwards;\n  }\n\n  /* So the flight lands where the image comes to rest. Not `animation: none`, which would replay the\n     scale as the flight ends: the normal duration, back OPEN_FLIGHT_MS later, finds it long over. */\n  &-flying:not(&-closing) &-carousel {\n    animation-duration: 0s;\n  }\n\n  &-arrow {\n    opacity: calc(1 - var(--custom-lightbox-fade, 0));\n  }\n\n  /* A tap clears the screen for the picture. Not `visibility`, which would drop the fade. */\n  &-clear &-toolbar,\n  &-clear &-arrow,\n  &-clear &-caption {\n    opacity: 0;\n    pointer-events: none;\n  }\n\n  /* Three classes, to outrank Carousel's two-class transition, which would trail the finger. */\n  &-dragging &-carousel &-arrow {\n    transition: none;\n  }\n\n  /* Carousel's row otherwise takes the tallest slide's height, and an image taller than the screen\n     pushes it past the bottom. */\n  &-track {\n    height: 100%;\n    grid-template-rows: 100%;\n  }\n\n  /* A grid, so the glow shares the PanZoom's cell and is fitted in it the way the image is. Isolated,\n     so the glow's negative z-index puts it behind the image rather than behind the backdrop. */\n  &-figure {\n    display: grid;\n    grid-template: minmax(0, 1fr) / minmax(0, 1fr);\n    gap: var(--spacing-s);\n    isolation: isolate;\n    box-sizing: border-box;\n    height: 100%;\n    margin: 0;\n    padding: 0 var(--spacing-m) var(--spacing-m);\n  }\n\n  /* Down to three quarters by the time a swipe to close has faded the backdrop out. */\n  &-panZoom,\n  &-glow {\n    grid-area: 1 / 1;\n    scale: calc(1 - var(--custom-lightbox-fade, 0) * 0.25);\n    transition: scale 0.18s ease-out;\n  }\n\n  /* The image's own size, capped by the cell as PanZoom caps the image, and a little over it so the\n     colours reach past its edges. Brightened, or a dark image throws a grey shadow on a light theme.\n     No `will-change`: on a layer of its own the compositor redoes the blur every frame, measured at 8 to\n     13 times its work during a swipe. */\n  &-glow {\n    z-index: -1;\n    place-self: center;\n    max-width: 100%;\n    max-height: 100%;\n    opacity: 0;\n    filter: blur(48px) saturate(2) brightness(1.2);\n    scale: calc(1.08 * (1 - var(--custom-lightbox-fade, 0) * 0.25));\n    transition:\n      scale 0.18s ease-out,\n      opacity 0.4s ease-out;\n    pointer-events: none;\n  }\n\n  &-glowLit {\n    opacity: 1;\n  }\n\n  /* Off while the copy in flight stands in for the image, and lit again once it has landed. */\n  &-flying &-glowLit {\n    opacity: 0;\n  }\n\n  /* The copy in flight stands in for it. */\n  &-flying &-panZoom {\n    visibility: hidden;\n  }\n\n  /* Over the dialog: the same z-index, later in the document. Placed and sized from lightbox.tsx. */\n  &-flyer {\n    position: fixed;\n    z-index: 1;\n    display: block;\n    margin: 0;\n    object-fit: cover;\n    transform-origin: 0 0;\n    pointer-events: none;\n  }\n\n  /* As large as the image will show: its own width, set by Image, capped by PanZoom like the image\n     is. Until that width is known, the whole viewport. */\n  &-thumbnail {\n    width: var(--custom-image-width, 100%);\n    height: 100%;\n    object-fit: contain;\n  }\n\n  /* Only once loading takes a moment, so a cached image never flashes a spinner. */\n  &-loader {\n    animation: lightboxLoaderAppear 0.2s 0.5s both;\n  }\n\n  &-error {\n    margin: 0;\n    color: var(--text-dark);\n    text-align: center;\n  }\n\n  &-caption {\n    flex: 0 0 auto;\n    opacity: calc(1 - var(--custom-lightbox-fade, 0));\n    transition: opacity 0.18s ease-out;\n    text-align: center;\n  }\n}\n\n@keyframes lightboxFadeIn {\n  from {\n    opacity: 0;\n  }\n}\n\n@keyframes lightboxLoaderAppear {\n  from {\n    opacity: 0;\n  }\n}\n\n@keyframes lightboxFadeOut {\n  to {\n    opacity: 0;\n  }\n}\n\n@keyframes lightboxZoomIn {\n  from {\n    transform: scale(0.95);\n  }\n}\n\n@keyframes lightboxZoomOut {\n  to {\n    transform: scale(0.95);\n  }\n}\n\n/* `!important` against the spring back useSwipeDismiss sets inline. Fades are not motion and stay. */\n@media (prefers-reduced-motion: reduce) {\n  .lightbox .lightbox-carousel,\n  .lightbox-closing .lightbox-carousel {\n    animation: none;\n  }\n\n  .lightbox-figure {\n    transition: none !important;\n  }\n\n  .lightbox-panZoom {\n    transition: none;\n  }\n\n  .lightbox-glow {\n    transition: opacity 0.4s ease-out;\n  }\n}\n","import {\n  useCallback,\n  useContext,\n  useEffect,\n  useLayoutEffect,\n  useRef,\n  useState,\n  type CSSProperties,\n  type KeyboardEvent as ReactKeyboardEvent,\n  type PointerEvent as ReactPointerEvent,\n  type Ref,\n  type RefCallback,\n  type RefObject,\n} from 'react';\nimport cx from 'classnames';\nimport { faMagnifyingGlassMinus, faMagnifyingGlassPlus, faXmark } from '@fortawesome/free-solid-svg-icons';\n\nimport ActionIcon from '../actionIcon';\nimport BodyEnd from '../bodyEnd';\nimport Carousel from '../carousel';\nimport Image from '../image';\nimport Loader from '../loader';\nimport { CarouselSlideContext } from '../carousel/carouselSlideContext';\nimport PanZoom from '../panZoom';\nimport type { PanZoomHandle } from '../panZoom/panZoom.types';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport { useOverlay } from '../hooks/useOverlay';\nimport { useSwipeDismiss } from '../hooks/useSwipeDismiss';\nimport type { LightboxImage, LightboxLabels, LightboxProps, LightboxSlots } from './lightbox.types';\nimport { flightKeyframes, flungFrame, hideThumbnail, pictureFrame, thumbnailFrame } from './lightbox.flight';\nimport type { SlotClassNames, SlotStyles } from '../types/slots.types';\nimport styles from './lightbox.module.pcss';\n\nconst DEFAULT_LABELS: Required<LightboxLabels> = {\n  dialog: 'Image viewer',\n  close: 'Close',\n  previous: 'Previous image',\n  next: 'Next image',\n  zoomIn: 'Zoom in',\n  zoomOut: 'Zoom out',\n  loadError: 'This image could not be loaded.',\n  counter: (position, count) => `${position} / ${count}`,\n};\n\n/** The closing animation's length in lightbox.module.pcss, and the flight back into a thumbnail. */\nconst CLOSE_MS = 250;\nconst OPEN_FLIGHT_MS = 300;\nconst FLIGHT_EASING = 'cubic-bezier(0.2, 0, 0, 1)';\n\n/** Controls that use the left and right arrow keys themselves. */\nconst OWNS_ARROW_KEYS = 'input, textarea, select, [contenteditable=\"\"], [contenteditable=\"true\"], [role=\"slider\"]';\n\nfunction Lightbox(props: LightboxProps) {\n  const { isOpen = false, ...rest } = useComponentDefaults('Lightbox', props);\n  const [isRendered, setRendered] = useState(isOpen);\n\n  // The dialog's key, so a reopening mid-close mounts fresh too: on `defaultIndex`, without a swipe's offset.\n  const [opening, setOpening] = useState(0);\n  const [wasOpen, setWasOpen] = useState(isOpen);\n  if (isOpen !== wasOpen) {\n    setWasOpen(isOpen);\n    if (isOpen) setOpening(opening + 1);\n  }\n\n  useEffect(() => {\n    if (isOpen) {\n      setRendered(true);\n      return;\n    }\n    const id = setTimeout(() => setRendered(false), CLOSE_MS);\n    return () => clearTimeout(id);\n  }, [isOpen]);\n\n  if (!(isOpen || isRendered) || rest.images.length === 0) return null;\n  return <LightboxDialog key={opening} {...rest} isClosing={!isOpen} />;\n}\n\nfunction LightboxDialog({\n  images,\n  onClose = () => {},\n  currentIndex,\n  defaultIndex = 0,\n  onChange,\n  actions,\n  getThumbnail,\n  glow = false,\n  className,\n  style,\n  classNames,\n  styles: slotStyles,\n  labels,\n  onKeyDown,\n  isClosing,\n  ...rest\n}: Omit<LightboxProps, 'isOpen'> & { isClosing: boolean }) {\n  const text = useLabels('Lightbox', labels, DEFAULT_LABELS);\n  const dialogRef = useRef<HTMLDivElement>(null);\n  const count = images.length;\n  const [internalIndex, setInternalIndex] = useState(defaultIndex);\n  const index = wrapIndex(currentIndex ?? internalIndex, count);\n  const panZoomRef = useRef<PanZoomHandle>(null);\n  // With its index: the next image always comes in fitted.\n  const [zoomedIndex, setZoomedIndex] = useState<number | null>(null);\n  const isZoomed = zoomedIndex === index;\n  // By source: a neighbour loads before it is on show.\n  const [loadedSources, setLoadedSources] = useState<ReadonlySet<string>>(() => new Set());\n  const canZoom = loadedSources.has(images[index].src);\n  const [isClear, setClear] = useState(false);\n\n  // Released at the start of closing, not after the animation.\n  useOverlay({ isActive: !isClosing, containerRef: dialogRef, onEscape: onClose });\n\n  // The backdrop and the controls have faded out by twice the distance that closes it.\n  const swipeThreshold = Math.round(window.innerHeight / 5);\n  const swipeOffset = useRef(0);\n  // The image stays opaque and where it was let go: it shrinks instead, and the flight takes it from there.\n  const { ref: swipeRef, isDragging } = useSwipeDismiss<HTMLElement>({\n    onDismiss: onClose,\n    axis: 'y',\n    distanceThreshold: swipeThreshold,\n    mobileOnly: false,\n    fade: false,\n    animateExit: false,\n    disabled: isClosing,\n    onDrag: (offset) => {\n      swipeOffset.current = offset;\n      dialogRef.current?.style.setProperty(\n        '--custom-lightbox-fade',\n        String(Math.min(1, Math.abs(offset) / (swipeThreshold * 2))),\n      );\n    },\n  });\n\n  const shownFigure = useRef<HTMLElement | null>(null);\n  const figureRef = useCallback(\n    (figure: HTMLElement | null) => {\n      swipeRef(figure);\n      shownFigure.current = figure;\n    },\n    [swipeRef],\n  );\n  const { flyerRef, isFlying } = useThumbnailFlight({\n    isClosing,\n    thumbnail: () => getThumbnail?.(images[index], index),\n    figureRef: shownFigure,\n    swipeOffset,\n  });\n\n  // Lifted out of the grid while on show, so it never shows twice: behind a backdrop a swipe fades.\n  useLayoutEffect(() => {\n    const thumbnail = isClosing ? null : getThumbnail?.(images[index], index);\n    return thumbnail ? hideThumbnail(thumbnail) : undefined;\n  }, [isClosing, getThumbnail, images, index]);\n\n  // A slide that left is `inert`, which drops its focus to the body, out of the arrow keys' reach.\n  // Chromium has dropped it before this runs, hence the body check.\n  useLayoutEffect(() => {\n    const active = document.activeElement;\n    if (!active || active === document.body || active.closest('[inert]')) dialogRef.current?.focus();\n  }, [index]);\n\n  function goTo(next: number) {\n    const wrapped = wrapIndex(next, count);\n    setInternalIndex(wrapped);\n    onChange?.(wrapped);\n  }\n\n  // Carousel answers the arrow keys on its own buttons only.\n  function handleKeyDown(event: ReactKeyboardEvent<HTMLDivElement>) {\n    onKeyDown?.(event);\n    // A key means a keyboard, which needs the controls it moves to.\n    setClear(false);\n    if (event.defaultPrevented || count < 2) return;\n    // Alt + left is the browser's Back, and a field in a caption needs its own arrows.\n    if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) return;\n    if ((event.target as Element).closest(OWNS_ARROW_KEYS)) return;\n    if (event.key === 'ArrowLeft') goTo(index - 1);\n    else if (event.key === 'ArrowRight') goTo(index + 1);\n    else return;\n    event.preventDefault();\n  }\n\n  const isNamed = rest['aria-label'] !== undefined || rest['aria-labelledby'] !== undefined;\n\n  return (\n    <BodyEnd>\n      <div\n        {...rest}\n        ref={dialogRef}\n        role='dialog'\n        aria-modal='true'\n        aria-label={isNamed ? rest['aria-label'] : text.dialog}\n        tabIndex={-1}\n        inert={isClosing || undefined}\n        className={cx(\n          styles.lightbox,\n          isClosing && styles.lightboxClosing,\n          isDragging && styles.lightboxDragging,\n          isFlying && styles.lightboxFlying,\n          isClear && styles.lightboxClear,\n          className,\n          classNames?.root,\n        )}\n        style={{ ...style, ...slotStyles?.root }}\n        onKeyDown={handleKeyDown}\n      >\n        <div className={cx(styles.lightboxToolbar, classNames?.toolbar)} style={slotStyles?.toolbar}>\n          <span className={cx(styles.lightboxCounter, classNames?.counter)} style={slotStyles?.counter}>\n            {count > 1 ? text.counter(index + 1, count) : null}\n          </span>\n          {actions && (\n            <div className={cx(styles.lightboxActions, classNames?.actions)} style={slotStyles?.actions}>\n              {actions(images[index], index)}\n            </div>\n          )}\n          <ActionIcon\n            icon={isZoomed ? faMagnifyingGlassMinus : faMagnifyingGlassPlus}\n            title={isZoomed ? text.zoomOut : text.zoomIn}\n            aria-label={isZoomed ? text.zoomOut : text.zoomIn}\n            aria-disabled={!canZoom || undefined}\n            onClick={() => {\n              if (canZoom) panZoomRef.current?.toggle();\n            }}\n            className={cx(!canZoom && styles.lightboxUnavailable, classNames?.zoomButton)}\n            style={slotStyles?.zoomButton}\n          />\n          <ActionIcon\n            icon={faXmark}\n            title={text.close}\n            aria-label={text.close}\n            onClick={onClose}\n            className={classNames?.closeButton}\n            style={slotStyles?.closeButton}\n          />\n        </div>\n\n        {/* Every prop set, or the app's Carousel defaults would reach it: autoplay, no arrows. */}\n        <Carousel\n          className={styles.lightboxCarousel}\n          classNames={{\n            track: styles.lightboxTrack,\n            prevButton: styles.lightboxArrow,\n            nextButton: styles.lightboxArrow,\n          }}\n          currentIndex={index}\n          onChange={goTo}\n          loop\n          autoPlay={false}\n          showArrows\n          showDots={false}\n          swipeable\n          lazyRender\n          adaptiveHeight={false}\n          labels={{ previous: text.previous, next: text.next }}\n        >\n          {images.map((image, i) => (\n            <LightboxSlide\n              // With the source, so a replaced image starts loading afresh.\n              key={`${i}:${image.src}`}\n              image={image}\n              loadError={text.loadError}\n              glow={glow}\n              isCurrent={i === index}\n              hasLoaded={loadedSources.has(image.src)}\n              onLoad={() =>\n                setLoadedSources((current) => (current.has(image.src) ? current : new Set(current).add(image.src)))\n              }\n              panZoomRef={panZoomRef}\n              figureRef={figureRef}\n              onScaleChange={(scale) => setZoomedIndex(scale > 1 ? index : null)}\n              // A mouse has the controls in reach and no screen to clear: a click would only lose them.\n              onTap={(event) => {\n                if (event.pointerType !== 'mouse') setClear((clear) => !clear);\n              }}\n              classNames={classNames}\n              styles={slotStyles}\n            />\n          ))}\n        </Carousel>\n      </div>\n      {/* Outside the dialog, whose fades would take it along. */}\n      {isFlying && <img ref={flyerRef} className={styles.lightboxFlyer} alt='' aria-hidden='true' />}\n    </BodyEnd>\n  );\n}\n\ninterface Flight {\n  direction: 'in' | 'out';\n  thumbnail: HTMLElement | null;\n  /** How far a swipe to close had moved the image, which flies on that way with no thumbnail to land in. */\n  swipe: number;\n}\n\ninterface ThumbnailFlightArgs {\n  isClosing: boolean;\n  /** The thumbnail of the image on show. */\n  thumbnail: () => HTMLElement | null | undefined;\n  figureRef: RefObject<HTMLElement | null>;\n  swipeOffset: RefObject<number>;\n}\n\n/**\n * Flies the image out of its thumbnail as the viewer opens, and into the thumbnail of the image on\n * show as it closes. A copy flies: the Carousel and the PanZoom both clip the image itself.\n */\nfunction useThumbnailFlight({ isClosing, thumbnail, figureRef, swipeOffset }: ThumbnailFlightArgs) {\n  const [flight, setFlight] = useState<Flight | null>(null);\n  const flyerRef = useRef<HTMLImageElement>(null);\n\n  useLayoutEffect(() => {\n    const element = thumbnail() ?? null;\n    const swipe = isClosing ? swipeOffset.current : 0;\n    if (!(element || swipe) || window.matchMedia?.('(prefers-reduced-motion: reduce)').matches) return;\n    setFlight({ direction: isClosing ? 'out' : 'in', thumbnail: element, swipe });\n    // As it opens and as it starts closing, with the image on show then.\n    // oxlint-disable-next-line react-hooks/exhaustive-deps\n  }, [isClosing]);\n\n  // A commit after the one above: on opening, the first in which the Image has put up its picture.\n  useLayoutEffect(() => {\n    const flyer = flyerRef.current;\n    const figure = figureRef.current;\n    if (!flight || !flyer || !figure) return;\n    const picture = pictureFrame(figure);\n    const home = picture && flight.thumbnail ? thumbnailFrame(flight.thumbnail, picture.aspect) : null;\n    const away = home ?? (picture && flight.swipe ? flungFrame(picture.frame, flight.swipe) : null);\n    const [from, to] = flight.direction === 'in' ? [home, picture?.frame] : [picture?.frame, away];\n    if (!picture || !from || !to) {\n      setFlight(null);\n      return;\n    }\n\n    // At the larger end, so it is scaled down rather than blurred up. Inline, against an app's\n    // `img { max-width: 100% }`, which wins over a layered stylesheet: a zoomed-in image is wider.\n    const layout = from.box.width > to.box.width ? from.box : to.box;\n    Object.assign(flyer.style, {\n      left: `${layout.left}px`,\n      top: `${layout.top}px`,\n      width: `${layout.width}px`,\n      height: `${layout.height}px`,\n      maxWidth: 'none',\n      maxHeight: 'none',\n    });\n    flyer.src = picture.src;\n    const show = home && flight.thumbnail ? hideThumbnail(flight.thumbnail) : undefined;\n\n    const animation = flyer.animate(flightKeyframes(from, to, layout), {\n      duration: flight.direction === 'in' ? OPEN_FLIGHT_MS : CLOSE_MS,\n      easing: FLIGHT_EASING,\n      fill: 'both',\n    });\n    animation.finished.then(\n      () => setFlight(null),\n      () => {},\n    );\n    return () => {\n      animation.cancel();\n      show?.();\n    };\n  }, [flight, figureRef]);\n\n  return { flyerRef, isFlying: flight !== null };\n}\n\ninterface LightboxSlideProps {\n  image: LightboxImage;\n  loadError: string;\n  glow: boolean;\n  /** For every instance of the image on show, its looping copy included, unlike the slide context. */\n  isCurrent: boolean;\n  hasLoaded: boolean;\n  onLoad: () => void;\n  panZoomRef: Ref<PanZoomHandle>;\n  figureRef: RefCallback<HTMLElement>;\n  onScaleChange: (scale: number) => void;\n  onTap: (event: ReactPointerEvent<HTMLDivElement>) => void;\n  classNames?: SlotClassNames<LightboxSlots>;\n  styles?: SlotStyles<LightboxSlots>;\n}\n\n/** Only the instance on show takes the refs: a looping Carousel also renders inert copies. */\nfunction LightboxSlide({\n  image,\n  loadError,\n  glow,\n  isCurrent,\n  hasLoaded,\n  onLoad,\n  panZoomRef,\n  figureRef,\n  onScaleChange,\n  onTap,\n  classNames,\n  styles: slotStyles,\n}: LightboxSlideProps) {\n  const isOnShow = useContext(CarouselSlideContext);\n  const [hasFailed, setFailed] = useState(false);\n\n  return (\n    // What a swipe to close moves, the caption with it.\n    <figure\n      ref={isOnShow ? figureRef : undefined}\n      className={cx(styles.lightboxFigure, classNames?.figure)}\n      style={slotStyles?.figure}\n    >\n      {/* Disabled off screen, which zooms an image back out as it leaves. */}\n      <PanZoom\n        ref={isOnShow ? panZoomRef : undefined}\n        className={styles.lightboxPanZoom}\n        scrollToZoom\n        showControls={false}\n        disabled={!isOnShow || hasFailed}\n        onScaleChange={isOnShow ? onScaleChange : undefined}\n        onTap={isOnShow ? onTap : undefined}\n      >\n        {/* A spinner: a skeleton would need the image's shape, unknown until it loads. */}\n        <Image\n          src={image.src}\n          alt={image.alt}\n          thumbnail={image.thumbnail}\n          placeholder={<Loader className={styles.lightboxLoader} />}\n          onLoad={onLoad}\n          onError={() => setFailed(true)}\n          fallback={<p className={styles.lightboxError}>{loadError}</p>}\n          className={classNames?.image}\n          style={slotStyles?.image}\n          classNames={{ placeholder: styles.lightboxThumbnail }}\n          // Not as `width` and `height`, which would become the image's attributes and size it.\n          styles={image.width && image.height ? { placeholder: knownSize(image.width, image.height) } : undefined}\n        />\n      </PanZoom>\n      {/* After the PanZoom, which the flight finds first in the figure. Lit once the image is in, and only\n          for the image on show: it reaches past its slide, and a neighbour's would shine in at the edge of\n          the screen. Its looping copy too, which is what a wrap from the last image to the first slides in. */}\n      {glow && (\n        <img\n          src={image.src}\n          alt=''\n          aria-hidden='true'\n          className={cx(styles.lightboxGlow, hasLoaded && isCurrent && styles.lightboxGlowLit, classNames?.glow)}\n          style={slotStyles?.glow}\n        />\n      )}\n      {image.caption != null && (\n        <figcaption className={cx(styles.lightboxCaption, classNames?.caption)} style={slotStyles?.caption}>\n          {image.caption}\n        </figcaption>\n      )}\n    </figure>\n  );\n}\n\nfunction knownSize(width: number, height: number): CSSProperties {\n  return { '--custom-image-width': `${width}px`, '--custom-image-ratio': `${width} / ${height}` } as CSSProperties;\n}\n\nfunction wrapIndex(index: number, count: number): number {\n  return ((index % count) + count) % count;\n}\n\nexport default Lightbox;\n","/* position: relative so the trigger's ::after can stretch over the whole row, and isolation so\n   its z-index stays inside the row instead of rising over a sticky header of the page. */\n.listItem {\n  position: relative;\n  isolation: isolate;\n  display: flex;\n  align-items: center;\n  gap: var(--spacing-m, 8px);\n  box-sizing: border-box;\n  width: 100%;\n  min-height: var(--list-item-min-height, 48px);\n  padding: var(--spacing-m, 8px);\n\n  &-icon {\n    /* FontAwesome's own rule is unlayered and outranks `width` from inside the library's layer. */\n    --fa-width: 1.25em;\n\n    flex-shrink: 0;\n  }\n\n  /* min-width: 0 lets the labels shrink, so a long title ellipses instead of pushing\n     endContent and the chevron out of the row. */\n  &-labels {\n    display: flex;\n    flex: 1 1 auto;\n    flex-direction: column;\n    gap: 2px;\n    min-width: 0;\n  }\n\n  &-title {\n    display: block;\n    overflow: hidden;\n    text-overflow: ellipsis;\n    white-space: nowrap;\n  }\n\n  /* The <a> or <button> around the title. */\n  &-trigger {\n    display: block;\n    margin: 0;\n    padding: 0;\n    border: none;\n    background: none;\n    color: inherit;\n    font: inherit;\n    text-align: start;\n    text-decoration: none;\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n\n    /* Over the whole row. It belongs to the link, so a middle click and a long press anywhere on\n       the row act on the link too. */\n\n    /* z-index above content that positions itself, a Chip among many, which would otherwise\n       paint over it in tree order and swallow the click. */\n    &::after {\n      content: '';\n      position: absolute;\n      inset: 0;\n      z-index: 1;\n      transition: background-color 0.15s;\n    }\n\n    /* A tint of the text colour, so it shows on any surface the row sits on. (hover: hover)\n       because on touch a tap latches :hover until the next tap elsewhere. */\n    @media (hover: hover) {\n      &:hover::after {\n        background-color: color-mix(in srgb, currentcolor 6%, transparent);\n      }\n    }\n\n    &:active::after {\n      background-color: color-mix(in srgb, currentcolor 12%, transparent);\n    }\n\n    /* On the stretched area, to ring the row rather than the title. Inside the edge: a row\n       usually fills a card that clips its overflow. */\n    &:focus-visible {\n      outline: none;\n    }\n\n    &:focus-visible::after {\n      outline: 2px solid var(--focus-border-color, var(--primary-color));\n      outline-offset: -2px;\n    }\n  }\n\n  /* Under the trigger's hover tint, which still shows on top of it. */\n  &-selected {\n    background-color: var(--list-item-selected-background, color-mix(in srgb, var(--primary-color) 12%, transparent));\n  }\n\n  /* One line with an ellipsis, like the title. Two clamped lines cut a word too long for the line\n     off without one (an email in a narrow row), and gave the rows of a list uneven heights. */\n  &-subtitle {\n    overflow: hidden;\n    color: var(--text-dark);\n    font-size: 0.85em;\n    text-overflow: ellipsis;\n    white-space: nowrap;\n  }\n\n  &-endContent {\n    display: flex;\n    flex-shrink: 0;\n    align-items: center;\n    gap: var(--spacing-s, 4px);\n  }\n\n  &-chevron {\n    flex-shrink: 0;\n    color: var(--text-dark);\n    font-size: 0.85em;\n  }\n}\n\n/* Controls elsewhere in the row sit above the stretched area, so they stay clickable while the\n   text around them still opens the row. Any `a`, not `a[href]`: a disabled link Button drops its\n   href, and a click on it must not open the row. All in :where(), for no specificity at all: a\n   control's own position wins, so an input it positions absolutely stays absolute. */\n:where(.listItem-interactive)\n  :where(a, button, input, select, textarea, label, [tabindex]):where(:not(.listItem-trigger)) {\n  position: relative;\n  z-index: 2;\n}\n","import { useId, type ReactElement, type ReactNode } from 'react';\nimport cx from 'classnames';\nimport { faChevronRight } from '@fortawesome/free-solid-svg-icons';\n\nimport type { ListItemProps } from './listItem.types';\nimport styles from './listItem.module.pcss';\nimport { renderIcon } from '../utils/renderIcon';\nimport { useLinkClick } from '../utils/navigation';\n\n// `subtitle={isOverdue && 'Overdue'}` passes false, which renders nothing and needs no box.\nconst isShown = (node: ReactNode) => node != null && typeof node !== 'boolean' && node !== '';\n\nfunction ListItem({\n  icon,\n  title,\n  subtitle,\n  endContent,\n  href,\n  target,\n  rel,\n  download,\n  onClick,\n  selected = false,\n  chevron = href !== undefined,\n  className,\n  style,\n  classNames,\n  styles: slotStyles,\n  ...rest\n}: ListItemProps): ReactElement {\n  const followLink = useLinkClick();\n  const id = useId();\n  const hasSubtitle = isShown(subtitle);\n  const hasEndContent = isShown(endContent);\n  const isInteractive = href !== undefined || onClick !== undefined;\n  // A status in endContent (\"Open\") is part of what the row says, so the link is described by it\n  // too, where it is text: a control there would only read its own name a second time.\n  const describedBy = [hasSubtitle && `${id}-subtitle`, typeof endContent === 'string' && `${id}-end`]\n    .filter(Boolean)\n    .join(' ');\n\n  const titleElement = (\n    <span className={cx(styles.listItemTitle, classNames?.title)} style={slotStyles?.title}>\n      {title}\n    </span>\n  );\n  // Only the title is the link or button: the rest of the row can then hold controls of its own,\n  // which an <a> or <button> may not contain. Its ::after stretches over the whole row instead.\n  const triggerProps = {\n    className: cx(styles.listItemTrigger, classNames?.trigger),\n    style: slotStyles?.trigger,\n    'aria-describedby': describedBy || undefined,\n  };\n  const trigger =\n    href !== undefined ? (\n      <a\n        {...triggerProps}\n        href={href}\n        target={target}\n        rel={rel}\n        download={download}\n        aria-current={selected ? 'page' : undefined}\n        onClick={(event) => {\n          onClick?.(event);\n          followLink(event);\n        }}\n      >\n        {titleElement}\n      </a>\n    ) : onClick ? (\n      <button {...triggerProps} type='button' aria-pressed={selected || undefined} onClick={onClick}>\n        {titleElement}\n      </button>\n    ) : (\n      titleElement\n    );\n\n  return (\n    <div\n      {...rest}\n      className={cx(\n        styles.listItem,\n        { [styles.listItemInteractive]: isInteractive, [styles.listItemSelected]: selected },\n        className,\n        classNames?.root,\n      )}\n      style={{ ...style, ...slotStyles?.root }}\n    >\n      {icon != null &&\n        renderIcon(icon, { className: cx(styles.listItemIcon, classNames?.icon), style: slotStyles?.icon })}\n      <div className={cx(styles.listItemLabels, classNames?.labels)} style={slotStyles?.labels}>\n        {trigger}\n        {hasSubtitle && (\n          <div\n            id={`${id}-subtitle`}\n            className={cx(styles.listItemSubtitle, classNames?.subtitle)}\n            style={slotStyles?.subtitle}\n          >\n            {subtitle}\n          </div>\n        )}\n      </div>\n      {hasEndContent && (\n        <div\n          id={`${id}-end`}\n          className={cx(styles.listItemEndContent, classNames?.endContent)}\n          style={slotStyles?.endContent}\n        >\n          {endContent}\n        </div>\n      )}\n      {chevron &&\n        renderIcon(faChevronRight, {\n          className: cx(styles.listItemChevron, classNames?.chevron),\n          style: slotStyles?.chevron,\n        })}\n    </div>\n  );\n}\n\nexport default ListItem;\n",".loadingOverlay {\n  position: relative;\n\n  &-content {\n    /* Its own stacking context, so nothing inside with a z-index (a sticky header) paints over the backdrop. */\n    isolation: isolate;\n    height: 100%;\n  }\n\n  &-backdrop {\n    position: absolute;\n    inset: 0;\n    display: grid;\n    place-items: center;\n    padding: var(--spacing-m, 12px);\n    border-radius: inherit;\n    background: var(--loading-overlay-background, color-mix(in srgb, var(--background) 50%, transparent));\n    -webkit-backdrop-filter: blur(var(--loading-overlay-blur, 4px));\n    backdrop-filter: blur(var(--loading-overlay-blur, 4px));\n    cursor: progress;\n    animation: loadingOverlayFade 0.2s ease-out;\n\n    /* A transition rather than keyframes, so loading again mid-fade turns it back from where it is\n       instead of restarting from 0. 0.2s is FADE_OUT_MS in loadingOverlay.tsx, which unmounts it. */\n    transition: opacity 0.2s ease-in;\n\n    &-leaving {\n      opacity: 0;\n    }\n  }\n\n  &-panel {\n    /* Sticky, so on content taller than the screen it stays in view rather than at the middle. */\n    position: sticky;\n    top: var(--spacing-l, 24px);\n    bottom: var(--spacing-l, 24px);\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    gap: 0.75em;\n    animation: loadingOverlayRise 0.4s cubic-bezier(0.2, 0.9, 0.3, 1.15) both;\n  }\n\n  &-message {\n    max-width: 32ch;\n    font-size: var(--default-font-size);\n    font-weight: 500;\n    text-align: center;\n    color: transparent;\n    background: linear-gradient(100deg, var(--text-dark) 40%, var(--text-color) 50%, var(--text-dark) 60%) 0 0 / 300%\n      100%;\n    -webkit-background-clip: text;\n    background-clip: text;\n    animation: loadingOverlayShimmer 2.4s linear infinite;\n  }\n}\n\n@keyframes loadingOverlayFade {\n  from {\n    opacity: 0;\n  }\n}\n\n@keyframes loadingOverlayRise {\n  from {\n    opacity: 0;\n    transform: translateY(8px) scale(0.94);\n  }\n}\n\n@keyframes loadingOverlayShimmer {\n  from {\n    background-position: 100% 0;\n  }\n\n  to {\n    background-position: 0 0;\n  }\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .loadingOverlay-panel {\n    animation-name: loadingOverlayFade;\n  }\n\n  .loadingOverlay-message {\n    color: var(--text-color);\n    background: none;\n    animation: none;\n  }\n}\n","import { useEffect, useId, useRef, useState, type ReactElement } from 'react';\nimport cx from 'classnames';\n\nimport type { LoadingOverlayProps } from './loadingOverlay.types';\nimport styles from './loadingOverlay.module.pcss';\nimport Loader from '../loader';\nimport { useComponentDefaults } from '../configProvider';\n\nconst MIN_VISIBLE_MS = 400;\n/** The backdrop's opacity transition in loadingOverlay.module.pcss. */\nconst FADE_OUT_MS = 200;\n\ntype Phase = 'hidden' | 'shown' | 'leaving';\n\n/** Holds MIN_VISIBLE_MS once shown: a load ending just after the overlay appeared would flash it. */\nfunction usePhase(isLoading: boolean, delay: number): Phase {\n  const [phase, setPhase] = useState<Phase>(() => (isLoading && delay <= 0 ? 'shown' : 'hidden'));\n  const shownAt = useRef<number | null>(null);\n\n  useEffect(() => {\n    let next: Phase;\n    let wait: number;\n    if (isLoading) {\n      if (phase === 'shown') {\n        // Shown on the first render, so no timer stamped it.\n        shownAt.current ??= Date.now();\n        return;\n      }\n      next = 'shown';\n      // Still on screen while it fades, so it comes straight back rather than waiting again.\n      wait = phase === 'leaving' ? 0 : delay;\n    } else if (phase === 'shown') {\n      next = 'leaving';\n      wait = Math.max(0, (shownAt.current ?? 0) + MIN_VISIBLE_MS - Date.now());\n    } else if (phase === 'leaving') {\n      next = 'hidden';\n      wait = FADE_OUT_MS;\n    } else {\n      return;\n    }\n    const id = setTimeout(() => {\n      if (next === 'shown') shownAt.current = Date.now();\n      setPhase(next);\n    }, wait);\n    return () => clearTimeout(id);\n  }, [isLoading, phase, delay]);\n\n  return phase;\n}\n\nfunction LoadingOverlay(props: LoadingOverlayProps): ReactElement {\n  const {\n    isLoading = false,\n    children,\n    loader,\n    message,\n    delay = 200,\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    ...rest\n  } = useComponentDefaults('LoadingOverlay', props);\n  const phase = usePhase(isLoading, delay);\n  const messageId = useId();\n  const hasMessage = message != null && typeof message !== 'boolean' && message !== '';\n\n  return (\n    <div {...rest} className={cx(styles.loadingOverlay, className)} style={style}>\n      <div\n        aria-busy={isLoading || undefined}\n        className={cx(styles.loadingOverlayContent, classNames?.content)}\n        style={slotStyles?.content}\n      >\n        {children}\n      </div>\n      {phase !== 'hidden' && (\n        <div\n          className={cx(\n            styles.loadingOverlayBackdrop,\n            phase === 'leaving' && styles.loadingOverlayBackdropLeaving,\n            classNames?.backdrop,\n          )}\n          style={slotStyles?.backdrop}\n        >\n          <div className={cx(styles.loadingOverlayPanel, classNames?.panel)} style={slotStyles?.panel}>\n            {loader ?? <Loader aria-labelledby={hasMessage ? messageId : undefined} />}\n            {hasMessage && (\n              <div\n                id={messageId}\n                className={cx(styles.loadingOverlayMessage, classNames?.message)}\n                style={slotStyles?.message}\n              >\n                {message}\n              </div>\n            )}\n          </div>\n        </div>\n      )}\n    </div>\n  );\n}\n\nexport default LoadingOverlay;\n",".menuPanel {\n  padding: 0;\n}\n\n.menu {\n  display: flex;\n  flex-direction: column;\n  min-width: 180px;\n  padding: 4px 0;\n  box-sizing: border-box;\n  font-family: var(--default-font);\n  font-size: var(--default-font-size);\n  color: var(--text-color);\n\n  /* Chrome (background, shadow, radius, positioning) comes from the Popover panel\n     this renders inside — keep only the list itself here. */\n\n  &-trigger {\n    /* The trigger is only here to give Escape somewhere to return the focus to;\n       it must not become a box of its own in the consumer's layout. */\n    display: contents;\n  }\n\n  &-item {\n    display: flex;\n    align-items: center;\n    gap: 10px;\n    width: 100%;\n    padding: 8px 12px;\n    border: none;\n    background: none;\n    font: inherit;\n    color: inherit;\n    text-align: left;\n    white-space: nowrap;\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n    box-sizing: border-box;\n    transition: background 0.1s;\n\n    /* An entry with an `href` is an <a>. */\n    text-decoration: none;\n\n    /* `:focus`, not `:focus-visible`: the entry with the focus is the one the\n       arrow keys are on, and it has to look like it whether the menu was opened\n       by keyboard or by mouse. `:focus-visible` would stay dark after a click on\n       the trigger, because the browser does not count that as keyboard input.\n\n       Not `--background-accent-light`: the panel under this list is\n       `--background-modal`, and the default theme gives both the same colour, so\n       the highlight was invisible. Dropdown and MultiSelect pick this one too. */\n    &:hover:not(:disabled),\n    &:focus {\n      background: var(--background-accent);\n    }\n\n    /* The ring would sit on top of the neighbouring rows' backgrounds; the row\n       highlight above is what marks the active entry. */\n    &:focus {\n      outline: none;\n    }\n\n    &:disabled {\n      opacity: 0.45;\n      cursor: default;\n    }\n\n    &-danger {\n      color: var(--error-color);\n    }\n\n    &-icon {\n      /* FontAwesome's own rule is unlayered, so it outranks `width` from inside the library's layer and\n         reads this instead. `width` still sizes a custom icon. */\n      --fa-width: 1em;\n\n      flex: none;\n      width: 1em;\n      text-align: center;\n    }\n\n    &-label {\n      flex: 1;\n      overflow: hidden;\n      text-overflow: ellipsis;\n    }\n\n    &-shortcut {\n      flex: none;\n      margin-left: 16px;\n      font-size: 0.85em;\n      color: var(--text-light);\n    }\n  }\n\n  &-separator {\n    height: 1px;\n    margin: 4px 0;\n    background: var(--border-color);\n  }\n\n  &-heading {\n    padding: 6px 12px 2px;\n    font-size: 0.8em;\n    font-weight: 600;\n    text-transform: uppercase;\n    letter-spacing: 0.04em;\n    color: var(--text-light);\n  }\n}\n","import { cloneElement, isValidElement, useEffect, useId, useRef, useState, type ReactElement } from 'react';\nimport cx from 'classnames';\n\nimport styles from './menu.module.pcss';\nimport Popover from '../popover';\nimport { PopoverAlign, PopoverPlacement } from '../popover/popover.types';\nimport { isSelfActivation } from '../utils/activationKey';\nimport { useLinkClick } from '../utils/navigation';\nimport { useFocusBoundary } from '../hooks/useFocusBoundary';\nimport { ariaKeyshortcuts, formatHotkey } from '../hooks/useHotkeys';\nimport { isMenuAction, isMenuHeading, isMenuSeparator, type MenuEntry, type MenuProps } from './menu.types';\nimport { hasAnyIcon, renderItemIcon } from './menuItem';\n\n/** How long a typed run stays one search before it starts over. */\nconst TYPEAHEAD_RESET_MS = 600;\n\n/** Index of the next action that can be focused, wrapping, or -1 if there is none. */\nfunction nextEnabled(items: MenuEntry[], from: number, step: 1 | -1): number {\n  for (let i = 1; i <= items.length; i++) {\n    // The `items.length * i` term only keeps the value positive for a backwards\n    // step; JavaScript's `%` would otherwise return a negative index.\n    const index = (from + step * i + items.length * i) % items.length;\n    const entry = items[index];\n    if (isMenuAction(entry) && !entry.disabled) return index;\n  }\n  return -1;\n}\n\nfunction firstEnabled(items: MenuEntry[]): number {\n  return items.findIndex((entry) => isMenuAction(entry) && !entry.disabled);\n}\n\nfunction lastEnabled(items: MenuEntry[]): number {\n  for (let i = items.length - 1; i >= 0; i--) {\n    const entry = items[i];\n    if (isMenuAction(entry) && !entry.disabled) return i;\n  }\n  return -1;\n}\n\n/**\n * A menu of actions on a trigger of your choosing.\n *\n * `Popover` does the anchoring, portalling and outside-click work but takes\n * arbitrary `content`, which leaves every consumer to write their own `ul`/`li`\n * and, in practice, to leave out the roles and the arrow keys. This is that list,\n * done once: `role=\"menu\"`, a roving focus that skips disabled entries, Escape\n * back to the trigger, and separators and headings that stay out of the keyboard\n * order.\n */\nfunction Menu({\n  items,\n  children,\n  onAction,\n  isOpen: controlledOpen,\n  onOpenChange,\n  placement = PopoverPlacement.Bottom,\n  align = PopoverAlign.Start,\n  presentation,\n  className,\n  style,\n  classNames,\n  styles: slotStyles,\n  ...rest\n}: MenuProps): ReactElement {\n  const isControlled = controlledOpen !== undefined;\n  const [internalOpen, setInternalOpen] = useState(false);\n  const isOpen = isControlled ? controlledOpen : internalOpen;\n\n  const [activeIndex, setActiveIndex] = useState(-1);\n  const itemRefs = useRef<(HTMLElement | null)[]>([]);\n  const triggerRef = useRef<HTMLSpanElement>(null);\n  const menuRef = useRef<HTMLDivElement>(null);\n  // Tab leaves the panel in both directions, so the menu needs the same boundary\n  // Popover uses: back to the trigger, or on to whatever follows it.\n  const { restoreFocusToTrigger, focusAfterTrigger } = useFocusBoundary({ triggerRef, contentRef: menuRef });\n  const menuId = useId();\n  const followLink = useLinkClick();\n  // Which end to land on when the menu opens: `ArrowUp` on the trigger opens at\n  // the bottom, everything else at the top.\n  const openAtRef = useRef<'first' | 'last'>('first');\n  const typeahead = useRef<{ query: string; timer: ReturnType<typeof setTimeout> | undefined }>({\n    query: '',\n    timer: undefined,\n  });\n\n  // Whether a key opened the menu, which decides between landing the focus on an\n  // entry and leaving it on the trigger. Set from Popover, which is what sees the\n  // trigger's events, or here when an arrow key did the opening.\n  const openedByKeyboardRef = useRef(false);\n\n  function setOpen(next: boolean, fromKeyboard = false) {\n    if (next) openedByKeyboardRef.current = fromKeyboard;\n    if (!isControlled) setInternalOpen(next);\n    onOpenChange?.(next);\n  }\n\n  // The effect below needs the entries, but must not re-run when the array is rebuilt. A consumer\n  // writes `items={[…]}` inline, so it is a new identity on every render of *their* component:\n  // keyed off it, any parent re-render while the menu was open threw the focus back to the first\n  // entry, mid-navigation. Declared above the effect that reads it, so it has already run.\n  const itemsRef = useRef(items);\n  useEffect(() => {\n    itemsRef.current = items;\n  });\n\n  // Focus follows the active entry rather than the menu keeping focus and\n  // pointing at it: the items are real buttons, so Enter and Space are theirs.\n  // Popover's own `focusOnOpen` would take the first focusable and stop there;\n  // the active index has to move with it, so the menu does it itself.\n  useEffect(() => {\n    if (!isOpen) return;\n    // A pointer open leaves the focus where the pointer put it. A highlight on an\n    // entry nobody pointed at reads as a selection, and the mouse is about to land\n    // somewhere else anyway; the first arrow key moves in, see the trigger's keys.\n    if (!openedByKeyboardRef.current) {\n      setActiveIndex(-1);\n      return;\n    }\n    const entries = itemsRef.current;\n    const target = openAtRef.current === 'last' ? lastEnabled(entries) : firstEnabled(entries);\n    setActiveIndex(target);\n    // The panel is portalled and animated in, so its buttons exist a frame later.\n    const frame = requestAnimationFrame(() => {\n      itemRefs.current[target]?.focus();\n    });\n    return () => {\n      cancelAnimationFrame(frame);\n      openAtRef.current = 'first';\n    };\n  }, [isOpen]);\n\n  useEffect(() => () => clearTimeout(typeahead.current.timer), []);\n\n  function moveTo(index: number) {\n    if (index < 0) return;\n    setActiveIndex(index);\n    itemRefs.current[index]?.focus();\n  }\n\n  function close(returnFocus: boolean) {\n    setOpen(false);\n    if (returnFocus) restoreFocusToTrigger();\n  }\n\n  /**\n   * Jumps to the next entry whose label starts with what has been typed. The\n   * text comes off the rendered button rather than the `label` prop, which is a\n   * `ReactNode` and may be an icon, a badge or anything else.\n   */\n  function handleTypeahead(key: string): boolean {\n    if (key.length !== 1 || !key.trim()) return false;\n    clearTimeout(typeahead.current.timer);\n    typeahead.current.query += key.toLowerCase();\n    typeahead.current.timer = setTimeout(() => {\n      typeahead.current.query = '';\n    }, TYPEAHEAD_RESET_MS);\n\n    const query = typeahead.current.query;\n    // A repeated single character walks through the entries starting with it,\n    // rather than sticking to the first one.\n    const repeated = query.length > 1 && [...query].every((c) => c === query[0]);\n    const needle = repeated ? query[0] : query;\n    const from = repeated || query.length === 1 ? activeIndex : activeIndex - 1;\n\n    for (let i = 1; i <= items.length; i++) {\n      const index = (from + i + items.length) % items.length;\n      const entry = items[index];\n      if (!isMenuAction(entry) || entry.disabled) continue;\n      const text = itemRefs.current[index]?.textContent?.trim().toLowerCase() ?? '';\n      if (text.startsWith(needle)) {\n        moveTo(index);\n        return true;\n      }\n    }\n    return false;\n  }\n\n  /** The trigger's own keys, which decide where the menu opens. */\n  function handleTriggerKeyDown(event: React.KeyboardEvent) {\n    if (event.key !== 'ArrowDown' && event.key !== 'ArrowUp') return;\n    // Popover opens on these keys too; letting both act would be harmless but\n    // this keeps the decision about where to land in one place.\n    event.preventDefault();\n    event.stopPropagation();\n    // Already open means a pointer opened it and the focus never left the trigger,\n    // since an entry's own keys never reach here. This is the key that moves in.\n    if (isOpen) {\n      moveTo(event.key === 'ArrowUp' ? lastEnabled(items) : firstEnabled(items));\n      return;\n    }\n    openAtRef.current = event.key === 'ArrowUp' ? 'last' : 'first';\n    setOpen(true, true);\n  }\n\n  function handleKeyDown(event: React.KeyboardEvent) {\n    switch (event.key) {\n      case 'ArrowDown':\n        moveTo(nextEnabled(items, activeIndex, 1));\n        break;\n      case 'ArrowUp':\n        // Stepping back from \"nothing active\" lands two short of the end, so the end is named.\n        moveTo(activeIndex < 0 ? lastEnabled(items) : nextEnabled(items, activeIndex, -1));\n        break;\n      case 'Home':\n        moveTo(firstEnabled(items));\n        break;\n      case 'End':\n        moveTo(lastEnabled(items));\n        break;\n      // Popover handles both of these too, but only once the focus has actually\n      // reached the panel: its Escape restores focus on that condition, and its\n      // Tab handling acts at the first and last focusable. A menu focuses its\n      // first entry a frame after opening, so a key pressed in between would\n      // fall through. Here they are unconditional, which is also what a menu\n      // wants from Tab: leave, whatever is focused.\n      case 'Escape':\n        close(true);\n        break;\n      case 'Tab':\n        // Carry on from the trigger, as if the panel had never been in the tab\n        // order. Closing and letting the browser take it from here would move on\n        // from an entry that is about to be removed, which lands at the top of\n        // the document.\n        close(false);\n        if (event.shiftKey) restoreFocusToTrigger();\n        else focusAfterTrigger();\n        break;\n      default:\n        if (!handleTypeahead(event.key)) return;\n    }\n    event.preventDefault();\n    event.stopPropagation();\n  }\n\n  function handleAction(index: number) {\n    const entry = items[index];\n    if (!isMenuAction(entry) || entry.disabled) return;\n    // Not runMenuAction: a link entry is an <a>, which the browser follows by itself.\n    entry.onClick?.();\n    onAction?.(entry.id);\n    close(true);\n  }\n\n  // A screen reader has no way to tell that this button opens a menu unless the\n  // button itself says so. Only filled in where the consumer has not, so a\n  // trigger that describes itself keeps doing that.\n  const triggerWithMenuSemantics = isValidElement<Record<string, unknown>>(children)\n    ? cloneElement(children, {\n        'aria-haspopup': children.props['aria-haspopup'] ?? 'menu',\n        'aria-expanded': children.props['aria-expanded'] ?? isOpen,\n        'aria-controls': isOpen ? (children.props['aria-controls'] ?? menuId) : children.props['aria-controls'],\n      })\n    : children;\n\n  const hasIcons = hasAnyIcon(items);\n\n  const content = (\n    <div\n      ref={menuRef}\n      id={menuId}\n      role='menu'\n      className={cx(styles.menu, classNames?.menu)}\n      style={slotStyles?.menu}\n      onKeyDown={handleKeyDown}\n    >\n      {items.map((entry, index) => {\n        if (isMenuSeparator(entry))\n          return (\n            <div\n              // Separators and headings carry no id of their own, and reordering\n              // the list re-renders it wholesale anyway.\n              key={`separator-${index}`}\n              role='separator'\n              className={cx(styles.menuSeparator, classNames?.separator)}\n              style={slotStyles?.separator}\n            />\n          );\n\n        if (isMenuHeading(entry))\n          return (\n            <div\n              key={`heading-${index}`}\n              role='presentation'\n              className={cx(styles.menuHeading, classNames?.heading)}\n              style={slotStyles?.heading}\n            >\n              {entry.heading}\n            </div>\n          );\n\n        const itemProps = {\n          ref: (el: HTMLElement | null) => {\n            itemRefs.current[index] = el;\n          },\n          role: 'menuitem',\n          'aria-keyshortcuts': ariaKeyshortcuts(entry.shortcut),\n          // One tab stop for the whole menu: the arrow keys move between items.\n          tabIndex: index === activeIndex ? 0 : -1,\n          className: cx(styles.menuItem, entry.danger && styles.menuItemDanger, classNames?.item),\n          style: slotStyles?.item,\n          onClick: () => handleAction(index),\n          onMouseEnter: () => setActiveIndex(index),\n        };\n        const itemContent = (\n          <>\n            {renderItemIcon(entry.icon, hasIcons, {\n              className: cx(styles.menuItemIcon, classNames?.itemIcon),\n              style: slotStyles?.itemIcon,\n            })}\n            <span className={cx(styles.menuItemLabel, classNames?.itemLabel)} style={slotStyles?.itemLabel}>\n              {entry.label}\n            </span>\n            {entry.shortcut != null && (\n              <span\n                aria-hidden\n                className={cx(styles.menuItemShortcut, classNames?.itemShortcut)}\n                style={slotStyles?.itemShortcut}\n              >\n                {formatHotkey(entry.shortcut)}\n              </span>\n            )}\n          </>\n        );\n\n        // An <a> cannot be disabled, so a disabled link is a disabled button.\n        return entry.href && !entry.disabled ? (\n          <a\n            key={entry.id}\n            href={entry.href}\n            {...itemProps}\n            onClick={(event) => {\n              handleAction(index);\n              followLink(event);\n            }}\n            // A link takes Enter but not Space, which every other entry answers to.\n            onKeyDown={(event) => {\n              if (event.key !== ' ' || !isSelfActivation(event)) return;\n              event.preventDefault();\n              event.currentTarget.click();\n            }}\n          >\n            {itemContent}\n          </a>\n        ) : (\n          <button key={entry.id} type='button' disabled={entry.disabled} {...itemProps}>\n            {itemContent}\n          </button>\n        );\n      })}\n    </div>\n  );\n\n  return (\n    <Popover\n      {...rest}\n      isOpen={isOpen}\n      onOpenChange={(next, { fromKeyboard }) => setOpen(next, fromKeyboard)}\n      placement={placement}\n      align={align}\n      presentation={presentation}\n      content={content}\n      // The list pads its own rows, so a hover highlight reaches the panel edge,\n      // and it sizes itself. Focus is the menu's own business, see above.\n      focusOnOpen={false}\n      constrainWidth={false}\n      classNames={{ panel: styles.menuPanel }}\n      className={cx(className, classNames?.root)}\n      style={{ ...style, ...slotStyles?.root }}\n    >\n      <span\n        ref={triggerRef}\n        className={cx(styles.menuTrigger, classNames?.trigger)}\n        style={slotStyles?.trigger}\n        onKeyDown={handleTriggerKeyDown}\n      >\n        {triggerWithMenuSemantics}\n      </span>\n    </Popover>\n  );\n}\n\nexport default Menu;\n","import type { HotkeyMap } from '../hooks/useHotkeys';\nimport { isMenuAction, type MenuEntry } from './menu.types';\nimport { runMenuAction } from './menuItem';\n\n/**\n * The bindings for a menu's `shortcut` entries, to hand to `useHotkeys` yourself.\n *\n * The menu draws a shortcut but never listens for one, because only the app knows what else is\n * listening: binding it here as well as in an app's own keymap would run the action twice, and\n * whether a shortcut should work at all times or only while the menu is open is the app's\n * question, not the menu's. Calling this keeps the answer in one spelling, so the hint the menu\n * draws cannot drift from what actually fires.\n *\n * ```tsx\n * useHotkeys(menuHotkeys(items, run));                      // whenever the page is up\n * useHotkeys(menuHotkeys(items, run), { enabled: isOpen }); // only while the menu is open\n * ```\n *\n * Runs each entry the way clicking it does: its own `onClick`, then `onAction`, then its `href`.\n * A `disabled` entry, and an entry with no `shortcut`, is left out.\n */\nexport function menuHotkeys(items: MenuEntry[], onAction?: (id: string) => void): HotkeyMap {\n  const bindings: HotkeyMap = {};\n  for (const entry of items) {\n    if (!isMenuAction(entry) || entry.disabled || !entry.shortcut) continue;\n    bindings[entry.shortcut] = () => runMenuAction(entry, onAction);\n  }\n  return bindings;\n}\n",".multiSelect {\n  position: relative;\n  width: 100%;\n\n  /* Input's notch geometry, so both labels float to the same height in a form row. */\n  --multi-select-notch-height: 1.75em;\n  --field-label-offset: 0px;\n\n  /* Input's --field-label-offset, for the text only: the resting label and the placeholder. */\n  &-withLabel {\n    --field-label-offset: 1px;\n  }\n\n  &-label {\n    position: absolute;\n    left: 7px;\n    right: 0;\n    top: calc(50% + var(--field-label-offset));\n    transform: translateY(-50%);\n    font-size: 1em;\n    color: var(--input-label-resting-color, var(--text-light));\n    pointer-events: none;\n    user-select: none;\n    transition: all 0.3s;\n\n    &-floating {\n      top: 0;\n      bottom: auto;\n      left: 14px;\n      font-size: 0.8em;\n      color: var(--input-label-floating-color, var(--text-color));\n    }\n  }\n\n  /* The field's border lives here, not on the control: the legend has to cut the notch out of it. */\n  &-fieldset {\n    position: absolute;\n    inset: calc(var(--multi-select-notch-height) / -2) 0 0;\n    width: 100%;\n    margin: 0;\n    padding: 0 0 0 8px;\n    min-width: 0;\n    box-sizing: border-box;\n    border: 1px solid var(--border-color);\n    border-radius: var(--default-border-radius);\n    font-size: 0.8em;\n    pointer-events: none;\n    user-select: none;\n    transition: border-color 0.15s;\n\n    &-focused {\n      border-color: var(--focus-border-color, var(--primary-color));\n    }\n\n    &-error {\n      border-color: var(--error-border-color, var(--error-color));\n    }\n\n    &-legend {\n      display: block;\n\n      /* Not taken from the text: an empty span gets no line box, which raised a label-less\n         field's border half a notch. */\n      height: var(--multi-select-notch-height);\n      max-width: 0;\n      padding: 0;\n      white-space: nowrap;\n      transition: max-width 0.2s;\n\n      &-active {\n        max-width: 300px;\n      }\n\n      span {\n        line-height: var(--multi-select-notch-height);\n        padding: 0 5px;\n        opacity: 0;\n      }\n\n      span:empty {\n        padding: 0;\n      }\n    }\n  }\n\n  &-control {\n    position: relative;\n    display: flex;\n    align-items: center;\n    gap: 6px;\n    box-sizing: border-box;\n    width: 100%;\n\n    /* Input's container sets the same, and Dropdown's button reaches it through a 1.25 line-height\n       plus 14px of padding. Kept as the formula rather than a flat 34px so it still matches them at\n       another font size. */\n    min-height: var(--multi-select-min-height, calc(1.25em + 14px));\n\n    /* No --field-label-offset: unlike text, a chip is a filled box nearly as tall as the field, and\n       the eye measures it against the border, not the label. */\n    padding: 2px 8px;\n    border-radius: var(--default-border-radius);\n    background: var(--input-background, var(--background));\n    color: var(--text-color);\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n\n    /* An outline here would draw a second, un-notched rectangle over the fieldset's border. */\n    &:focus-visible {\n      outline: none;\n\n      .multiSelect-fieldset {\n        border-color: var(--focus-border-color, var(--primary-color));\n      }\n    }\n  }\n\n  &-values {\n    display: flex;\n    flex-wrap: wrap;\n    align-items: center;\n    gap: 4px;\n    flex: 1;\n    min-width: 0;\n  }\n\n  /* At full size a Chip is nearly as tall as the field, so one row would set the height instead\n     of fitting inside it. Shrinking the font shrinks the text and the 1em remove icon together. */\n  &-chip {\n    max-width: 100%;\n    min-width: 0;\n    font-size: 0.8em;\n  }\n\n  /* Faded in on a delay rather than swapped in: the label takes 0.3s to float clear, and showing\n     the placeholder as it starts moving puts the two on top of each other. As Input does it. */\n  &-placeholder {\n    position: absolute;\n    left: 8px;\n    right: 1.75em;\n    top: calc(50% + var(--field-label-offset));\n    transform: translateY(-50%);\n    overflow: hidden;\n    text-overflow: ellipsis;\n    white-space: nowrap;\n    opacity: 0;\n    color: var(--input-label-resting-color, var(--text-light));\n    pointer-events: none;\n    user-select: none;\n\n    &-shown {\n      animation: 0.2s multiSelectFadeIn 0.3s forwards;\n    }\n\n    /* Nothing to wait for without a label. Must stay after `-shown` to win the specificity tie. */\n    &-instant {\n      animation-delay: 0s;\n    }\n  }\n\n  @keyframes multiSelectFadeIn {\n    from {\n      opacity: 0;\n    }\n\n    to {\n      opacity: 1;\n    }\n  }\n\n  &-clearButton {\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    flex: none;\n    width: 22px;\n    height: 22px;\n    padding: 0;\n    border: none;\n    border-radius: 50%;\n    background: transparent;\n    color: var(--text-dark);\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n\n    &:hover {\n      background: var(--background-accent);\n      color: var(--text-color);\n    }\n  }\n\n  &-icon {\n    flex: none;\n    color: var(--input-icon-color, var(--text-dark));\n    transition: transform 0.2s;\n\n    &-open {\n      transform: rotate(180deg);\n    }\n  }\n\n  &-disabled {\n    opacity: 0.5;\n\n    .multiSelect-control {\n      cursor: default;\n    }\n  }\n}\n\n/* Portalled, so a sibling of the field rather than a descendant, and not nestable above. */\n.multiSelect-panel {\n  display: flex;\n  flex-direction: column;\n  gap: 6px;\n  min-width: 180px;\n}\n\n.multiSelect-search {\n  flex: none;\n}\n\n.multiSelect-options {\n  display: flex;\n  flex-direction: column;\n  max-height: var(--multi-select-panel-height, 260px);\n  overflow-y: auto;\n}\n\n.multiSelect-option {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  padding: 8px 10px;\n  border-radius: var(--default-border-radius);\n  color: var(--text-color);\n  cursor: pointer;\n  -webkit-tap-highlight-color: transparent;\n\n  &-active {\n    background: var(--background-accent);\n  }\n\n  &-selected {\n    color: var(--primary-color);\n    font-weight: 600;\n  }\n\n  &-disabled {\n    opacity: 0.45;\n    cursor: default;\n    pointer-events: none;\n  }\n}\n\n.multiSelect-optionCheck {\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  flex: none;\n  width: 1em;\n  color: var(--primary-color);\n}\n\n.multiSelect-optionLabel {\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n\n.multiSelect-empty {\n  padding: 12px 10px;\n  color: var(--text-dark);\n  text-align: center;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .multiSelect-label,\n  .multiSelect-icon {\n    transition: none;\n  }\n}\n","import { useEffect, useId, useRef, useState } from 'react';\nimport type { ReactElement } from 'react';\nimport cx from 'classnames';\nimport { faChevronDown, faCheck, faXmark } from '@fortawesome/free-solid-svg-icons';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\n\nimport Chip from 'components/common/chip';\nimport SearchInput from 'components/common/searchInput';\nimport Tooltip from 'components/common/tooltip';\nimport Popover from 'components/common/popover';\nimport { PopoverAlign, PopoverPlacement, PopoverTrigger } from '../popover/popover.types';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport { edgeIndex, nextIndex, scrollOptionIntoView } from '../utils/listNavigation';\nimport type { MultiSelectItem, MultiSelectLabels, MultiSelectProps, MultiSelectValue } from './multiSelect.types';\n\nimport styles from './multiSelect.module.pcss';\n\nconst EMPTY: MultiSelectValue[] = [];\n\nconst DEFAULT_LABELS: Required<MultiSelectLabels> = {\n  clearSelection: 'Clear selection',\n  create: (value) => `Add \"${value}\"`,\n  searchPlaceholder: 'Search…',\n  empty: 'No matches',\n};\n\nfunction MultiSelect(props: MultiSelectProps): ReactElement {\n  const {\n    items,\n    value,\n    defaultValue = [],\n    onChange = () => {},\n    label = '',\n    alwaysFloatLabel = false,\n    placeholder = '',\n    searchable = false,\n    allowCustomValues = false,\n    maxSelected,\n    clearable = true,\n    disabled = false,\n    readOnly = false,\n    presentation,\n    formValidator = null,\n    errorMessage = '',\n    isValid = true,\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    onMouseEnter,\n    onMouseLeave,\n    onFocus,\n    onBlur,\n    labels,\n    ...rest\n  } = useComponentDefaults('MultiSelect', props);\n  const text = useLabels('MultiSelect', labels, DEFAULT_LABELS);\n\n  const rootRef = useRef<HTMLDivElement>(null);\n  const controlRef = useRef<HTMLDivElement>(null);\n  const searchRef = useRef<HTMLDivElement>(null);\n  const optionsRef = useRef<HTMLDivElement>(null);\n  const listboxId = useId();\n\n  const [internalValue, setInternalValue] = useState<MultiSelectValue[]>(defaultValue);\n  const [isOpen, setIsOpen] = useState(false);\n  const [search, setSearch] = useState('');\n  const [activeIndex, setActiveIndex] = useState(0);\n  const [isFocused, setIsFocused] = useState(false);\n  const [isHovered, setIsHovered] = useState(false);\n  const [panelWidth, setPanelWidth] = useState<number>();\n\n  // A ref as well as the prop: react-hooks/immutability refuses a mutation of the prop itself in\n  // an event handler, and marking the validator touched happens in one.\n  const fvRef = useRef(formValidator);\n  const [, forceUpdate] = useState(0);\n  useEffect(() => {\n    fvRef.current = formValidator;\n    const fv = fvRef.current;\n    if (!fv) return;\n    fv.label = label;\n    fv.validate();\n    const listener = () => forceUpdate((n) => n + 1);\n    fv.registerOnUpdateListener(listener);\n    return () => {\n      fv.removeOnUpdateListener(listener);\n    };\n  }, [formValidator, label]);\n\n  const isControlled = value !== undefined && !formValidator;\n  const rawValue = formValidator ? formValidator.value : isControlled ? value : internalValue;\n  const selected = Array.isArray(rawValue) ? (rawValue as MultiSelectValue[]) : EMPTY;\n\n  const hasError = formValidator ? formValidator.shouldShowError : !isValid || !!errorMessage;\n  const errorText = formValidator ? formValidator.getCurrentErrorMessage() : errorMessage || '';\n\n  const isInteractive = !disabled && !readOnly;\n  const atMax = maxSelected !== undefined && selected.length >= maxSelected;\n\n  const shouldFloatLabel = alwaysFloatLabel || isOpen || isFocused || selected.length > 0;\n\n  const isSearchable = searchable || allowCustomValues;\n  const typed = search.trim();\n  const needle = isSearchable ? typed.toLowerCase() : '';\n  const matches = needle ? items.filter((item) => item.label.toLowerCase().includes(needle)) : items;\n\n  const canCreate =\n    allowCustomValues &&\n    typed.length > 0 &&\n    !atMax &&\n    !items.some((item) => item.label.toLowerCase() === needle || String(item.key).toLowerCase() === needle) &&\n    !selected.some((key) => String(key).toLowerCase() === needle);\n\n  const visibleItems: MultiSelectItem[] = canCreate ? [...matches, { key: typed, label: text.create(typed) }] : matches;\n\n  // Mapped from `selected`, not filtered from `items`, to keep the chips in pick order. A custom\n  // value has no entry in `items`, so it stands in for itself.\n  const selectedItems = selected\n    .map<MultiSelectItem | undefined>(\n      (key) => items.find((item) => item.key === key) ?? (allowCustomValues ? { key, label: String(key) } : undefined),\n    )\n    .filter((item): item is MultiSelectItem => item !== undefined);\n\n  function commit(next: MultiSelectValue[]) {\n    if (formValidator) formValidator.set(next);\n    else if (!isControlled) setInternalValue(next);\n    onChange(next);\n  }\n\n  function toggle(item: MultiSelectItem) {\n    if (!isInteractive || item.disabled) return;\n    if (selected.includes(item.key)) commit(selected.filter((key) => key !== item.key));\n    else if (!atMax) commit([...selected, item.key]);\n    // The typed text has become a chip, so leaving it in the box would offer to add it again.\n    if (canCreate && item.key === typed) setSearch('');\n  }\n\n  /**\n   * A focused element that unmounts fires no blur, so removing the chip or clear button that was\n   * just clicked would leave `isFocused` stuck true. Hand focus back so there is still something\n   * left to blur.\n   */\n  function keepFocusInControl() {\n    if (rootRef.current?.contains(document.activeElement)) controlRef.current?.focus();\n  }\n\n  function handleOpenChange(next: boolean) {\n    if (next && !isInteractive) return;\n    setIsOpen(next);\n    if (next) {\n      setPanelWidth(controlRef.current?.offsetWidth);\n      setActiveIndex(0);\n    } else {\n      setSearch('');\n      if (fvRef.current) fvRef.current.touched = true;\n    }\n  }\n\n  useEffect(() => {\n    if (!isOpen || !isSearchable) return;\n    searchRef.current?.querySelector('input')?.focus();\n  }, [isOpen, isSearchable]);\n\n  useEffect(() => {\n    if (!isOpen) return;\n    scrollOptionIntoView(optionsRef.current, document.getElementById(`${listboxId}-${activeIndex}`));\n  }, [isOpen, activeIndex, listboxId]);\n\n  // Matches what the options render as `aria-disabled`, so the arrows cannot land on one.\n  const navigation = {\n    isDisabled: (index: number) => {\n      const item = visibleItems[index];\n      return !!item.disabled || (atMax && !selected.includes(item.key));\n    },\n  };\n\n  function handleKeyDown(event: React.KeyboardEvent) {\n    if (!isInteractive) return;\n\n    if (!isOpen) {\n      if (event.key === 'ArrowDown' || event.key === 'Enter' || event.key === ' ') {\n        event.preventDefault();\n        handleOpenChange(true);\n      } else if (event.key === 'Backspace' && selected.length > 0) {\n        commit(selected.slice(0, -1));\n      }\n      return;\n    }\n\n    if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n      event.preventDefault();\n      setActiveIndex(nextIndex(activeIndex, event.key === 'ArrowDown' ? 1 : -1, visibleItems.length, navigation) ?? 0);\n    } else if (event.key === 'Home' || event.key === 'End') {\n      event.preventDefault();\n      setActiveIndex(edgeIndex(event.key === 'Home' ? 'first' : 'last', visibleItems.length, navigation) ?? 0);\n    } else if (event.key === 'Enter') {\n      event.preventDefault();\n      const item = visibleItems[activeIndex];\n      if (item) toggle(item);\n    } else if (event.key === 'Backspace' && !search && selected.length > 0) {\n      commit(selected.slice(0, -1));\n    }\n  }\n\n  const panel = (\n    <div className={cx(styles.multiSelectPanel, classNames?.panel)} style={slotStyles?.panel}>\n      {isSearchable && (\n        <div ref={searchRef} className={cx(styles.multiSelectSearch, classNames?.search)} style={slotStyles?.search}>\n          <SearchInput\n            value={search}\n            onChange={(value) => {\n              setSearch(value);\n              setActiveIndex(0);\n            }}\n            placeholder={text.searchPlaceholder}\n            onKeyDown={handleKeyDown}\n          />\n        </div>\n      )}\n      <div\n        ref={optionsRef}\n        id={listboxId}\n        role='listbox'\n        aria-multiselectable='true'\n        aria-label={label || undefined}\n        className={cx(styles.multiSelectOptions, classNames?.options)}\n        style={slotStyles?.options}\n      >\n        {visibleItems.length === 0 ? (\n          <div className={cx(styles.multiSelectEmpty, classNames?.empty)} style={slotStyles?.empty}>\n            {text.empty}\n          </div>\n        ) : (\n          visibleItems.map((item, index) => {\n            const isSelected = selected.includes(item.key);\n            const isDisabled = item.disabled || (atMax && !isSelected);\n\n            return (\n              <div\n                key={item.key}\n                id={`${listboxId}-${index}`}\n                role='option'\n                aria-selected={isSelected}\n                aria-disabled={isDisabled || undefined}\n                className={cx(\n                  styles.multiSelectOption,\n                  index === activeIndex && styles.multiSelectOptionActive,\n                  isSelected && styles.multiSelectOptionSelected,\n                  isDisabled && styles.multiSelectOptionDisabled,\n                  classNames?.option,\n                )}\n                style={slotStyles?.option}\n                onMouseDown={(event) => event.preventDefault()}\n                onMouseEnter={() => setActiveIndex(index)}\n                onClick={() => toggle(item)}\n              >\n                <span\n                  className={cx(styles.multiSelectOptionCheck, classNames?.optionCheck)}\n                  style={slotStyles?.optionCheck}\n                >\n                  {isSelected && <FontAwesomeIcon icon={faCheck} />}\n                </span>\n                <span\n                  className={cx(styles.multiSelectOptionLabel, classNames?.optionLabel)}\n                  style={slotStyles?.optionLabel}\n                >\n                  {item.label}\n                </span>\n              </div>\n            );\n          })\n        )}\n      </div>\n    </div>\n  );\n\n  return (\n    <div\n      {...rest}\n      ref={rootRef}\n      className={cx(\n        styles.multiSelect,\n        disabled && styles.multiSelectDisabled,\n        !!label && styles.multiSelectWithLabel,\n        classNames?.root,\n        className,\n      )}\n      style={{ ...style, ...slotStyles?.root }}\n      onMouseEnter={(event) => {\n        setIsHovered(true);\n        onMouseEnter?.(event);\n      }}\n      onMouseLeave={(event) => {\n        setIsHovered(false);\n        onMouseLeave?.(event);\n      }}\n      onFocus={(event) => {\n        setIsFocused(true);\n        onFocus?.(event);\n      }}\n      onBlur={(event) => {\n        if (event.currentTarget.contains(event.relatedTarget as Node | null)) return;\n        setIsFocused(false);\n        onBlur?.(event);\n      }}\n    >\n      <Popover\n        isOpen={isOpen}\n        onOpenChange={handleOpenChange}\n        trigger={PopoverTrigger.Click}\n        placement={PopoverPlacement.Bottom}\n        align={PopoverAlign.Start}\n        offset={4}\n        withArrow={false}\n        // Neither half of this is the keyboard-only default. A search field takes\n        // the caret however the panel was opened, and without one the control\n        // keeps the focus and points at the active option with\n        // `aria-activedescendant`, so nothing may take it away.\n        focusOnOpen={isSearchable}\n        constrainWidth={false}\n        width='trigger'\n        disabled={!isInteractive}\n        presentation={presentation}\n        content={panel}\n        styles={{ panel: panelWidth ? { minWidth: panelWidth } : undefined }}\n      >\n        <div\n          ref={controlRef}\n          role='combobox'\n          tabIndex={disabled ? -1 : 0}\n          aria-expanded={isOpen}\n          aria-haspopup='listbox'\n          aria-controls={listboxId}\n          aria-activedescendant={isOpen && visibleItems.length > 0 ? `${listboxId}-${activeIndex}` : undefined}\n          aria-disabled={disabled || undefined}\n          aria-readonly={readOnly || undefined}\n          className={cx(styles.multiSelectControl, classNames?.control)}\n          style={slotStyles?.control}\n          onKeyDown={handleKeyDown}\n        >\n          {placeholder && selectedItems.length === 0 && (\n            <span\n              className={cx(\n                styles.multiSelectPlaceholder,\n                (!label || shouldFloatLabel) && styles.multiSelectPlaceholderShown,\n                !label && styles.multiSelectPlaceholderInstant,\n                classNames?.placeholder,\n              )}\n              style={slotStyles?.placeholder}\n            >\n              {placeholder}\n            </span>\n          )}\n\n          <div className={cx(styles.multiSelectValues, classNames?.values)} style={slotStyles?.values}>\n            {selectedItems.map((item) => (\n              <Chip\n                key={item.key}\n                text={item.label}\n                showRemoveIcon={isInteractive}\n                labels={{ remove: `Remove ${item.label}` }}\n                onMouseDown={(event) => event.preventDefault()}\n                onRemove={() => {\n                  toggle(item);\n                  keepFocusInControl();\n                }}\n                className={cx(styles.multiSelectChip, classNames?.chip)}\n                style={slotStyles?.chip}\n              />\n            ))}\n          </div>\n\n          {clearable && isInteractive && selected.length > 0 && (\n            <button\n              type='button'\n              tabIndex={-1}\n              aria-label={text.clearSelection}\n              className={cx(styles.multiSelectClearButton, classNames?.clearButton)}\n              style={slotStyles?.clearButton}\n              onMouseDown={(event) => event.preventDefault()}\n              onClick={(event) => {\n                event.stopPropagation();\n                commit([]);\n                keepFocusInControl();\n              }}\n            >\n              <FontAwesomeIcon icon={faXmark} />\n            </button>\n          )}\n\n          <FontAwesomeIcon\n            icon={faChevronDown}\n            className={cx(styles.multiSelectIcon, isOpen && styles.multiSelectIconOpen, classNames?.icon)}\n            style={slotStyles?.icon as React.CSSProperties & Record<`--fa-font-${string}`, string>}\n          />\n\n          {label && (\n            <div\n              className={cx(\n                styles.multiSelectLabel,\n                shouldFloatLabel && styles.multiSelectLabelFloating,\n                classNames?.label,\n              )}\n              style={slotStyles?.label}\n            >\n              {label}\n            </div>\n          )}\n\n          {/* The visible border lives here rather than on the control, because the legend is what\n              cuts the notch the floating label sits in. Same construction as Input. */}\n          <fieldset\n            className={cx(\n              styles.multiSelectFieldset,\n              (isFocused || isOpen) && styles.multiSelectFieldsetFocused,\n              hasError && styles.multiSelectFieldsetError,\n              classNames?.fieldset,\n            )}\n            style={slotStyles?.fieldset}\n          >\n            <legend\n              className={cx(\n                styles.multiSelectFieldsetLegend,\n                shouldFloatLabel && styles.multiSelectFieldsetLegendActive,\n              )}\n            >\n              <span>{label}</span>\n            </legend>\n          </fieldset>\n        </div>\n      </Popover>\n\n      <Tooltip variant='error' message={errorText} isVisible={hasError && (isFocused || isHovered)} />\n    </div>\n  );\n}\n\nexport default MultiSelect;\n",".numberInput {\n  /* Only set when the split layout wraps this input in a flex row. */\n  &-split {\n    flex: 1;\n    min-width: 0;\n  }\n}\n\n/* ── Stacked layout: chevrons inside the field's right edge (mouse-oriented) ───────── */\n\n/* Input's `suffix` slot: stretch to the field so the chevrons get its full height. Two classes, to\n   outrank Input's one-class suffix rules whichever stylesheet loads first. */\n.numberInput .arrowSuffix {\n  align-self: stretch;\n  display: flex;\n  padding-right: 0;\n}\n\n.arrows {\n  display: flex;\n  flex-direction: column;\n  align-self: stretch;\n  border-left: 1px solid var(--border-color);\n\n  /* Sitting flush against the field's edge, the chevrons' background would square off its\n     rounded corner, so it is clipped. Read from --group-radius-* like Input's own border, so it\n     still flattens inside a ButtonGroup. */\n  overflow: hidden;\n  border-top-right-radius: var(--group-radius-tr, var(--default-border-radius));\n  border-bottom-right-radius: var(--group-radius-br, var(--default-border-radius));\n}\n\n.arrowBtn {\n  flex: 1;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  background: none;\n  border: none;\n  padding: 0 0.5em;\n\n  /* No min-height: the two halves divide the field's own height, so a stepper never makes a\n     NumberInput taller than a plain Input. In em, so it tracks the field's text size. */\n  min-width: 1.75em;\n  font-size: 0.75em;\n  line-height: 1;\n  color: var(--text-dark);\n  cursor: pointer;\n  -webkit-tap-highlight-color: transparent;\n\n  /* So a long press reads as a hold, not a text gesture. */\n  touch-action: manipulation;\n  user-select: none;\n  -webkit-user-select: none;\n  -webkit-touch-callout: none;\n  transition:\n    color 0.15s,\n    opacity 0.15s;\n\n  &:disabled {\n    cursor: default;\n    opacity: 0.35;\n  }\n\n  &:focus-visible {\n    outline: 2px solid var(--focus-border-color, var(--primary-color));\n    outline-offset: -2px;\n  }\n\n  /* (hover: hover) only — on touch a :hover state latches after a tap until you tap elsewhere.\n\n     Colour-only, unlike the --background-accent-light hover surface used elsewhere in the\n     library: this button sits flush against .arrows' 1px --border-color divider, and in dark\n     palettes those two tokens are close enough in luminance that painting the surface right up\n     to the line eats most of the divider's already-low contrast, under the pointer. Tuning the\n     hover colour per theme would not hold — the collision is between three tokens (divider,\n     hover surface, field background), so any new theme can reintroduce it. The chevron turning\n     --primary-color is affordance enough on a target this small. */\n  @media (hover: hover) {\n    &:hover:not(:disabled) {\n      color: var(--primary-color);\n    }\n  }\n\n  &:active:not(:disabled) {\n    color: var(--primary-color);\n  }\n}\n\n/* ── Split layout: full-height − / + flanking the field (touch-oriented) ───────────── */\n\n/* ButtonGroup is inline-flex; a form field fills its row instead, with the Input taking the\n   slack (see .numberInput-split). The class is repeated to beat `.buttonGroup`'s own `display`\n   without depending on which CSS module the bundler emits first. */\n.split.split {\n  display: flex;\n  width: 100%;\n}\n\n/* Two classes, to outrank Button's one-class rules whichever stylesheet loads first. Resting state\n   only: Button's hover and press layer tints over it. */\n.split .splitBtn {\n  background: var(--input-background);\n  color: var(--text-dark);\n  border-color: var(--border-color);\n}\n\n.splitBtn {\n  flex: 0 0 auto;\n\n  /* No fixed height — ButtonGroup stretches these to the field.\n\n     The width repeats Input's own height, `calc(1.25em + 14px)` on `.input-container`, to come\n     out square at any text size, and must follow it if that changes. `aspect-ratio: 1` cannot do this: a flex item's main size is resolved from\n     its content before the stretched cross size exists. */\n  align-self: stretch;\n  width: calc(1.25em + 14px);\n  touch-action: manipulation;\n  user-select: none;\n  -webkit-user-select: none;\n  -webkit-touch-callout: none;\n  transition: opacity 0.15s;\n\n  &:disabled {\n    opacity: 0.35;\n  }\n\n  &:focus-visible {\n    outline: 2px solid var(--focus-border-color, var(--primary-color));\n    outline-offset: 2px;\n  }\n}\n\n/* One continuous border, so the buttons follow the field's focus and error colours — left grey,\n   the group reads as a box inside a box. */\n.split:focus-within .splitBtn {\n  border-color: var(--focus-border-color, var(--primary-color));\n}\n\n.split-invalid .splitBtn {\n  border-color: var(--error-border-color);\n}\n","import { useCallback, useEffect, useRef } from 'react';\n\n/** Time a button must be held before the value starts repeating. */\nconst INITIAL_DELAY_MS = 400;\n/** Interval between repeats right after the hold kicks in. */\nconst SLOW_INTERVAL_MS = 120;\n/** Interval the repeat accelerates to once the hold has been going a while. */\nconst FAST_INTERVAL_MS = 50;\n/** How long the slow phase lasts before switching to `FAST_INTERVAL_MS`. */\nconst ACCELERATE_AFTER_MS = 1000;\n\nexport interface UseStepRepeatResult {\n  /** Fires one step immediately, then starts the hold-to-repeat timer. */\n  start: (direction: 1 | -1, multiplier: number) => void;\n  /** Cancels a running repeat. Safe to call when nothing is running. */\n  stop: () => void;\n}\n\n/**\n * Hold-to-repeat for the stepper buttons: one step on press, then an accelerating repeat.\n *\n * The release is watched on `window` rather than by capturing the pointer, which would retarget\n * the following click past the node Button's ripple listens on. Capture would have guaranteed a\n * `pointerup`, so the extra listeners cover the ways a press can end without one.\n *\n * `onStep` returns whether anything changed; `false` ends the repeat, so a hold at a bound stops.\n */\nexport function useStepRepeat(onStep: (delta: number) => boolean): UseStepRepeatResult {\n  const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n  const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);\n  // Read at repeat time, so a repeat always steps from the latest value.\n  const onStepRef = useRef(onStep);\n  onStepRef.current = onStep;\n\n  // One stable reference to attach and detach with; `stop` and `onEndEvent` also refer to each\n  // other, so both go through the ref.\n  const handlersRef = useRef<{ stop: () => void; onEndEvent: (e: Event) => void }>(null!);\n\n  const stop = useCallback(() => {\n    if (timeoutRef.current !== null) {\n      clearTimeout(timeoutRef.current);\n      timeoutRef.current = null;\n    }\n    if (intervalRef.current !== null) {\n      clearInterval(intervalRef.current);\n      intervalRef.current = null;\n    }\n    if (typeof window === 'undefined') return;\n    const { onEndEvent } = handlersRef.current;\n    window.removeEventListener('pointerup', onEndEvent);\n    window.removeEventListener('pointercancel', onEndEvent);\n    window.removeEventListener('pointermove', onEndEvent);\n    window.removeEventListener('blur', onEndEvent);\n    document.removeEventListener('visibilitychange', onEndEvent);\n  }, []);\n\n  handlersRef.current = {\n    stop,\n    onEndEvent: (e: Event) => {\n      // A move ends the hold only if nothing is pressed — how a mouse released outside the\n      // window reports itself on re-entry.\n      if (e.type === 'pointermove' && (e as PointerEvent).buttons !== 0) return;\n      if (e.type === 'visibilitychange' && !document.hidden) return;\n      handlersRef.current.stop();\n    },\n  };\n\n  const start = useCallback(\n    (direction: 1 | -1, multiplier: number) => {\n      stop();\n      const { onEndEvent } = handlersRef.current;\n      window.addEventListener('pointerup', onEndEvent);\n      window.addEventListener('pointercancel', onEndEvent);\n      window.addEventListener('pointermove', onEndEvent);\n      window.addEventListener('blur', onEndEvent);\n      document.addEventListener('visibilitychange', onEndEvent);\n\n      const fire = () => {\n        if (!onStepRef.current(direction * multiplier)) stop();\n      };\n      fire();\n\n      timeoutRef.current = setTimeout(() => {\n        timeoutRef.current = null;\n        const startedAt = Date.now();\n        let isFast = false;\n        const tick = () => {\n          fire();\n          if (intervalRef.current === null) return;\n          if (!isFast && Date.now() - startedAt >= ACCELERATE_AFTER_MS) {\n            isFast = true;\n            if (intervalRef.current !== null) clearInterval(intervalRef.current);\n            intervalRef.current = setInterval(tick, FAST_INTERVAL_MS);\n          }\n        };\n        intervalRef.current = setInterval(tick, SLOW_INTERVAL_MS);\n      }, INITIAL_DELAY_MS);\n    },\n    [stop],\n  );\n\n  useEffect(() => stop, [stop]);\n\n  return { start, stop };\n}\n\nexport default useStepRepeat;\n","import type { CSSProperties, ReactElement } from 'react';\nimport React from 'react';\nimport cx from 'classnames';\nimport { NumericFormat } from 'react-number-format';\nimport type { FormValidator } from 'services/formValidation';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faChevronUp, faChevronDown, faMinus, faPlus } from '@fortawesome/free-solid-svg-icons';\n\nimport styles from './numberInput.module.pcss';\nimport Input from '../input';\nimport Button from '../button';\nimport ButtonGroup from '../buttonGroup';\nimport { ButtonSize } from '../button/button.types';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport { useCoarsePointer } from '../hooks/useCoarsePointer';\nimport { useStepRepeat } from './useStepRepeat';\nimport type { NumberInputLabels, NumberInputProps } from './numberInput.types';\nimport { ThemeColor } from '../themeProvider/color.types';\n\nexport type { NumberInputProps, NumberInputArrowsLayout } from './numberInput.types';\n\ninterface StepButtonProps {\n  label: string;\n  className?: string;\n  style?: CSSProperties;\n  icon: typeof faPlus;\n  disabled: boolean;\n  direction: 1 | -1;\n  onStart: (direction: 1 | -1, multiplier: number) => void;\n  onStop: () => void;\n}\n\n/** Press-and-hold behaviour shared by both stepper layouts. */\nconst DEFAULT_LABELS: Required<NumberInputLabels> = {\n  increase: 'Increase',\n  decrease: 'Decrease',\n};\n\nfunction stepButtonProps({ label, disabled, direction, onStart, onStop }: StepButtonProps) {\n  return {\n    // The input keeps the tab stop — it carries role=\"spinbutton\", which announces the value\n    // and the arrow-key affordance, so two more tab stops per field would be noise.\n    tabIndex: -1,\n    disabled,\n    'aria-label': label,\n    // pointerdown (not click) keeps focus in the field and responds immediately on touch.\n    // Deliberately no setPointerCapture: capturing retargets the following click to the captured\n    // element, past the node Button's ripple listens on. useStepRepeat watches the window for\n    // the release instead.\n    onPointerDown: (e: React.PointerEvent<HTMLElement>) => {\n      if (e.button !== 0 && e.pointerType === 'mouse') return;\n      e.preventDefault();\n      onStart(direction, e.shiftKey ? 10 : 1);\n    },\n    onPointerUp: onStop,\n    onPointerCancel: onStop,\n    onContextMenu: (e: React.SyntheticEvent) => e.preventDefault(),\n  };\n}\n\n/** Stacked-layout chevron: borderless, sits inside the field's border. */\nfunction ChevronStepButton(props: StepButtonProps): ReactElement {\n  return (\n    <button\n      type='button'\n      {...stepButtonProps(props)}\n      className={cx(styles.arrowBtn, props.className)}\n      style={props.style}\n    >\n      <FontAwesomeIcon icon={props.icon} aria-hidden='true' />\n    </button>\n  );\n}\n\n/** Split-layout stepper: a standalone control, so a real Button. */\nfunction SplitStepButton(props: StepButtonProps): ReactElement {\n  const { disabled, ...handlers } = stepButtonProps(props);\n  return (\n    <Button\n      {...handlers}\n      className={cx(styles.splitBtn, props.className)}\n      style={props.style}\n      color={ThemeColor.Neutral}\n      size={ButtonSize.Icon}\n      disabled={disabled}\n      icon={props.icon}\n    />\n  );\n}\n\nfunction NumberInput(props: NumberInputProps): ReactElement {\n  const fvRef = React.useRef<FormValidator | null | undefined>(undefined);\n  const {\n    value,\n    onChange,\n    formValidator,\n    label,\n    error,\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    allowNegative = false,\n    decimalSeparator,\n    thousandSeparator,\n    prefix,\n    suffix,\n    decimalScale,\n    fixedDecimalScale = true,\n    placeholder,\n    sign,\n    showArrows = false,\n    step = 1,\n    min,\n    max,\n    arrowsLayout = 'auto',\n    labels,\n    readOnly = false,\n    disabled = false,\n    isRequired = false,\n    onKeyDown: onKeyDownProp,\n    ref,\n    ...rest\n  } = useComponentDefaults('NumberInput', props);\n  const text = useLabels('NumberInput', labels, DEFAULT_LABELS);\n  const [, forceUpdate] = React.useState(0);\n  React.useEffect(() => {\n    fvRef.current = formValidator;\n    if (!formValidator) return;\n    const listener = () => forceUpdate((n) => n + 1);\n    formValidator.registerOnUpdateListener(listener);\n    return () => {\n      formValidator.removeOnUpdateListener(listener);\n    };\n  }, [formValidator]);\n  const computedIsRequired =\n    isRequired || !!formValidator?.validators.find((v) => (v as { validatorId?: string }).validatorId === 'required');\n  const rawValue = formValidator != null ? (formValidator.value as number | string) : value;\n  // When a sign is forced, display the absolute value — the prefix character carries the sign visually.\n  const displayValue = sign != null && rawValue != null ? Math.abs(Number(rawValue)) : rawValue;\n  const controlledValue = displayValue != null ? String(displayValue) : '';\n\n  function handleValueChange(floatValue: number | undefined, wasUser = true) {\n    let out = floatValue;\n    if (out !== undefined) {\n      if (sign === '-') out = -Math.abs(out);\n      if (sign === '+') out = Math.abs(out);\n    }\n    if (formValidator != null) formValidator.set(out ?? null, wasUser);\n    onChange?.(out);\n  }\n\n  function clamp(next: number): number {\n    let out = next;\n    if (!allowNegative && sign !== '-') out = Math.max(0, out);\n    if (sign === '-') out = Math.min(0, out);\n    if (min !== undefined) out = Math.max(min, out);\n    if (max !== undefined) out = Math.min(max, out);\n    return out;\n  }\n\n  const currentNumber = rawValue != null && rawValue !== '' ? Number(rawValue) : 0;\n\n  /**\n   * Called on blur and Enter, never per keystroke: rejecting an out-of-bounds edit as it is\n   * typed swallows keystrokes on their way somewhere valid (with `max: 10`, typing `1` in front\n   * of `8` to reach `18`). An empty field is skipped so a `min` is never typed in for the user.\n   */\n  function clampToBounds() {\n    if (readOnly || disabled) return;\n    if (rawValue == null || rawValue === '') return;\n    const n = Number(rawValue);\n    if (!Number.isFinite(n)) return;\n    const next = clamp(n);\n    if (next !== n) handleValueChange(next);\n  }\n\n  /** Returns whether the value moved, which is how useStepRepeat knows to end a hold at a bound. */\n  function handleStep(delta: number): boolean {\n    if (readOnly || disabled) return false;\n    const next = clamp(currentNumber + delta);\n    if (next === currentNumber) return false;\n    handleValueChange(next);\n    return true;\n  }\n\n  const { start: startRepeat, stop: stopRepeat } = useStepRepeat((delta) => handleStep(delta * step));\n\n  function handleKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {\n    // Enter submits the surrounding form without ever blurring, so the blur clamp would not run.\n    // Before the stepper guard, since the bounds apply with or without steppers.\n    if (e.key === 'Enter') {\n      clampToBounds();\n      onKeyDownProp?.(e);\n      return;\n    }\n    if (!showStepper) {\n      onKeyDownProp?.(e);\n      return;\n    }\n    if (e.key === 'ArrowUp') {\n      e.preventDefault();\n      handleStep(e.shiftKey ? step * 10 : step);\n      return;\n    }\n    if (e.key === 'ArrowDown') {\n      e.preventDefault();\n      handleStep(e.shiftKey ? -step * 10 : -step);\n      return;\n    }\n    onKeyDownProp?.(e);\n  }\n\n  const resolvedPrefix = sign === '-' ? '-' : sign === '+' ? '+' : prefix;\n\n  const isCoarsePointer = useCoarsePointer();\n  const showStepper = showArrows && !readOnly && !disabled;\n  const isSplit = arrowsLayout === 'split' || (arrowsLayout === 'auto' && isCoarsePointer);\n\n  const canIncrease = clamp(currentNumber + step) !== currentNumber;\n  const canDecrease = clamp(currentNumber - step) !== currentNumber;\n\n  const stepperProps = { onStart: startRepeat, onStop: stopRepeat };\n\n  const stackedArrows =\n    showStepper && !isSplit ? (\n      <div className={cx(styles.arrows, classNames?.arrows)} style={slotStyles?.arrows}>\n        <ChevronStepButton\n          className={classNames?.increaseButton}\n          style={slotStyles?.increaseButton}\n          label={text.increase}\n          icon={faChevronUp}\n          disabled={!canIncrease}\n          direction={1}\n          {...stepperProps}\n        />\n        <ChevronStepButton\n          className={classNames?.decreaseButton}\n          style={slotStyles?.decreaseButton}\n          label={text.decrease}\n          icon={faChevronDown}\n          disabled={!canDecrease}\n          direction={-1}\n          {...stepperProps}\n        />\n      </div>\n    ) : null;\n\n  // aria-* is spread onto NumericFormat, which forwards unknown props to the DOM input.\n  // Input's own cloneElement doesn't set these keys, so they survive the clone.\n  const spinButtonProps = showStepper\n    ? {\n        role: 'spinbutton',\n        'aria-valuenow': Number.isFinite(currentNumber) ? currentNumber : undefined,\n        'aria-valuemin': min,\n        'aria-valuemax': max,\n        'aria-valuetext': controlledValue || undefined,\n      }\n    : {};\n\n  // Shared with the split group, whose buttons have to mirror the field's invalid state — they\n  // form one continuous border.\n  const isValid = formValidator ? !formValidator.shouldShowError : !error;\n\n  // In the split layout the ButtonGroup is the root, so the consumer's className and style\n  // belong there rather than on the Input.\n  const isSplitGroup = showStepper && isSplit;\n\n  const input = (\n    <Input\n      label={label}\n      className={cx(\n        styles.numberInput,\n        { [styles.numberInputSplit]: isSplitGroup },\n        // In split mode the ButtonGroup around all three is the root, so the\n        // consumer's root class belongs there rather than on the field.\n        isSplitGroup ? undefined : className,\n        isSplitGroup ? undefined : classNames?.root,\n        classNames?.input,\n      )}\n      style={{\n        ...(isSplitGroup ? undefined : style),\n        ...(isSplitGroup ? undefined : slotStyles?.root),\n        ...slotStyles?.input,\n      }}\n      value={controlledValue}\n      onChange={() => {}}\n      formValidator={null}\n      isRequired={computedIsRequired}\n      onBlur={() => {\n        clampToBounds();\n        if (fvRef.current) fvRef.current.touched = true;\n      }}\n      isValid={isValid}\n      errorMessage={\n        formValidator\n          ? formValidator.shouldShowError\n            ? (formValidator.getCurrentErrorMessage() ?? '')\n            : ''\n          : (error ?? '')\n      }\n      placeholder={placeholder}\n      readOnly={readOnly}\n      disabled={disabled}\n      suffix={stackedArrows}\n      // Input centres its suffix wrapper; this lets the chevrons claim the field's full height.\n      classNames={stackedArrows ? { suffix: styles.arrowSuffix } : undefined}\n      inputMode={decimalScale === 0 ? 'numeric' : 'decimal'}\n      customInput={\n        <NumericFormat\n          // NumericFormat also reports a value set from outside (fv.set(), group.reset()) once it has\n          // reformatted it; that must not mark the field dirty. A string, because its `SourceType`\n          // enum exists only in the type declarations and is undefined at runtime.\n          onValueChange={(vals, { source }) => handleValueChange(vals.floatValue, source === 'event')}\n          allowNegative={sign != null ? false : allowNegative}\n          decimalSeparator={decimalSeparator}\n          thousandSeparator={thousandSeparator}\n          decimalScale={decimalScale}\n          fixedDecimalScale={fixedDecimalScale}\n          prefix={resolvedPrefix}\n          suffix={suffix}\n          onKeyDown={handleKeyDown}\n          // react-number-format owns the <input>, so its ref is reachable only through this prop.\n          getInputRef={ref}\n          {...spinButtonProps}\n          {...rest}\n        />\n      }\n    />\n  );\n\n  if (!isSplitGroup) return input;\n\n  return (\n    // ButtonGroup collapses the doubled borders and flattens the inner corners, so the three\n    // read as one control.\n    <ButtonGroup\n      className={cx(styles.split, { [styles.splitInvalid]: !isValid }, className, classNames?.root)}\n      style={{ ...style, ...slotStyles?.root }}\n    >\n      <SplitStepButton\n        className={classNames?.decreaseButton}\n        style={slotStyles?.decreaseButton}\n        label={text.decrease}\n        icon={faMinus}\n        disabled={!canDecrease}\n        direction={-1}\n        {...stepperProps}\n      />\n      {input}\n      <SplitStepButton\n        className={classNames?.increaseButton}\n        style={slotStyles?.increaseButton}\n        label={text.increase}\n        icon={faPlus}\n        disabled={!canIncrease}\n        direction={1}\n        {...stepperProps}\n      />\n    </ButtonGroup>\n  );\n}\n\nexport default NumberInput;\n","import NumberInput from './numberInput';\n\nexport type { NumberInputProps, NumberInputArrowsLayout } from './numberInput.types';\nexport default NumberInput;\n","import type React from 'react';\nimport type { FormValidator } from 'services/formValidation';\nimport type { SlotClassNames, SlotStyles, HtmlProps } from '../types/slots.types';\n\nexport type OtpInputSlots = 'root' | 'label' | 'inputs' | 'input' | 'separator';\n\nexport enum OtpInputCharset {\n  /** Digits only (0-9). Shows a numeric mobile keyboard. */\n  Numeric = 'numeric',\n  /** Letters and digits. */\n  Alphanumeric = 'alphanumeric',\n}\n\n/** Text OtpInput renders of its own, for translating it. */\nexport interface OtpInputLabels {\n  /** The group of boxes, used when no `label` is given. */\n  field?: string;\n  /** One box, given its 1-based position and how many there are. */\n  digit?: (position: number, length: number) => string;\n}\n\nexport interface OtpInputProps extends Omit<HtmlProps, 'onChange'> {\n  /** Number of character boxes. Default 6. */\n  length?: number;\n  /** Current code as a plain string, e.g. \"123456\". */\n  value?: string;\n  /** Fired on every change with the full current code (may be shorter than `length`). */\n  onChange?: ((value: string) => void) | null;\n  /** Fired once, when the code reaches `length` characters. */\n  onComplete?: ((value: string) => void) | null;\n  formValidator?: FormValidator | null;\n\n  /** Restricts which characters can be typed and picks the mobile keyboard. Default `numeric`. */\n  charset?: OtpInputCharset;\n  /** Render each box's value as a dot, like a password field. */\n  masked?: boolean;\n  /** Draw a small gap after these 1-based box indices, e.g. `[3]` to split 6 boxes into 3+3. */\n  groupAfter?: number[];\n\n  label?: string;\n  isValid?: boolean;\n  /** Shown in an error Tooltip on focus/hover once `isValid` is `false` (or, with a\n   * `formValidator`, once its `shouldShowError` is true). Persists until the value changes\n   * `isValid`/`formValidator`'s error state itself — see `clearOnError` to auto-dismiss it. */\n  errorMessage?: string;\n  isRequired?: boolean;\n  /** When an error newly appears (`isValid`/`formValidator` turning invalid), clear every box and\n   * refocus the first one after `clearErrorDelay`, so the next attempt starts clean instead of the\n   * user editing individual wrong digits (and re-triggering `onComplete` on every keystroke while\n   * the code stays at full length). Default `false`. */\n  clearOnError?: boolean;\n  /** Delay in ms before `clearOnError` clears the boxes, so the wrong code and error message are\n   * still visible for a moment first. Default `900`. */\n  clearErrorDelay?: number;\n  disabled?: boolean;\n  /** Focuses the first empty box (or the first box) on mount. */\n  autoFocus?: boolean;\n\n  className?: string;\n  style?: React.CSSProperties;\n  /** Ref to the root element. */\n  ref?: React.Ref<HTMLDivElement>;\n  classNames?: SlotClassNames<OtpInputSlots>;\n  styles?: SlotStyles<OtpInputSlots>;\n  /** Overrides the text OtpInput renders of its own. Merged per key. */\n  labels?: OtpInputLabels;\n}\n","@keyframes otpInputShake {\n  10%,\n  90% {\n    transform: translateX(-1px);\n  }\n\n  20%,\n  80% {\n    transform: translateX(2px);\n  }\n\n  30%,\n  50%,\n  70% {\n    transform: translateX(-4px);\n  }\n\n  40%,\n  60% {\n    transform: translateX(4px);\n  }\n}\n\n.otpInput {\n  display: inline-flex;\n  flex-direction: column;\n  row-gap: 0.5em;\n\n  /* With the min-widths below, the boxes shrink into a container narrower than their full width\n     instead of running out of it. */\n  max-width: 100%;\n\n  &-label {\n    font-size: var(--default-font-size);\n    color: var(--text-color);\n  }\n\n  &-requiredMark {\n    color: var(--required-mark-color, var(--error-color));\n  }\n\n  /* As wide as the boxes even when the root stretches, so the error tooltip anchored here points\n     at them. */\n  &-boxes {\n    display: flex;\n    align-self: flex-start;\n    align-items: center;\n    max-width: 100%;\n    column-gap: 0.5em;\n\n    &-shake {\n      animation: otpInputShake 0.4s ease-in-out;\n\n      @media (prefers-reduced-motion: reduce) {\n        animation: none;\n      }\n    }\n  }\n\n  &-boxWrapper {\n    display: flex;\n    align-items: center;\n    column-gap: 0.5em;\n    min-width: 0;\n  }\n\n  /* These em are against this box's OWN 1.25em font, not the root's: 40px and 48px of 20px. */\n  &-box {\n    width: 2em;\n    min-width: 1.4em;\n    height: 2.4em;\n    text-align: center;\n    font-size: 1.25em;\n    font-weight: 600;\n    font-family: var(--themed-font, inherit);\n    color: var(--text-color);\n    background: var(--input-background, var(--background));\n    border: 0.075em solid var(--border-color);\n    border-radius: var(--default-border-radius);\n    outline: none;\n    transition:\n      border-color 0.15s,\n      box-shadow 0.15s;\n\n    &:focus {\n      border-color: var(--focus-border-color, var(--primary-color));\n      box-shadow: 0 0 0 3px color-mix(in srgb, var(--focus-border-color, var(--primary-color)) 20%, transparent);\n    }\n\n    &-filled {\n      border-color: var(--primary-lighter, var(--border-color));\n    }\n\n    &-error {\n      border-color: var(--error-color);\n\n      &:focus {\n        border-color: var(--error-color);\n        box-shadow: 0 0 0 3px color-mix(in srgb, var(--error-color) 20%, transparent);\n      }\n    }\n\n    &:disabled {\n      color: var(--disabled-font-color);\n      background: var(--disabled-input-color);\n      cursor: not-allowed;\n    }\n  }\n\n  &-separator {\n    width: 0.625em;\n    height: calc(1.5em / 16);\n    background: var(--border-color);\n    flex-shrink: 0;\n  }\n\n  &-disabled &-label {\n    color: var(--disabled-font-color);\n  }\n}\n","import { useEffect, useRef, useState, type ReactElement } from 'react';\nimport cx from 'classnames';\nimport Tooltip from 'components/common/tooltip';\nimport { useComponentDefaults, useLabels } from '../configProvider';\n\nimport { OtpInputCharset, type OtpInputLabels, type OtpInputProps } from './otpInput.types';\nimport styles from './otpInput.module.pcss';\n\nconst NUMERIC_RE = /[0-9]/g;\nconst ALPHANUMERIC_RE = /[a-zA-Z0-9]/g;\n\nconst DEFAULT_LABELS: Required<OtpInputLabels> = {\n  field: 'One-time code',\n  digit: (position, length) => `Digit ${position} of ${length}`,\n};\n\nfunction filterChars(raw: string, charset: OtpInputCharset): string {\n  const re = charset === OtpInputCharset.Alphanumeric ? ALPHANUMERIC_RE : NUMERIC_RE;\n  return (raw.match(re) || []).join('');\n}\n\nfunction OtpInput(props: OtpInputProps): ReactElement {\n  const {\n    length = 6,\n    value,\n    onChange = () => {},\n    onComplete = () => {},\n    formValidator = null,\n    charset = OtpInputCharset.Numeric,\n    masked = false,\n    groupAfter = [],\n    label = '',\n    isValid = true,\n    errorMessage = '',\n    isRequired = false,\n    clearOnError = false,\n    clearErrorDelay = 900,\n    disabled = false,\n    autoFocus = false,\n    className = '',\n    style,\n    classNames,\n    styles: slotStyles,\n    labels,\n    ...rest\n  } = useComponentDefaults('OtpInput', props);\n  const text = useLabels('OtpInput', labels, DEFAULT_LABELS);\n\n  const currentValue = (formValidator ? (formValidator.value as string) : value) ?? '';\n  const [, forceUpdate] = useState(0);\n  const [isShaking, setIsShaking] = useState(false);\n  const [isFocused, setIsFocused] = useState(false);\n  const [isHovered, setIsHovered] = useState(false);\n  const [pendingClear, setPendingClear] = useState(false);\n  const [wasError, setWasError] = useState(false);\n  const inputRefs = useRef<Array<HTMLInputElement | null>>([]);\n\n  useEffect(() => {\n    if (!formValidator) return;\n    formValidator.validate();\n    const listener = () => forceUpdate((n) => n + 1);\n    formValidator.registerOnUpdateListener(listener);\n    return () => formValidator.removeOnUpdateListener(listener);\n  }, [formValidator]);\n\n  useEffect(() => {\n    if (!autoFocus) return;\n    const firstEmpty = Math.min(currentValue.length, length - 1);\n    inputRefs.current[firstEmpty]?.focus();\n    // Only run once, on mount.\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, []);\n\n  const showError = formValidator ? formValidator.shouldShowError : !isValid || !!errorMessage;\n  const displayedErrorMessage = formValidator ? formValidator.getCurrentErrorMessage() : errorMessage || '';\n\n  // State set during render, not a ref read in an effect: the ref survives a remount (StrictMode,\n  // <Activity>), so the re-run saw no new error and dropped the pending clear.\n  if (showError !== wasError) {\n    setWasError(showError);\n    if (showError) setIsShaking(true);\n    setPendingClear(showError && clearOnError);\n  }\n\n  useEffect(() => {\n    if (!isShaking) return;\n    const id = window.setTimeout(() => setIsShaking(false), 400);\n    return () => window.clearTimeout(id);\n  }, [isShaking]);\n\n  useEffect(() => {\n    if (!pendingClear || !clearOnError) return;\n    const id = window.setTimeout(() => {\n      setPendingClear(false);\n      commit('');\n      inputRefs.current[0]?.focus();\n    }, clearErrorDelay);\n    return () => window.clearTimeout(id);\n    // commit is recreated every render; listing it would restart the delay on each keystroke.\n    // oxlint-disable-next-line react-hooks/exhaustive-deps\n  }, [pendingClear, clearOnError, clearErrorDelay]);\n\n  function commit(next: string) {\n    const wasComplete = currentValue.length === length;\n    const clipped = filterChars(next, charset).slice(0, length);\n    if (formValidator) {\n      formValidator.set(clipped);\n    }\n    onChange?.(clipped);\n    // Only fire once per genuinely new completion (going from partial to full), not on every\n    // keystroke made while correcting an already-full code.\n    if (clipped.length === length && !wasComplete) {\n      onComplete?.(clipped);\n    }\n  }\n\n  function setCharAt(index: number, char: string) {\n    const chars = currentValue.split('');\n    chars[index] = char;\n    commit(chars.join(''));\n  }\n\n  function handleChange(index: number, rawInput: string) {\n    if (disabled) return;\n    if (rawInput === '') {\n      // Box was cleared (e.g. select + delete) — Backspace itself is handled in onKeyDown.\n      setCharAt(index, '');\n      return;\n    }\n\n    const filtered = filterChars(rawInput, charset);\n    if (!filtered) {\n      // Typed an invalid/disallowed character — the controlled `value` re-render discards it.\n      return;\n    }\n\n    // Clicking straight into a box past the contiguous filled prefix (skipping ahead of\n    // empty boxes) fills the next expected slot instead, rather than leaving a gap before it\n    // that setCharAt/distributeFrom can't represent in a plain string and would silently collapse.\n    const targetIndex = Math.min(index, currentValue.length);\n\n    if (rawInput.length > 1) {\n      // Mobile autofill / SMS suggestions can drop the whole code into one box, bypassing maxLength.\n      distributeFrom(targetIndex, filtered);\n      return;\n    }\n\n    setCharAt(targetIndex, filtered);\n    if (targetIndex < length - 1) {\n      inputRefs.current[targetIndex + 1]?.focus();\n    } else {\n      inputRefs.current[targetIndex]?.blur();\n    }\n  }\n\n  function distributeFrom(startIndex: number, chars: string) {\n    const clean = filterChars(chars, charset);\n    if (!clean) return;\n    const existing = currentValue.padEnd(length, ' ').split('');\n    let cursor = startIndex;\n    for (const char of clean) {\n      if (cursor >= length) break;\n      existing[cursor] = char;\n      cursor += 1;\n    }\n    commit(existing.join('').trimEnd());\n    const nextFocus = Math.min(cursor, length - 1);\n    inputRefs.current[nextFocus]?.focus();\n    if (cursor >= length) {\n      inputRefs.current[length - 1]?.blur();\n    }\n  }\n\n  function handlePaste(index: number, event: React.ClipboardEvent<HTMLInputElement>) {\n    if (disabled) return;\n    const pasted = event.clipboardData.getData('text');\n    if (!pasted) return;\n    event.preventDefault();\n    // Same clamp as handleChange — pasting into a box past the filled prefix starts at the\n    // next expected slot instead of leaving an unrepresentable gap before it.\n    distributeFrom(Math.min(index, currentValue.length), pasted);\n  }\n\n  function handleKeyDown(index: number, event: React.KeyboardEvent<HTMLInputElement>) {\n    if (disabled) return;\n\n    // React's controlled-input onChange never fires when the resulting value is identical to\n    // what's already rendered (its value tracker sees no change) — so retyping the exact digit\n    // already in a box would otherwise silently do nothing. keydown always fires, so handle that\n    // one case here and skip the native edit entirely.\n    if (event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey) {\n      const typed = filterChars(event.key, charset);\n      if (typed && currentValue[index] === typed) {\n        event.preventDefault();\n        setCharAt(index, typed);\n        if (index < length - 1) {\n          inputRefs.current[index + 1]?.focus();\n        } else {\n          inputRefs.current[index]?.blur();\n        }\n        return;\n      }\n    }\n\n    if (event.key === 'Backspace') {\n      if (currentValue[index]) {\n        setCharAt(index, '');\n        return;\n      }\n      if (index > 0) {\n        event.preventDefault();\n        setCharAt(index - 1, '');\n        inputRefs.current[index - 1]?.focus();\n      }\n      return;\n    }\n    if (event.key === 'Delete') {\n      if (currentValue[index]) setCharAt(index, '');\n      return;\n    }\n    if (event.key === 'ArrowLeft' && index > 0) {\n      event.preventDefault();\n      inputRefs.current[index - 1]?.focus();\n      return;\n    }\n    if (event.key === 'ArrowRight' && index < length - 1) {\n      event.preventDefault();\n      inputRefs.current[index + 1]?.focus();\n    }\n  }\n\n  function handleFocus(event: React.FocusEvent<HTMLInputElement>) {\n    event.target.select();\n  }\n\n  function handleGroupFocus() {\n    setIsFocused(true);\n  }\n\n  function handleGroupBlur(event: React.FocusEvent<HTMLDivElement>) {\n    // Auto-advance moves focus between boxes constantly while typing — only treat this as\n    // leaving the group once focus actually lands outside it, not on the next box.\n    if (event.currentTarget.contains(event.relatedTarget as Node | null)) return;\n    setIsFocused(false);\n    if (formValidator) formValidator.touched = true;\n  }\n\n  return (\n    <div {...rest} className={cx(styles.otpInput, disabled && styles.otpInputDisabled, className)} style={style}>\n      {label && (\n        <div className={cx(styles.otpInputLabel, classNames?.label)} style={slotStyles?.label}>\n          {label}\n          {isRequired && <span className={styles.otpInputRequiredMark}> *</span>}\n        </div>\n      )}\n      <div\n        role='group'\n        aria-label={label || text.field}\n        className={cx(styles.otpInputBoxes, isShaking && styles.otpInputBoxesShake, classNames?.inputs)}\n        style={slotStyles?.inputs}\n        onFocus={handleGroupFocus}\n        onBlur={handleGroupBlur}\n        onMouseEnter={() => setIsHovered(true)}\n        onMouseLeave={() => setIsHovered(false)}\n      >\n        {Array.from({ length }, (_, index) => {\n          const char = currentValue[index] || '';\n          return (\n            <div key={index} className={styles.otpInputBoxWrapper}>\n              <input\n                ref={(el) => {\n                  inputRefs.current[index] = el;\n                }}\n                type={masked ? 'password' : 'text'}\n                inputMode={charset === OtpInputCharset.Numeric ? 'numeric' : 'text'}\n                pattern={charset === OtpInputCharset.Numeric ? '[0-9]*' : undefined}\n                autoComplete={index === 0 ? 'one-time-code' : 'off'}\n                maxLength={1}\n                disabled={disabled}\n                value={char}\n                aria-label={text.digit(index + 1, length)}\n                aria-invalid={showError || undefined}\n                className={cx(\n                  styles.otpInputBox,\n                  showError && styles.otpInputBoxError,\n                  char && styles.otpInputBoxFilled,\n                  classNames?.input,\n                )}\n                style={slotStyles?.input}\n                onChange={(e) => handleChange(index, e.target.value)}\n                onKeyDown={(e) => handleKeyDown(index, e)}\n                onPaste={(e) => handlePaste(index, e)}\n                onFocus={handleFocus}\n              />\n              {groupAfter.includes(index + 1) && index < length - 1 && (\n                <span className={cx(styles.otpInputSeparator, classNames?.separator)} style={slotStyles?.separator} />\n              )}\n            </div>\n          );\n        })}\n        {/* Inside the boxes, which the tooltip anchors to: the root stretches in a flex column,\n            and anchored to it the tooltip pointed at the empty space beside the last box. */}\n        <Tooltip variant='error' message={displayedErrorMessage} isVisible={showError && (isFocused || isHovered)} />\n      </div>\n    </div>\n  );\n}\n\nexport default OtpInput;\n","import { useState, useEffect } from 'react';\n\nconst MOBILE_BREAKPOINT = 768;\n\nexport interface WindowSize {\n  width: number;\n  height: number;\n  isMobileSize: boolean;\n}\n\nexport default function useWindowSize(): WindowSize {\n  const [size, setSize] = useState<WindowSize>(() => ({\n    width: window.innerWidth,\n    height: window.innerHeight,\n    isMobileSize: window.innerWidth < MOBILE_BREAKPOINT,\n  }));\n\n  useEffect(() => {\n    function onResize() {\n      setSize({\n        width: window.innerWidth,\n        height: window.innerHeight,\n        isMobileSize: window.innerWidth < MOBILE_BREAKPOINT,\n      });\n    }\n    window.addEventListener('resize', onResize);\n    return () => window.removeEventListener('resize', onResize);\n  }, []);\n\n  return size;\n}\n",".overscroll {\n  height: 100vh;\n  overflow: auto;\n  -ms-overflow-style: none; /* IE and Edge */\n  scrollbar-width: none; /* Firefox */\n\n  &::-webkit-scrollbar {\n    display: none;\n  }\n\n  &-header {\n    position: absolute;\n    bottom: 0;\n    width: 100%;\n\n    &-centered {\n      display: flex;\n      justify-content: center;\n      align-items: center;\n    }\n\n    &-container {\n      position: relative;\n    }\n  }\n}\n","import { useRef, useEffect, useCallback } from 'react';\nimport cx from 'classnames';\nimport type { UIEvent } from 'react';\nimport useWindowSize from '../../hooks/useWindowSize';\nimport styles from './overscroll.module.pcss';\nimport type { OverscrollProps } from './overscroll.types';\n\nexport default function Overscroll({\n  children = null,\n  overscrollContent = null,\n  overscrollContentDesktop = null,\n  centerHeader = true,\n  min = 10,\n  max = 80,\n  start = 30,\n  unit = '%',\n  mobileOnly = true,\n  onScrollChange = () => {},\n  scrollDisabled = false,\n}: OverscrollProps) {\n  const { isMobileSize, height } = useWindowSize();\n\n  const overscrollRef = useRef<HTMLDivElement>(null);\n  const overscrollHeaderRef = useRef<HTMLDivElement>(null);\n  const overscrollHeaderContainerRef = useRef<HTMLDivElement>(null);\n  const isScrollingRef = useRef(false);\n  const scrollTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n  const onScrollChangeRef = useRef(onScrollChange);\n  useEffect(() => {\n    onScrollChangeRef.current = onScrollChange;\n  });\n\n  const updateHeaderHeight = useCallback(\n    (scrollTop: number) => {\n      let h: number;\n      if (unit === '%') {\n        if (!overscrollHeaderContainerRef.current) return;\n        const containerH = overscrollHeaderContainerRef.current.clientHeight;\n        h = Math.max((containerH * min) / 100, containerH - scrollTop);\n      } else {\n        h = Math.max(min, max - scrollTop);\n      }\n      if (overscrollHeaderRef.current) {\n        overscrollHeaderRef.current.style.height = `${h}px`;\n      }\n    },\n    [min, max, unit],\n  );\n\n  const scrollToInitialPosition = useCallback(() => {\n    let toScroll: number;\n    if (unit === '%') {\n      if (!overscrollHeaderContainerRef.current) return;\n      const hundredPerc = (overscrollHeaderContainerRef.current.clientHeight / max) * 100;\n      toScroll = overscrollHeaderContainerRef.current.clientHeight - hundredPerc * (start / 100);\n    } else {\n      toScroll = max - start;\n    }\n    if (overscrollRef.current) {\n      overscrollRef.current.scrollTop = toScroll;\n      updateHeaderHeight(toScroll);\n    }\n  }, [max, start, unit, updateHeaderHeight]);\n\n  // Initial scroll position on mount (and if scroll-related props change)\n  useEffect(() => {\n    const id = setTimeout(scrollToInitialPosition, 100);\n    return () => clearTimeout(id);\n  }, [scrollToInitialPosition]);\n\n  // Toggle overflow when scrollDisabled changes\n  useEffect(() => {\n    if (overscrollRef.current) {\n      overscrollRef.current.style.overflow = scrollDisabled ? 'hidden' : 'auto';\n    }\n  }, [scrollDisabled]);\n\n  // Scroll to initial position when entering mobile view\n  const prevIsMobileRef = useRef(isMobileSize);\n  useEffect(() => {\n    if (isMobileSize && !prevIsMobileRef.current) scrollToInitialPosition();\n    prevIsMobileRef.current = isMobileSize;\n  }, [isMobileSize, scrollToInitialPosition]);\n\n  // Scroll to initial position when mobileOnly is turned off (full overscroll DOM just mounted)\n  const prevMobileOnlyRef = useRef(mobileOnly);\n  useEffect(() => {\n    if (!mobileOnly && prevMobileOnlyRef.current) scrollToInitialPosition();\n    prevMobileOnlyRef.current = mobileOnly;\n  }, [mobileOnly, scrollToInitialPosition]);\n\n  // Update header height on window resize while in mobile view\n  const prevHeightRef = useRef(height);\n  useEffect(() => {\n    if (isMobileSize && height !== prevHeightRef.current && overscrollRef.current) {\n      updateHeaderHeight(overscrollRef.current.scrollTop);\n    }\n    prevHeightRef.current = height;\n  }, [height, isMobileSize, updateHeaderHeight]);\n\n  function handleScroll(e: UIEvent<HTMLDivElement>) {\n    updateHeaderHeight(e.currentTarget.scrollTop);\n    if (!isScrollingRef.current) {\n      isScrollingRef.current = true;\n      onScrollChangeRef.current(true);\n    }\n    if (scrollTimeoutRef.current) clearTimeout(scrollTimeoutRef.current);\n    scrollTimeoutRef.current = setTimeout(() => {\n      scrollTimeoutRef.current = null;\n      isScrollingRef.current = false;\n      onScrollChangeRef.current(false);\n    }, 500);\n  }\n\n  if (mobileOnly && !isMobileSize) {\n    if (overscrollContentDesktop)\n      return (\n        <div>\n          {overscrollContentDesktop}\n          {children}\n        </div>\n      );\n    return <>{children}</>;\n  }\n\n  const contentMinHeight = unit === '%' ? `${100 - min}%` : `calc(100% - ${min}px)`;\n\n  return (\n    <div className={styles.overscroll} ref={overscrollRef} onScroll={handleScroll}>\n      <div\n        className={styles.overscrollHeaderContainer}\n        style={{ height: `${max}${unit}` }}\n        ref={overscrollHeaderContainerRef}\n      >\n        <div\n          className={cx(styles.overscrollHeader, centerHeader && styles.overscrollHeaderCentered)}\n          ref={overscrollHeaderRef}\n        >\n          {overscrollContent}\n        </div>\n      </div>\n      <div style={{ minHeight: contentMinHeight }}>{children}</div>\n    </div>\n  );\n}\n",".pagination {\n  display: flex;\n  align-items: center;\n  flex-wrap: wrap;\n  gap: var(--pagination-gap, 4px);\n\n  &-item,\n  &-control {\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    box-sizing: border-box;\n    min-width: var(--pagination-size, 36px);\n    height: var(--pagination-size, 36px);\n    padding: 0 6px;\n    border: 1px solid var(--border-color);\n    border-radius: var(--default-border-radius);\n    background: transparent;\n    color: var(--text-color);\n    font: inherit;\n    font-size: var(--default-font-size);\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n    transition:\n      background-color 0.15s,\n      color 0.15s,\n      border-color 0.15s;\n\n    &:hover:not(:disabled) {\n      background: var(--background-accent);\n    }\n\n    &:focus-visible {\n      outline: var(--focus-border-color, var(--primary-color)) solid 2px;\n      outline-offset: -2px;\n    }\n\n    &:disabled {\n      cursor: default;\n      opacity: 0.5;\n    }\n  }\n\n  /* Ties on specificity with the shared `&-item` rule above and wins on source order, so the\n     active page keeps its fill through hover too rather than falling back to the hover surface. */\n  &-item-active {\n    border-color: var(--pagination-active-background, var(--primary-color));\n    background: var(--pagination-active-background, var(--primary-color));\n\n    /* Must follow the shorthand above, which resets background-image. */\n    background-image: var(--primary-gradient, none);\n    color: var(--pagination-active-color, var(--text-on-primary));\n\n    &:hover:not(:disabled) {\n      background: var(--pagination-active-background, var(--primary-color));\n      background-image: var(--primary-gradient, none);\n    }\n\n    /* The active item sits on the primary fill, where the default primary-color focus ring is\n       invisible — same swap OptionPicker makes for its selected option. */\n    &:focus-visible {\n      outline-color: var(--pagination-active-color, var(--text-on-primary));\n    }\n  }\n\n  &-ellipsis {\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    min-width: var(--pagination-size, 36px);\n    height: var(--pagination-size, 36px);\n    color: var(--text-dark);\n    user-select: none;\n  }\n}\n","import { useState } from 'react';\nimport type { ReactElement } from 'react';\nimport cx from 'classnames';\nimport { faAnglesLeft, faAnglesRight, faChevronLeft, faChevronRight } from '@fortawesome/free-solid-svg-icons';\n\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport type { IconContent } from '../types/icon.types';\nimport { renderIcon } from '../utils/renderIcon';\nimport type { PaginationLabels, PaginationProps } from './pagination.types';\n\nimport styles from './pagination.module.pcss';\n\nconst DOTS = 'dots';\n\ntype PaginationEntry = number | typeof DOTS;\n\nconst DEFAULT_LABELS: Required<PaginationLabels> = {\n  goToPage: (page) => `Go to page ${page}`,\n  first: 'Go to first page',\n  previous: 'Go to previous page',\n  next: 'Go to next page',\n  last: 'Go to last page',\n};\n\nfunction range(start: number, end: number): number[] {\n  if (end < start) return [];\n  return Array.from({ length: end - start + 1 }, (_, index) => start + index);\n}\n\nfunction clampPage(page: number, total: number): number {\n  return Math.max(1, Math.min(total, Math.round(page)));\n}\n\nfunction getEntries(page: number, total: number, siblings: number, boundaries: number): PaginationEntry[] {\n  const maxEntries = siblings * 2 + boundaries * 2 + 3;\n  if (maxEntries >= total) return range(1, total);\n\n  const leftBoundary = range(1, boundaries);\n  const rightBoundary = range(total - boundaries + 1, total);\n  const leftGap: PaginationEntry[] = leftBoundary.length > 0 ? [DOTS] : [];\n  const rightGap: PaginationEntry[] = rightBoundary.length > 0 ? [DOTS] : [];\n\n  const leftSibling = Math.max(page - siblings, boundaries + 1);\n  const rightSibling = Math.min(page + siblings, total - boundaries);\n\n  const hasLeftDots = leftSibling > boundaries + 2;\n  const hasRightDots = rightSibling < total - boundaries - 1;\n\n  if (!hasLeftDots && hasRightDots) {\n    return [...range(1, boundaries + siblings * 2 + 2), ...rightGap, ...rightBoundary];\n  }\n\n  if (hasLeftDots && !hasRightDots) {\n    return [...leftBoundary, ...leftGap, ...range(total - boundaries - siblings * 2 - 1, total)];\n  }\n\n  return [...leftBoundary, ...leftGap, ...range(leftSibling, rightSibling), ...rightGap, ...rightBoundary];\n}\n\nfunction Pagination(props: PaginationProps): ReactElement | null {\n  const {\n    total,\n    page: controlledPage,\n    defaultPage = 1,\n    onChange = () => {},\n    siblings = 1,\n    boundaries = 1,\n    showControls = true,\n    showEdges = false,\n    disabled = false,\n    previousIcon = faChevronLeft,\n    nextIcon = faChevronRight,\n    firstIcon = faAnglesLeft,\n    lastIcon = faAnglesRight,\n    'aria-label': ariaLabel = 'Pagination',\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    labels,\n    ...rest\n  } = useComponentDefaults('Pagination', props);\n  const text = useLabels('Pagination', labels, DEFAULT_LABELS);\n\n  const isControlled = controlledPage !== undefined;\n  const [internalPage, setInternalPage] = useState(defaultPage);\n  const page = clampPage(isControlled ? controlledPage! : internalPage, total);\n\n  if (total < 1) return null;\n\n  function goTo(next: number) {\n    const clamped = clampPage(next, total);\n    if (clamped === page) return;\n    if (!isControlled) setInternalPage(clamped);\n    onChange(clamped);\n  }\n\n  const controlClassName = cx(styles.paginationControl, classNames?.control);\n\n  function renderControl(key: string, label: string, icon: IconContent, target: number, isDisabled: boolean) {\n    return (\n      <button\n        key={key}\n        type='button'\n        className={controlClassName}\n        style={slotStyles?.control}\n        aria-label={label}\n        disabled={disabled || isDisabled}\n        onClick={() => goTo(target)}\n      >\n        {renderIcon(icon)}\n      </button>\n    );\n  }\n\n  const isFirst = page <= 1;\n  const isLast = page >= total;\n\n  return (\n    <nav\n      {...rest}\n      aria-label={ariaLabel}\n      className={cx(styles.pagination, classNames?.root, className)}\n      style={{ ...style, ...slotStyles?.root }}\n    >\n      {showEdges && renderControl('first', text.first, firstIcon, 1, isFirst)}\n      {showControls && renderControl('previous', text.previous, previousIcon, page - 1, isFirst)}\n\n      {getEntries(page, total, siblings, boundaries).map((entry, index) =>\n        entry === DOTS ? (\n          <span\n            key={`dots-${index}`}\n            aria-hidden='true'\n            className={cx(styles.paginationEllipsis, classNames?.ellipsis)}\n            style={slotStyles?.ellipsis}\n          >\n            &hellip;\n          </span>\n        ) : (\n          <button\n            key={entry}\n            type='button'\n            className={cx(styles.paginationItem, entry === page && styles.paginationItemActive, classNames?.item)}\n            style={slotStyles?.item}\n            aria-label={text.goToPage(entry)}\n            aria-current={entry === page ? 'page' : undefined}\n            disabled={disabled}\n            onClick={() => goTo(entry)}\n          >\n            {entry}\n          </button>\n        ),\n      )}\n\n      {showControls && renderControl('next', text.next, nextIcon, page + 1, isLast)}\n      {showEdges && renderControl('last', text.last, lastIcon, total, isLast)}\n    </nav>\n  );\n}\n\nexport default Pagination;\n",".progressBar {\n  min-width: 100px;\n  min-height: 10px;\n\n  /* Not --background-accent-light, which is Card's own surface: on a Card the track vanished. A\n     tint of the text colour shows on any surface. */\n  background-color: var(--progress-bar-track-color, color-mix(in srgb, currentcolor 20%, transparent));\n  border-radius: var(--default-border-radius);\n  overflow: hidden;\n  display: flex;\n  align-items: stretch;\n\n  &-fill {\n    background-color: var(--primary-color);\n    background-image: var(--primary-gradient, none);\n    width: 0;\n    display: flex;\n    align-items: center;\n    overflow: hidden;\n    transition: width 0.3s ease;\n    transform: translateZ(0);\n    container-type: inline-size;\n\n    &-end {\n      border-radius: 0 var(--default-border-radius) var(--default-border-radius) 0;\n    }\n\n    /* After the rule above, which it ties on specificity. */\n    &-toned {\n      background-color: var(--tone-fill);\n      background-image: var(--tone-gradient);\n    }\n  }\n\n  &-label {\n    color: var(--text-on-primary);\n    font-size: 12px;\n    padding: 0 min(5px, 4cqw);\n    box-sizing: border-box;\n    overflow: hidden;\n    text-overflow: ellipsis;\n    white-space: nowrap;\n  }\n\n  &-fill-toned &-label {\n    color: var(--tone-contrast);\n  }\n}\n\n/* The bar still shows its value, it just stops sliding to it. */\n@media (prefers-reduced-motion: reduce) {\n  .progressBar-fill {\n    transition: none;\n  }\n}\n","import type { ReactElement } from 'react';\nimport cx from 'classnames';\nimport { Progress, ProgressBarProps } from './progressBar.types';\n\nimport styles from './progressBar.module.pcss';\nimport { toneProps, useToneCheck } from '../styles/tone';\n\nfunction ProgressBar({\n  className = undefined,\n  total = 100,\n  progress = 0,\n  style = undefined,\n  classNames,\n  styles: slotStyles,\n  ...rest\n}: ProgressBarProps): ReactElement {\n  const progressArray: Progress[] = Array.isArray(progress)\n    ? progress\n    : typeof progress === 'object'\n      ? [progress]\n      : [{ amount: progress }];\n  useToneCheck(\n    'ProgressBar',\n    progressArray.map((prog) => prog.color),\n  );\n\n  function renderProgressBars() {\n    return progressArray.map((prog, index) => {\n      const widthPercentage = !total ? 0 : (prog.amount / total) * 100;\n      const tone = prog.color ? toneProps(prog.color) : undefined;\n\n      return (\n        <div\n          key={index}\n          className={cx(\n            styles.progressBarFill,\n            tone && [tone.className, styles.progressBarFillToned],\n            index === progressArray.length - 1 && styles.progressBarFillEnd,\n            prog.class,\n            classNames?.fill,\n          )}\n          style={{\n            width: `${widthPercentage}%`,\n            ...tone?.style,\n            ...slotStyles?.fill,\n          }}\n          title={prog.label ? `${prog.label}: ${prog.amount}` : undefined}\n        >\n          {prog.label && (\n            <div className={cx(styles.progressBarLabel, classNames?.label)} style={slotStyles?.label}>\n              {prog.label}\n            </div>\n          )}\n        </div>\n      );\n    });\n  }\n\n  const value = progressArray.reduce((sum, prog) => sum + prog.amount, 0);\n\n  return (\n    <div\n      // Before `rest`, unlike the class and style: a stacked breakdown may want `role='img'` instead.\n      role='progressbar'\n      aria-valuemin={0}\n      aria-valuemax={total}\n      aria-valuenow={value}\n      {...rest}\n      className={cx(styles.progressBar, className, classNames?.root)}\n      style={{ ...style, ...slotStyles?.root }}\n    >\n      {renderProgressBars()}\n    </div>\n  );\n}\n\nexport default ProgressBar;\n","import type { CSSProperties, ReactNode } from 'react';\nimport type { FormValidator } from 'services/formValidation';\nimport type { SlotClassNames, SlotStyles, HtmlProps } from '../types/slots.types';\n\nexport type RadioValue = string | number;\n\nexport enum RadioGroupOrientation {\n  Vertical = 'vertical',\n  Horizontal = 'horizontal',\n}\n\nexport interface RadioOption {\n  value: RadioValue;\n  /** Visible label; falls back to `value` when omitted. */\n  label?: ReactNode;\n  /** Secondary description shown under the label. */\n  description?: ReactNode;\n  /** Disable just this option. */\n  disabled?: boolean;\n}\n\nexport type RadioGroupSlots = 'root' | 'groupLabel' | 'options' | 'option' | 'radio' | 'dot' | 'label' | 'description';\n\nexport interface RadioGroupProps extends Omit<HtmlProps, 'onChange'> {\n  /** The selectable options. */\n  options: RadioOption[];\n  /** Visible label above the options. Names the group for screen readers, and the field in its validator's messages. */\n  label?: string;\n  /** Controlled selected value. Omit for uncontrolled. */\n  value?: RadioValue;\n  /** Initial value when uncontrolled. */\n  defaultValue?: RadioValue;\n  /** Fires with the newly selected value. */\n  onChange?: (value: RadioValue, event?: React.MouseEvent | React.KeyboardEvent) => void;\n  /** Layout direction (default `Vertical`). */\n  orientation?: RadioGroupOrientation;\n  /** Disable the whole group. */\n  disabled?: boolean;\n  /** Diameter of the radio circle. Defaults to `1.125em` of `--default-font-size`, 18px at a 16px base. */\n  size?: string;\n  /** Tab index of the active (roving) option. */\n  tabIndex?: number;\n  formValidator?: FormValidator | null;\n  /** Manual error message shown in a tooltip (used when there's no `formValidator`, or to force one). */\n  errorMessage?: string;\n  /** Manual valid state when not using a `formValidator` (default `true`). */\n  isValid?: boolean;\n\n  className?: string;\n  style?: CSSProperties;\n  classNames?: SlotClassNames<RadioGroupSlots>;\n  styles?: SlotStyles<RadioGroupSlots>;\n}\n",".radioGroup {\n  /* The base is declared here rather than only on the option, so every em below resolves\n     against the theme's text size instead of whatever the group happens to sit in. At a 16px\n     base these are the 18px dot, 12px row gap and 10px label gap it has always had. */\n  font-size: var(--default-font-size);\n\n  --custom-radio-size: var(--radio-size, 1.125em);\n  --custom-radio-gap: var(--radio-gap, 0.625em);\n\n  user-select: none;\n  display: flex;\n  flex-direction: column;\n  gap: 0.5em;\n  width: fit-content;\n\n  &-groupLabel {\n    color: var(--text-color);\n  }\n\n  &-options {\n    display: flex;\n    flex-direction: column;\n    gap: 0.75em;\n  }\n\n  &-horizontal &-options {\n    flex-flow: row wrap;\n    gap: 1.25em;\n  }\n\n  &-disabled {\n    filter: brightness(0.85);\n    cursor: not-allowed;\n  }\n\n  &-error &-radio:not(.radioGroup-radio-checked) {\n    border-color: var(--error-color);\n  }\n\n  &-option {\n    display: flex;\n    align-items: flex-start;\n    column-gap: var(--custom-radio-gap);\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n    border: 1px solid transparent;\n    border-radius: var(--default-border-radius);\n    margin: -1px;\n    color: var(--text-color);\n    font-family: var(--default-font);\n\n    &:focus-visible {\n      outline: none;\n      border-color: var(--focus-border-color, var(--primary-color));\n    }\n\n    &-disabled {\n      cursor: not-allowed;\n      opacity: 0.55;\n    }\n  }\n\n  &-radio {\n    flex-shrink: 0;\n    width: var(--custom-radio-size);\n    height: var(--custom-radio-size);\n    border-radius: 50%;\n    border: 0.1125em solid var(--border-color);\n    position: relative;\n    margin-top: 0.0625em;\n    transition: border-color 0.15s;\n\n    &-checked {\n      border-color: var(--primary-color);\n    }\n  }\n\n  &-dot {\n    /* Inset by a transparent border, not centred by flex: border widths snap to whole device\n       pixels, so the gap is equal on every side. A flex-centred 9px dot sat 3.5px into the ring and\n       rendered a pixel off. 0.1875 leaves a dot of about half the ring. */\n    position: absolute;\n    inset: 0;\n    border: calc(var(--custom-radio-size) * 0.1875) solid transparent;\n    border-radius: 50%;\n    background-color: var(--primary-color);\n    background-clip: padding-box;\n    transform: scale(0);\n    transition: transform 0.15s ease;\n\n    &-checked {\n      transform: scale(1);\n    }\n  }\n\n  &-text {\n    display: flex;\n    flex-direction: column;\n    gap: 0.125em;\n  }\n\n  &-label {\n    line-height: 1.2;\n    color: var(--text-color);\n  }\n\n  &-description {\n    font-size: 0.85em;\n    line-height: 1.3;\n    color: var(--text-dark);\n  }\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .radioGroup-dot {\n    transition: none;\n  }\n}\n","import { useEffect, useId, useRef, useState, type ReactElement } from 'react';\nimport cx from 'classnames';\nimport Tooltip from 'components/common/tooltip';\n\nimport { useComponentDefaults } from '../configProvider';\nimport { RadioGroupOrientation, type RadioGroupProps, type RadioValue } from './radioGroup.types';\nimport styles from './radioGroup.module.pcss';\n\nfunction RadioGroup(props: RadioGroupProps): ReactElement {\n  const {\n    options = [],\n    label,\n    value,\n    defaultValue,\n    onChange = () => {},\n    orientation = RadioGroupOrientation.Vertical,\n    disabled = false,\n    size,\n    tabIndex = 0,\n    formValidator = null,\n    errorMessage = '',\n    isValid = true,\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    ...rest\n  } = useComponentDefaults('RadioGroup', props);\n\n  const optionRefs = useRef<(HTMLDivElement | null)[]>([]);\n  const fvRef = useRef(formValidator);\n  useEffect(() => {\n    fvRef.current = formValidator;\n  });\n  const [, forceUpdate] = useState(0);\n  const [internalValue, setInternalValue] = useState<RadioValue | undefined>(defaultValue);\n  const [isFocused, setIsFocused] = useState(false);\n  const [isHovered, setIsHovered] = useState(false);\n\n  useEffect(() => {\n    if (!formValidator) return;\n    if (label) formValidator.label = label;\n    formValidator.validate();\n    const listener = () => forceUpdate((n) => n + 1);\n    formValidator.registerOnUpdateListener(listener);\n    return () => formValidator.removeOnUpdateListener(listener);\n  }, [formValidator, label]);\n\n  const labelId = useId();\n\n  const hasError = formValidator ? formValidator.shouldShowError : !isValid || !!errorMessage;\n  const errorText = formValidator ? formValidator.getCurrentErrorMessage() : errorMessage || '';\n\n  const isControlled = value !== undefined && !formValidator;\n  const currentValue = (formValidator ? formValidator.value : isControlled ? value : internalValue) as\n    | RadioValue\n    | undefined;\n\n  function select(next: RadioValue, event?: React.MouseEvent | React.KeyboardEvent) {\n    if (formValidator) formValidator.set(next);\n    else if (!isControlled) setInternalValue(next);\n    onChange(next, event);\n  }\n\n  function handleBlur(event: React.FocusEvent<HTMLDivElement>) {\n    if (event.currentTarget.contains(event.relatedTarget as Node | null)) return;\n    setIsFocused(false);\n    if (fvRef.current) fvRef.current.touched = true;\n  }\n\n  const selectedIndex = options.findIndex((o) => o.value === currentValue);\n  const firstEnabledIndex = options.findIndex((o) => !o.disabled);\n  const rovingIndex = selectedIndex >= 0 ? selectedIndex : firstEnabledIndex;\n\n  function focusOption(index: number) {\n    optionRefs.current[index]?.focus();\n  }\n\n  function moveTo(step: 1 | -1, fromIndex: number, event: React.KeyboardEvent) {\n    if (disabled || options.length === 0) return;\n    let i = fromIndex;\n    for (let count = 0; count < options.length; count++) {\n      i = (i + step + options.length) % options.length;\n      if (!options[i].disabled) {\n        focusOption(i);\n        select(options[i].value, event);\n        return;\n      }\n    }\n  }\n\n  function handleKeyDown(event: React.KeyboardEvent, index: number) {\n    const option = options[index];\n    switch (event.key) {\n      case 'ArrowDown':\n      case 'ArrowRight':\n        event.preventDefault();\n        moveTo(1, index, event);\n        break;\n      case 'ArrowUp':\n      case 'ArrowLeft':\n        event.preventDefault();\n        moveTo(-1, index, event);\n        break;\n      case ' ':\n      case 'Enter':\n        event.preventDefault();\n        if (!disabled && !option.disabled) select(option.value, event);\n        break;\n      default:\n        break;\n    }\n  }\n\n  return (\n    <div\n      {...rest}\n      role='radiogroup'\n      aria-labelledby={label ? labelId : undefined}\n      aria-orientation={orientation}\n      aria-disabled={disabled || undefined}\n      aria-invalid={hasError || undefined}\n      className={cx(\n        styles.radioGroup,\n        orientation === RadioGroupOrientation.Horizontal && styles.radioGroupHorizontal,\n        disabled && styles.radioGroupDisabled,\n        hasError && styles.radioGroupError,\n        className,\n        classNames?.root,\n      )}\n      style={{ ...(size ? { '--radio-size': size } : null), ...style, ...slotStyles?.root } as React.CSSProperties}\n      onFocus={() => setIsFocused(true)}\n      onBlur={handleBlur}\n      onMouseEnter={() => setIsHovered(true)}\n      onMouseLeave={() => setIsHovered(false)}\n    >\n      {label && (\n        <div\n          id={labelId}\n          className={cx(styles.radioGroupGroupLabel, classNames?.groupLabel)}\n          style={slotStyles?.groupLabel}\n        >\n          {label}\n        </div>\n      )}\n      <div className={cx(styles.radioGroupOptions, classNames?.options)} style={slotStyles?.options}>\n        {options.map((option, index) => {\n          const checked = option.value === currentValue;\n          const optionDisabled = disabled || !!option.disabled;\n          return (\n            <div\n              key={String(option.value)}\n              ref={(el) => {\n                optionRefs.current[index] = el;\n              }}\n              role='radio'\n              aria-checked={checked}\n              aria-disabled={optionDisabled || undefined}\n              tabIndex={optionDisabled ? -1 : index === rovingIndex ? tabIndex : -1}\n              className={cx(\n                styles.radioGroupOption,\n                checked && styles.radioGroupOptionChecked,\n                optionDisabled && styles.radioGroupOptionDisabled,\n                classNames?.option,\n              )}\n              style={slotStyles?.option}\n              onClick={() => {\n                if (!optionDisabled) select(option.value);\n              }}\n              onKeyDown={(e) => handleKeyDown(e, index)}\n            >\n              <div\n                className={cx(styles.radioGroupRadio, checked && styles.radioGroupRadioChecked, classNames?.radio)}\n                style={slotStyles?.radio}\n              >\n                <div\n                  className={cx(styles.radioGroupDot, checked && styles.radioGroupDotChecked, classNames?.dot)}\n                  style={slotStyles?.dot}\n                />\n              </div>\n              {(option.label ?? option.value) !== '' && (\n                <div className={styles.radioGroupText}>\n                  <div className={cx(styles.radioGroupLabel, classNames?.label)} style={slotStyles?.label}>\n                    {option.label ?? option.value}\n                  </div>\n                  {option.description != null && (\n                    <div\n                      className={cx(styles.radioGroupDescription, classNames?.description)}\n                      style={slotStyles?.description}\n                    >\n                      {option.description}\n                    </div>\n                  )}\n                </div>\n              )}\n            </div>\n          );\n        })}\n      </div>\n      <Tooltip variant='error' message={errorText} isVisible={hasError && (isFocused || isHovered)} />\n    </div>\n  );\n}\n\nexport default RadioGroup;\n",".rating {\n  /* calc, not em: this is the element that sets the base, so an em here would measure\n     whatever the rating happens to sit in instead of the theme's text size. */\n  --custom-rating-size: var(--rating-size, calc(var(--default-font-size) * 1.25));\n\n  display: inline-flex;\n  align-items: center;\n  align-self: flex-start;\n  width: fit-content;\n  gap: 0.15em;\n  font-size: var(--custom-rating-size);\n  outline: none;\n  touch-action: pan-y;\n  user-select: none;\n\n  &:focus-visible {\n    box-shadow: 0 0 0 2px var(--primary-lighter);\n    border-radius: var(--default-border-radius);\n  }\n\n  &-disabled {\n    opacity: 0.5;\n  }\n\n  &-disabled,\n  &-readOnly {\n    pointer-events: none;\n  }\n\n  &-item {\n    position: relative;\n    display: inline-flex;\n    width: 1em;\n    height: 1em;\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n  }\n\n  &-iconEmpty {\n    /* FontAwesome's own rule is unlayered, so it outranks `width` from inside the library's layer and\n       reads this instead. `width` still sizes a custom icon. */\n    --fa-width: 1em;\n\n    width: 1em;\n    height: 1em;\n\n    /* Not --background-accent-light, which is Card's own surface: on a Card the empty stars\n       vanished. A tint of the text colour shows on any surface. */\n    color: var(--rating-empty-color, color-mix(in srgb, currentcolor 20%, transparent));\n  }\n\n  &-iconFilledClip {\n    position: absolute;\n    top: 0;\n    left: 0;\n\n    /* flex, so the icon is not an inline box: on a baseline it sat a fraction of a pixel below\n       the empty icon behind it, which the clip then cut off the bottom of. */\n    display: flex;\n    height: 100%;\n    overflow: hidden;\n    pointer-events: none;\n  }\n\n  &-iconFilled {\n    --fa-width: 1em;\n\n    width: 1em;\n    height: 1em;\n\n    /* The clip is narrower than a star at a partial fill. An `svg` refuses to shrink, but `icon`\n       takes any element, and a wrapped one would be squeezed rather than clipped. */\n    flex-shrink: 0;\n    color: var(--rating-filled-color, var(--primary-color));\n  }\n}\n","import { useRef, useState } from 'react';\nimport type { ReactElement } from 'react';\nimport cx from 'classnames';\nimport { faStar } from '@fortawesome/free-solid-svg-icons';\n\nimport { useComponentDefaults } from '../configProvider';\nimport type { RatingProps } from './rating.types';\n\nimport styles from './rating.module.pcss';\nimport { renderIcon } from '../utils/renderIcon';\n\nconst DRAG_THRESHOLD = 4;\n\nfunction clampValue(value: number, max: number): number {\n  return Math.max(0, Math.min(max, value));\n}\n\nfunction Rating(props: RatingProps): ReactElement {\n  const {\n    value: controlledValue,\n    defaultValue = 0,\n    onChange = () => {},\n    max = 5,\n    allowHalf = false,\n    allowClear = true,\n    readOnly = false,\n    disabled = false,\n    icon = faStar,\n    'aria-label': ariaLabel = 'Rating',\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    ...rest\n  } = useComponentDefaults('Rating', props);\n\n  const isControlled = controlledValue !== undefined;\n  const [internalValue, setInternalValue] = useState(defaultValue);\n  const value = clampValue(isControlled ? controlledValue! : internalValue, max);\n\n  const [hoverValue, setHoverValue] = useState<number | null>(null);\n  const isInteractive = !readOnly && !disabled;\n  const displayValue = hoverValue ?? value;\n\n  const rootRef = useRef<HTMLDivElement>(null);\n  const valueBeforeGestureRef = useRef(value);\n  const draggingRef = useRef(false);\n  const gestureStartXRef = useRef(0);\n  const hasDraggedRef = useRef(false);\n\n  function commit(nextValue: number) {\n    const clamped = clampValue(nextValue, max);\n    if (!isControlled) setInternalValue(clamped);\n    onChange(clamped);\n  }\n\n  function getValueFromClientX(clientX: number): number | null {\n    const rect = rootRef.current?.getBoundingClientRect();\n    if (!rect || rect.width === 0) return null;\n    const fraction = clampValue((clientX - rect.left) / rect.width, 1);\n    const raw = fraction * max;\n    return allowHalf ? Math.round(raw * 2) / 2 : Math.ceil(raw);\n  }\n\n  function handlePointerDown(event: React.PointerEvent<HTMLDivElement>) {\n    if (!isInteractive) return;\n    event.currentTarget.setPointerCapture?.(event.pointerId);\n    draggingRef.current = true;\n    hasDraggedRef.current = false;\n    gestureStartXRef.current = event.clientX;\n    valueBeforeGestureRef.current = value;\n    const next = getValueFromClientX(event.clientX);\n    if (next !== null) setHoverValue(next);\n  }\n\n  function handlePointerMove(event: React.PointerEvent<HTMLDivElement>) {\n    if (!isInteractive) return;\n    const next = getValueFromClientX(event.clientX);\n    if (next === null) return;\n    if (draggingRef.current) {\n      event.preventDefault();\n      if (Math.abs(event.clientX - gestureStartXRef.current) > DRAG_THRESHOLD) {\n        hasDraggedRef.current = true;\n      }\n    }\n    setHoverValue(next);\n  }\n\n  function handlePointerUp(event: React.PointerEvent<HTMLDivElement>) {\n    if (!isInteractive || !draggingRef.current) return;\n    event.currentTarget.releasePointerCapture?.(event.pointerId);\n    draggingRef.current = false;\n    const next = getValueFromClientX(event.clientX) ?? hoverValue;\n    if (next !== null) {\n      const isClick = !hasDraggedRef.current;\n      commit(isClick && allowClear && next === valueBeforeGestureRef.current ? 0 : next);\n    }\n    setHoverValue(null);\n  }\n\n  function handlePointerLeave() {\n    if (!isInteractive || draggingRef.current) return;\n    setHoverValue(null);\n  }\n\n  function handleKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {\n    const step = allowHalf ? 0.5 : 1;\n    if (event.key === 'ArrowRight' || event.key === 'ArrowUp') {\n      event.preventDefault();\n      commit(value + step);\n    } else if (event.key === 'ArrowLeft' || event.key === 'ArrowDown') {\n      event.preventDefault();\n      commit(value - step);\n    } else if (event.key === 'Home') {\n      event.preventDefault();\n      commit(0);\n    } else if (event.key === 'End') {\n      event.preventDefault();\n      commit(max);\n    }\n  }\n\n  return (\n    <div\n      {...rest}\n      ref={rootRef}\n      role='slider'\n      aria-label={ariaLabel}\n      aria-valuemin={0}\n      aria-valuemax={max}\n      aria-valuenow={value}\n      aria-valuetext={`${value} out of ${max}`}\n      aria-readonly={readOnly || undefined}\n      aria-disabled={disabled || undefined}\n      tabIndex={isInteractive ? 0 : -1}\n      className={cx(\n        styles.rating,\n        disabled && styles.ratingDisabled,\n        readOnly && styles.ratingReadOnly,\n        classNames?.root,\n        className,\n      )}\n      style={{ ...style, ...slotStyles?.root }}\n      onKeyDown={isInteractive ? handleKeyDown : undefined}\n      onPointerDown={isInteractive ? handlePointerDown : undefined}\n      onPointerMove={isInteractive ? handlePointerMove : undefined}\n      onPointerUp={isInteractive ? handlePointerUp : undefined}\n      onPointerCancel={isInteractive ? handlePointerUp : undefined}\n      onPointerLeave={isInteractive ? handlePointerLeave : undefined}\n    >\n      {Array.from({ length: max }, (_, index) => {\n        const fillPercent = Math.round(Math.max(0, Math.min(1, displayValue - index)) * 100);\n\n        return (\n          <div key={index} className={cx(styles.ratingItem, classNames?.item)} style={slotStyles?.item}>\n            {renderIcon(icon, {\n              className: cx(styles.ratingIconEmpty, classNames?.iconEmpty),\n              style: slotStyles?.iconEmpty,\n            })}\n            <div className={styles.ratingIconFilledClip} style={{ width: `${fillPercent}%` }}>\n              {renderIcon(icon, {\n                className: cx(styles.ratingIconFilled, classNames?.iconFilled),\n                style: slotStyles?.iconFilled,\n              })}\n            </div>\n          </div>\n        );\n      })}\n    </div>\n  );\n}\n\nexport default Rating;\n",".revealLens {\n  display: grid;\n  overflow: hidden;\n  touch-action: none;\n\n  &-background,\n  &-overlay {\n    grid-area: 1 / 1;\n    min-width: 0;\n    min-height: 0;\n  }\n\n  &-background {\n    z-index: 0;\n  }\n\n  &-overlay {\n    z-index: 1;\n  }\n\n  &-overlay-transitioning {\n    transition:\n      mask-image 0.25s ease,\n      -webkit-mask-image 0.25s ease;\n\n    @media (prefers-reduced-motion: reduce) {\n      transition: none;\n    }\n  }\n}\n","import { useRef, type PointerEvent as ReactPointerEvent, type ReactElement } from 'react';\nimport cx from 'classnames';\n\nimport { useComponentDefaults } from '../configProvider';\nimport type { RevealLensProps } from './revealLens.types';\nimport styles from './revealLens.module.pcss';\n\nfunction buildMaskImage(x: number, y: number, radius: number, feather: number): string {\n  const inner = Math.max(radius - feather, 0);\n  return `radial-gradient(circle ${radius}px at ${x}px ${y}px, transparent ${inner}px, transparent ${inner}px, white ${radius}px)`;\n}\n\nfunction applyMask(overlay: HTMLDivElement, value: string) {\n  overlay.style.setProperty('mask-image', value);\n  overlay.style.setProperty('-webkit-mask-image', value);\n}\n\nfunction clearMask(overlay: HTMLDivElement) {\n  overlay.style.removeProperty('mask-image');\n  overlay.style.removeProperty('-webkit-mask-image');\n}\n\nfunction RevealLens(props: RevealLensProps): ReactElement {\n  const {\n    background,\n    overlay,\n    radius = 120,\n    feather = 24,\n    shape = 'circle',\n    maskImage,\n    disabled = false,\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    ...rest\n  } = useComponentDefaults('RevealLens', props);\n\n  const rootRef = useRef<HTMLDivElement>(null);\n  const overlayRef = useRef<HTMLDivElement>(null);\n\n  const updateMask = (clientX: number, clientY: number) => {\n    const root = rootRef.current;\n    const overlayEl = overlayRef.current;\n    if (!root || !overlayEl) return;\n\n    const rect = root.getBoundingClientRect();\n    const x = clientX - rect.left;\n    const y = clientY - rect.top;\n\n    overlayEl.classList.remove(styles.revealLensOverlayTransitioning);\n    applyMask(overlayEl, maskImage ?? buildMaskImage(x, y, radius, feather));\n  };\n\n  const resetMask = () => {\n    const overlayEl = overlayRef.current;\n    if (!overlayEl) return;\n\n    overlayEl.classList.add(styles.revealLensOverlayTransitioning);\n    clearMask(overlayEl);\n  };\n\n  const handlePointerDown = (event: ReactPointerEvent<HTMLDivElement>) => {\n    if (disabled) return;\n    event.currentTarget.setPointerCapture?.(event.pointerId);\n    updateMask(event.clientX, event.clientY);\n  };\n\n  const handlePointerMove = (event: ReactPointerEvent<HTMLDivElement>) => {\n    if (disabled) return;\n    updateMask(event.clientX, event.clientY);\n  };\n\n  const handlePointerUp = (event: ReactPointerEvent<HTMLDivElement>) => {\n    if (disabled || event.pointerType === 'mouse') return;\n    resetMask();\n  };\n\n  // pointerleave/pointercancel can fire spuriously, with the pointer still physically over the\n  // element, when it's nested inside two coincident 3D transform contexts (e.g. Tilt + Flip\n  // composed per the docs, where Flip fills Tilt exactly and their perspective-origins land on\n  // the same pixel). At that shared origin point, Chromium's hit-test can misresolve to an\n  // ancestor instead of the actual topmost content. elementFromPoint reads that same broken\n  // result, so it can't be trusted there either. The event's own clientX/clientY are real,\n  // unaffected viewport coordinates, so check those directly against our own (transform-aware)\n  // bounding rect first. A leave that's still geometrically inside us is never real.\n  const handlePointerEnd = (event: ReactPointerEvent<HTMLDivElement>) => {\n    if (disabled) return;\n    const root = rootRef.current;\n    if (root) {\n      const rect = root.getBoundingClientRect();\n      const stillInside =\n        event.clientX >= rect.left &&\n        event.clientX <= rect.right &&\n        event.clientY >= rect.top &&\n        event.clientY <= rect.bottom;\n      if (stillInside) return;\n\n      if (typeof document.elementFromPoint === 'function') {\n        const target = document.elementFromPoint(event.clientX, event.clientY);\n        if (target && root.contains(target)) return;\n      }\n    }\n    resetMask();\n  };\n\n  return (\n    <div\n      {...rest}\n      ref={rootRef}\n      className={cx(styles.revealLens, className, classNames?.root)}\n      style={{ ...style, ...slotStyles?.root }}\n      data-reveal-shape={shape}\n      onPointerDown={handlePointerDown}\n      onPointerMove={handlePointerMove}\n      onPointerUp={handlePointerUp}\n      onPointerCancel={handlePointerEnd}\n      onPointerLeave={handlePointerEnd}\n    >\n      <div className={cx(styles.revealLensBackground, classNames?.background)} style={slotStyles?.background}>\n        {background}\n      </div>\n      <div ref={overlayRef} className={cx(styles.revealLensOverlay, classNames?.overlay)} style={slotStyles?.overlay}>\n        {overlay}\n      </div>\n    </div>\n  );\n}\n\nexport default RevealLens;\n","/**\n * Every colour comes from a theme variable. The wall fill/outline pair in particular\n * must stay two distinct surfaces, or the two-pass stroke has nothing to reveal.\n */\n\n.planCanvas {\n  /*\n   * The wall body and its edge, resolved once: the outline pass, the caps and the jamb\n   * faces are the same edge of the same object and must not drift apart. Both mix towards\n   * the canvas rather than sitting at the text colour, so a wall reads as mass on the\n   * floor instead of as ink drawn over it. `--plan-wall-outline` and `--plan-wall-fill`\n   * are still the knobs a consumer sets.\n   */\n  --plan-wall-edge-resolved: var(--plan-wall-outline, color-mix(in oklab, var(--text-color) 38%, var(--background)));\n  --plan-wall-fill-resolved: var(--plan-wall-fill, color-mix(in oklab, var(--text-color) 58%, var(--background)));\n\n  display: block;\n  touch-action: none;\n  user-select: none;\n  background: var(--plan-canvas-background, var(--background));\n\n  &-screen {\n    pointer-events: none;\n  }\n\n  /* The storey below, for lining walls up between floors. Faint and dashed so it reads\n     as a tracing under the drawing rather than as part of it, and never picks up a\n     pointer: it is not this floor's geometry. */\n  &-underlay {\n    pointer-events: none;\n\n    line {\n      stroke: var(--plan-underlay-color, var(--text-dark));\n      opacity: 0.28;\n      stroke-dasharray: 6 5;\n      stroke-linecap: round;\n      vector-effect: non-scaling-stroke;\n    }\n  }\n\n  &-grid {\n    pointer-events: none;\n\n    line {\n      stroke: var(--plan-grid-color, var(--text-color));\n      opacity: 0.1;\n    }\n  }\n\n  /* ── Rooms ── */\n\n  &-rooms {\n    pointer-events: auto;\n  }\n\n  &-roomFill {\n    fill: var(--plan-room-fill, var(--background-accent));\n    stroke: none;\n    transition:\n      fill 0.12s ease-out,\n      opacity 0.12s ease-out;\n\n    &-interactive {\n      cursor: pointer;\n      -webkit-tap-highlight-color: transparent;\n\n      &:focus-visible {\n        outline: none;\n        stroke: var(--focus-border-color, var(--primary-color));\n        stroke-width: 2;\n        vector-effect: non-scaling-stroke;\n      }\n    }\n\n    /* Hover tints the floor, selection colours the walls: two channels, so both can be\n       read at once. A fill answers \"which room\" instantly, where a shared wall is\n       ambiguous between the rooms either side of it. */\n    &-hovered {\n      fill: var(--plan-room-fill-hover, var(--background-accent-light));\n    }\n  }\n\n  &-roomLabel {\n    pointer-events: none;\n  }\n\n  &-roomName {\n    fill: var(--text-color);\n    font-size: 13px;\n    font-weight: 600;\n\n    /* A halo, so a label stays legible over any room colour a consumer sets. */\n    paint-order: stroke;\n    stroke: var(--background);\n    stroke-width: 3px;\n    stroke-linejoin: round;\n  }\n\n  &-roomArea {\n    fill: var(--text-dark);\n    font-size: 11px;\n    paint-order: stroke;\n    stroke: var(--background);\n    stroke-width: 3px;\n    stroke-linejoin: round;\n  }\n\n  /* ── Walls ── */\n\n  &-walls {\n    pointer-events: none;\n  }\n\n  /*\n   * Round joins, so the outer corners of the building come out softened and an acute\n   * corner cannot fire a mitre spike out of the junction at all. `--plan-wall-join: miter`\n   * restores the drafting corner, and `--plan-wall-miter-limit` then caps how far the\n   * spike may run. The selection highlight shares the rule: it is drawn from the same\n   * geometry over the same walls, so a different join would leave crescents showing.\n   */\n  &-wallOutline path,\n  &-wallFill path,\n  &-roomEdgeSelected path {\n    fill: none;\n    stroke-linecap: butt;\n    stroke-linejoin: var(--plan-wall-join, round);\n    stroke-miterlimit: var(--plan-wall-miter-limit, 4);\n  }\n\n  &-wallOutline path {\n    stroke: var(--plan-wall-edge-resolved);\n  }\n\n  /* Must match the outline pass exactly: the two are the same shape, and the visible\n     outline is what is left of the first after this one covers its middle. */\n  &-wallFill path {\n    stroke: var(--plan-wall-fill-resolved);\n  }\n\n  &-wallCaps line {\n    stroke: var(--plan-wall-edge-resolved);\n    stroke-linecap: butt;\n  }\n\n  /* Drawn from the same geometry as the walls, so it inherits their corners and their\n     gaps at doors. */\n  &-roomEdgeCaps line {\n    stroke: var(--plan-room-edge-selected, var(--primary-color));\n    stroke-linecap: butt;\n  }\n\n  /* Edge darker than body, so the wall keeps the crisp outline every other wall has\n     rather than turning into a flat slab, and reads as one highlighted object. */\n  &-roomEdgeSelected {\n    .planCanvas-roomEdgeOutline {\n      stroke: var(--plan-room-edge-selected, var(--primary-color));\n    }\n\n    .planCanvas-roomEdgeFill {\n      stroke: var(--plan-room-edge-selected-fill, var(--primary-lighter));\n    }\n  }\n\n  &-wallSelected {\n    stroke: var(--primary-color);\n    opacity: 0.45;\n    stroke-linecap: butt;\n  }\n\n  &-wallHovered {\n    stroke: var(--primary-lighter);\n    opacity: 0.35;\n    stroke-linecap: butt;\n  }\n\n  /* ── Openings ── */\n\n  &-openings {\n    pointer-events: auto;\n  }\n\n  &-opening {\n    /*\n     * State lives on the group so the whole symbol responds, not just the box drawn round\n     * it by `-outline`: a door reads as selected when its leaf and swing do, the way the\n     * room fill does, rather than by acquiring a rectangle.\n     */\n    &-selected {\n      .planCanvas-doorLeaf,\n      .planCanvas-doorSwing,\n      .planCanvas-windowPane {\n        stroke: var(--primary-color);\n      }\n\n      .planCanvas-doorSwing {\n        opacity: 0.9;\n      }\n    }\n\n    &-hovered {\n      .planCanvas-doorLeaf,\n      .planCanvas-doorSwing,\n      .planCanvas-windowPane {\n        stroke: var(--primary-lighter, var(--primary-color));\n      }\n    }\n\n    &-hit {\n      fill: transparent;\n      stroke: none;\n      pointer-events: none;\n\n      &-interactive {\n        pointer-events: auto;\n        cursor: pointer;\n        -webkit-tap-highlight-color: transparent;\n      }\n    }\n\n    &-outline {\n      fill: none;\n      stroke: var(--primary-color);\n      stroke-width: 2;\n    }\n  }\n\n  /* The wall seen in section, so it takes the body colour rather than the edge one: at a\n     doorway it is the only thing standing for the wall, and the edge tone disappears. */\n  &-jamb {\n    stroke: var(--plan-wall-fill-resolved);\n    stroke-linecap: butt;\n\n    &-highlighted {\n      stroke: var(--plan-room-edge-selected, var(--primary-color));\n    }\n  }\n\n  &-doorLeaf {\n    fill: none;\n    stroke: var(--plan-door-color, var(--text-dark));\n    stroke-linecap: butt;\n  }\n\n  &-doorSwing {\n    fill: none;\n    stroke: var(--plan-door-color, var(--text-dark));\n    stroke-dasharray: 6 4;\n    opacity: 0.6;\n  }\n\n  &-windowPane {\n    stroke: var(--plan-window-color, var(--info-color));\n    stroke-linecap: butt;\n  }\n\n  &-selection {\n    pointer-events: none;\n  }\n}\n","import { useMemo, type ReactElement } from 'react';\nimport cx from 'classnames';\n\nimport {\n  OpeningKind,\n  buildWallPaths,\n  openingGeometry,\n  type DerivedRoom,\n  type PlanOpening,\n  type PlanPoint,\n  type PlanSelection,\n} from '../floorPlan';\nimport type { PlanCanvasProps } from './planCanvas.types';\nimport styles from './planCanvas.module.pcss';\n\n/**\n * The read-only renderer shared by RoomDrawer and RoomViewer.\n *\n * It owns the projection, the layer stack, and the accessible room shapes. It owns no\n * editing state: the drawer supplies its handles and previews through `overlay`. Having\n * one renderer is the point, so that a plan cannot look different in the editor than it\n * does in the viewer.\n *\n * The viewport is a TRANSFORM on a group, not an SVG `viewBox`. That is what lets the\n * screen-space layer draw labels and handles at a fixed pixel size while the drawing\n * itself scales.\n */\n\nconst DEFAULT_OUTLINE_PX = 1;\n\n/** Stable identity, so a default-valued `selection` prop does not re-run memos. */\nconst EMPTY_SELECTION: readonly PlanSelection[] = [];\n\nfunction formatAreaDefault(areaMm2: number): string {\n  const m2 = areaMm2 / 1_000_000;\n  return `${m2.toFixed(m2 < 10 ? 1 : 0)} m²`;\n}\n\nfunction ringToPath(ring: PlanPoint[]): string {\n  if (ring.length === 0) return '';\n  return `${ring.map((p, i) => `${i === 0 ? 'M' : 'L'} ${p.x},${p.y}`).join(' ')} Z`;\n}\n\n/** Outer ring plus holes as one path; `fill-rule: evenodd` punches the holes out. */\nfunction faceToPath(face: DerivedRoom): string {\n  return [ringToPath(face.outer), ...face.holes.map(ringToPath)].filter(Boolean).join(' ');\n}\n\n/**\n * The ref lands on the `<svg>` itself, which is what a consumer wants for measuring or\n * screenshotting. Both the drawer and the viewer expose it as `getSvgElement()`.\n */\nfunction PlanCanvas(props: PlanCanvasProps) {\n  const {\n    ref,\n    floor,\n    underlay,\n    rooms,\n    view,\n    selection = EMPTY_SELECTION,\n    hovered = null,\n    interactiveRooms = false,\n    interactiveOpenings = false,\n    showGrid = false,\n    gridSize = 100,\n    showRoomLabels = true,\n    showOpeningSymbols = true,\n    getRoomColor,\n    renderRoomLabel,\n    getRoomLabel,\n    formatArea = formatAreaDefault,\n    onRoomActivate,\n    onOpeningActivate,\n    onBackgroundActivate,\n    onHoverChange,\n    overlay,\n    worldOverlay,\n    size,\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    'aria-label': ariaLabel,\n  } = props;\n\n  const pxToWorld = view.zoom > 0 ? 1 / view.zoom : 1;\n  const outlineWidth = DEFAULT_OUTLINE_PX * pxToWorld;\n\n  const wallPaths = useMemo(() => buildWallPaths(floor), [floor]);\n\n  /**\n   * Centrelines only, at one stroke width. The underlay is there to line walls up\n   * against, so it wants to read as a tracing rather than compete with the floor being\n   * drawn; the two-pass thick-wall treatment would give it equal weight.\n   */\n  const underlayLines = useMemo(() => {\n    if (!underlay) return [];\n    return Object.values(underlay.walls).flatMap((wall) => {\n      const a = underlay.nodes[wall.a];\n      const b = underlay.nodes[wall.b];\n      return a && b ? [{ id: wall.id, x1: a.x, y1: a.y, x2: b.x, y2: b.y }] : [];\n    });\n  }, [underlay]);\n\n  /** World point to screen point, for the untransformed layer. */\n  const toScreen = (p: PlanPoint) => ({\n    x: (p.x - view.x) * view.zoom,\n    y: (p.y - view.y) * view.zoom,\n  });\n\n  // Grid lines are generated for the visible rect only. Deliberately not an SVG\n  // <pattern>: a pattern needs an id, and two canvases on one page then collide.\n  const gridLines = useMemo(() => {\n    if (!showGrid || gridSize <= 0) return null;\n    const worldW = size.width * pxToWorld;\n    const worldH = size.height * pxToWorld;\n    // Keep the line count sane when zoomed far out.\n    const step = gridSize * Math.max(1, Math.pow(10, Math.ceil(Math.log10(worldW / gridSize / 60))));\n    const startX = Math.floor(view.x / step) * step;\n    const startY = Math.floor(view.y / step) * step;\n    const verticals: number[] = [];\n    const horizontals: number[] = [];\n    for (let x = startX; x <= view.x + worldW; x += step) verticals.push(x);\n    for (let y = startY; y <= view.y + worldH; y += step) horizontals.push(y);\n    return { verticals, horizontals, step, worldW, worldH };\n  }, [showGrid, gridSize, size.width, size.height, pxToWorld, view.x, view.y]);\n\n  const isSelected = (type: string, id: string) => selection.some((entry) => entry.type === type && entry.id === id);\n  const isHovered = (type: string, id: string) => hovered?.type === type && hovered.id === id;\n\n  /**\n   * The walls bounding the selected rooms. A SELECTED room is marked by colouring its\n   * boundary rather than by flooding its floor, which would bury its doors and its label;\n   * hover keeps the faint floor tint instead, since the two say different things.\n   */\n  const highlightedWalls = useMemo(() => {\n    const selectedRooms = new Set(selection.filter((entry) => entry.type === 'room').map((entry) => entry.id));\n    const ids = new Set<string>();\n    for (const room of rooms) {\n      if (room.roomId && selectedRooms.has(room.roomId)) {\n        // A wall shared by two selected rooms belongs to both; the set draws it once.\n        for (const id of room.wallIds) ids.add(id);\n      }\n    }\n    return ids;\n  }, [rooms, selection]);\n\n  const roomEdge = useMemo(\n    () => (highlightedWalls.size > 0 ? buildWallPaths(floor, { wallIds: highlightedWalls }) : { groups: [], caps: [] }),\n    [floor, highlightedWalls],\n  );\n\n  function renderOpening(opening: PlanOpening) {\n    const geo = openingGeometry(floor, opening);\n    if (!geo) return null;\n    const wall = floor.walls[opening.wallId];\n    const half = wall ? wall.thickness / 2 : 50;\n    const { p1, p2, normal, dir, centre } = geo;\n\n    const selected = isSelected('opening', opening.id);\n    const hoveredNow = isHovered('opening', opening.id);\n    const width = Math.hypot(p2.x - p1.x, p2.y - p1.y);\n\n    const parts: ReactElement[] = [];\n\n    if (showOpeningSymbols && opening.kind === OpeningKind.Door) {\n      // Leaf plus swing arc, drawn on the wall's left side.\n      const hingeAtStart = opening.swing !== 'endLeft' && opening.swing !== 'endRight';\n      const hinge = hingeAtStart ? p1 : p2;\n      const sweepDir = hingeAtStart ? 1 : -1;\n      const leafEnd = { x: hinge.x + normal.x * width, y: hinge.y + normal.y * width };\n      const arcEnd = {\n        x: hinge.x + dir.x * width * sweepDir,\n        y: hinge.y + dir.y * width * sweepDir,\n      };\n      parts.push(\n        <path\n          key='leaf'\n          className={styles.planCanvasDoorLeaf}\n          d={`M ${hinge.x},${hinge.y} L ${leafEnd.x},${leafEnd.y}`}\n          strokeWidth={outlineWidth}\n        />,\n        <path\n          key='swing'\n          className={styles.planCanvasDoorSwing}\n          d={`M ${leafEnd.x},${leafEnd.y} A ${width},${width} 0 0,${hingeAtStart ? 0 : 1} ${arcEnd.x},${arcEnd.y}`}\n          strokeWidth={outlineWidth}\n        />,\n      );\n    }\n\n    if (showOpeningSymbols && opening.kind === OpeningKind.Window) {\n      // A pane line down the middle of the wall's thickness.\n      parts.push(\n        <path\n          key='pane'\n          className={styles.planCanvasWindowPane}\n          d={`M ${p1.x},${p1.y} L ${p2.x},${p2.y}`}\n          strokeWidth={outlineWidth * 2}\n        />,\n      );\n    }\n\n    const hitPath = `M ${p1.x + normal.x * half},${p1.y + normal.y * half} L ${\n      p2.x + normal.x * half\n    },${p2.y + normal.y * half} L ${p2.x - normal.x * half},${p2.y - normal.y * half} L ${\n      p1.x - normal.x * half\n    },${p1.y - normal.y * half} Z`;\n\n    return (\n      <g\n        key={opening.id}\n        className={cx(\n          selected && styles.planCanvasOpeningSelected,\n          hoveredNow && styles.planCanvasOpeningHovered,\n          classNames?.opening,\n        )}\n        style={slotStyles?.opening}\n        data-opening-kind={opening.kind}\n      >\n        {parts}\n        <path\n          className={cx(styles.planCanvasOpeningHit, interactiveOpenings && styles.planCanvasOpeningHitInteractive)}\n          d={hitPath}\n          onClick={\n            interactiveOpenings && onOpeningActivate\n              ? (e) => {\n                  e.stopPropagation();\n                  onOpeningActivate(opening.id);\n                }\n              : undefined\n          }\n          onPointerEnter={onHoverChange ? () => onHoverChange({ type: 'opening', id: opening.id }) : undefined}\n        >\n          {interactiveOpenings && <title>{opening.kind}</title>}\n        </path>\n        {(selected || hoveredNow) && (\n          <path className={styles.planCanvasOpeningOutline} d={hitPath} vectorEffect='non-scaling-stroke' />\n        )}\n        {/* The jamb faces, so an opening reads as a hole with sides rather than a blank. */}\n        <path\n          className={cx(\n            styles.planCanvasJamb,\n            // The jamb face belongs to the wall it is cut into, so it follows that wall\n            // into the highlight rather than staying white against it.\n            highlightedWalls.has(opening.wallId) && styles.planCanvasJambHighlighted,\n          )}\n          d={`M ${p1.x - normal.x * half},${p1.y - normal.y * half} L ${p1.x + normal.x * half},${\n            p1.y + normal.y * half\n          } M ${p2.x - normal.x * half},${p2.y - normal.y * half} L ${p2.x + normal.x * half},${\n            p2.y + normal.y * half\n          }`}\n          strokeWidth={outlineWidth}\n        />\n        {centre && null}\n      </g>\n    );\n  }\n\n  return (\n    <svg\n      ref={ref}\n      className={cx(styles.planCanvas, className, classNames?.canvas)}\n      style={{ ...style, ...slotStyles?.canvas }}\n      viewBox={`0 0 ${Math.max(1, size.width)} ${Math.max(1, size.height)}`}\n      width={size.width}\n      height={size.height}\n      role='img'\n      aria-label={ariaLabel}\n      onClick={onBackgroundActivate ? () => onBackgroundActivate() : undefined}\n      onPointerLeave={onHoverChange ? () => onHoverChange(null) : undefined}\n    >\n      <g transform={`translate(${-view.x * view.zoom} ${-view.y * view.zoom}) scale(${view.zoom})`}>\n        {/* 1. Grid */}\n        {gridLines && (\n          <g className={cx(styles.planCanvasGrid, classNames?.grid)} style={slotStyles?.grid} aria-hidden='true'>\n            {gridLines.verticals.map((x) => (\n              <line key={`v${x}`} x1={x} y1={view.y} x2={x} y2={view.y + gridLines.worldH} strokeWidth={pxToWorld} />\n            ))}\n            {gridLines.horizontals.map((y) => (\n              <line key={`h${y}`} x1={view.x} y1={y} x2={view.x + gridLines.worldW} y2={y} strokeWidth={pxToWorld} />\n            ))}\n          </g>\n        )}\n\n        {/* 1b. The storey below, traced */}\n        {underlayLines.length > 0 && (\n          <g\n            className={cx(styles.planCanvasUnderlay, classNames?.underlay)}\n            style={slotStyles?.underlay}\n            aria-hidden='true'\n          >\n            {/* Screen units: the rule sets vector-effect: non-scaling-stroke, so the width\n                is read in screen pixels rather than world millimetres. */}\n            {underlayLines.map((line) => (\n              <line key={line.id} x1={line.x1} y1={line.y1} x2={line.x2} y2={line.y2} strokeWidth={1.5} />\n            ))}\n          </g>\n        )}\n\n        {/* 2. Room fills — the only hit target for room selection */}\n        <g className={cx(styles.planCanvasRooms, classNames?.rooms)} style={slotStyles?.rooms}>\n          {rooms.map((room) => {\n            const selected = room.roomId ? isSelected('room', room.roomId) : false;\n            const hoveredNow = room.roomId ? isHovered('room', room.roomId) : false;\n            const colour = room.meta ? getRoomColor?.(room.meta, room.area) : undefined;\n            const label = getRoomLabel?.(room.meta, { area: room.area });\n\n            return (\n              <path\n                key={room.id}\n                className={cx(\n                  styles.planCanvasRoomFill,\n                  hoveredNow && styles.planCanvasRoomFillHovered,\n                  interactiveRooms && styles.planCanvasRoomFillInteractive,\n                  classNames?.roomFill,\n                )}\n                data-state={selected ? 'selected' : hoveredNow ? 'hovered' : undefined}\n                style={{ ...slotStyles?.roomFill, ...(colour ? { fill: colour } : null) }}\n                d={faceToPath(room)}\n                fillRule='evenodd'\n                tabIndex={interactiveRooms ? 0 : undefined}\n                role={interactiveRooms ? 'button' : undefined}\n                aria-pressed={interactiveRooms ? selected : undefined}\n                onClick={\n                  interactiveRooms && onRoomActivate && room.roomId\n                    ? (e) => {\n                        e.stopPropagation();\n                        onRoomActivate(room.roomId!);\n                      }\n                    : undefined\n                }\n                onKeyDown={\n                  interactiveRooms && onRoomActivate && room.roomId\n                    ? (e) => {\n                        if (e.key !== 'Enter' && e.key !== ' ') return;\n                        e.preventDefault();\n                        onRoomActivate(room.roomId!);\n                      }\n                    : undefined\n                }\n                onPointerEnter={\n                  onHoverChange && room.roomId ? () => onHoverChange({ type: 'room', id: room.roomId! }) : undefined\n                }\n              >\n                {/* Both a <title> and the aria-label: aria-label on SVG shapes is\n                    unreliable in older Safari/VoiceOver, and <title> doubles as the\n                    native tooltip. */}\n                {interactiveRooms && label && <title>{label}</title>}\n              </path>\n            );\n          })}\n        </g>\n\n        {/* 3. Wall bodies — outline pass, then fill pass. Never interleaved: the visible\n            outline is what is left of the first pass after the second covers its middle. */}\n        <g className={cx(styles.planCanvasWalls, classNames?.walls)} style={slotStyles?.walls} aria-hidden='true'>\n          <g className={styles.planCanvasWallOutline}>\n            {wallPaths.groups.map((group) => (\n              <path\n                key={`o${group.thickness}`}\n                className={classNames?.wallBody}\n                style={slotStyles?.wallBody}\n                d={group.d}\n                strokeWidth={group.thickness}\n              />\n            ))}\n          </g>\n          <g className={styles.planCanvasWallFill}>\n            {wallPaths.groups.map((group) => {\n              const fillWidth = group.thickness - 2 * outlineWidth;\n              // Zoomed far out the fill would invert; walls collapsing to solid lines is\n              // the right look at that scale.\n              if (fillWidth <= 0) return null;\n              return <path key={`f${group.thickness}`} d={group.d} strokeWidth={fillWidth} />;\n            })}\n          </g>\n          <g className={styles.planCanvasRoomEdgeSelected}>\n            {/* The same two passes the walls themselves use, on the same path data: an\n                outline at full thickness, then a fill inside it. Recolouring only the body\n                left the wall edged in its normal colour, so it did not read as one object. */}\n            {roomEdge.groups.map((group) => (\n              <path\n                key={`o${group.thickness}`}\n                className={styles.planCanvasRoomEdgeOutline}\n                d={group.d}\n                strokeWidth={group.thickness}\n              />\n            ))}\n            {roomEdge.groups.map((group) => {\n              const fillWidth = group.thickness - 2 * outlineWidth;\n              if (fillWidth <= 0) return null;\n              return (\n                <path\n                  key={`f${group.thickness}`}\n                  className={styles.planCanvasRoomEdgeFill}\n                  d={group.d}\n                  strokeWidth={fillWidth}\n                />\n              );\n            })}\n          </g>\n\n          <g className={styles.planCanvasWallCaps}>\n            {/* A highlighted wall's caps are drawn below in the highlight colour. Skipped\n                here rather than painted over: an antialiased hairline is never fully\n                covered, and the two would blend into a pale seam. */}\n            {wallPaths.caps\n              .filter((cap) => !highlightedWalls.has(cap.wallId))\n              .map((cap, i) => (\n                <line\n                  key={`${cap.wallId}-${cap.reason}-${i}`}\n                  x1={cap.a.x}\n                  y1={cap.a.y}\n                  x2={cap.b.x}\n                  y2={cap.b.y}\n                  strokeWidth={outlineWidth}\n                />\n              ))}\n          </g>\n          <g className={styles.planCanvasRoomEdgeCaps}>\n            {roomEdge.caps.map((cap, i) => (\n              <line\n                key={`${cap.wallId}-${cap.reason}-${i}`}\n                x1={cap.a.x}\n                y1={cap.a.y}\n                x2={cap.b.x}\n                y2={cap.b.y}\n                strokeWidth={outlineWidth}\n              />\n            ))}\n          </g>\n        </g>\n\n        {/* 4. Openings */}\n        <g className={cx(styles.planCanvasOpenings, classNames?.openings)} style={slotStyles?.openings}>\n          {Object.values(floor.openings).map(renderOpening)}\n        </g>\n\n        {/* 5. Selection / hover outlines for walls and nodes */}\n        <g className={styles.planCanvasSelection} aria-hidden='true'>\n          {selection\n            .filter((entry) => entry.type === 'wall')\n            .map((entry) => {\n              const wall = floor.walls[entry.id];\n              if (!wall) return null;\n              const a = floor.nodes[wall.a];\n              const b = floor.nodes[wall.b];\n              if (!a || !b) return null;\n              return (\n                <line\n                  key={entry.id}\n                  x1={a.x}\n                  y1={a.y}\n                  x2={b.x}\n                  y2={b.y}\n                  strokeWidth={wall.thickness}\n                  className={styles.planCanvasWallSelected}\n                />\n              );\n            })}\n          {hovered?.type === 'wall' &&\n            !isSelected('wall', hovered.id) &&\n            (() => {\n              const wall = floor.walls[hovered.id];\n              if (!wall) return null;\n              const a = floor.nodes[wall.a];\n              const b = floor.nodes[wall.b];\n              if (!a || !b) return null;\n              return (\n                <line\n                  x1={a.x}\n                  y1={a.y}\n                  x2={b.x}\n                  y2={b.y}\n                  strokeWidth={wall.thickness}\n                  className={styles.planCanvasWallHovered}\n                />\n              );\n            })()}\n        </g>\n\n        {worldOverlay}\n      </g>\n\n      {/* Screen space: sized in pixels, so labels and handles stay constant under zoom */}\n      <g className={cx(styles.planCanvasScreen, classNames?.overlay)} style={slotStyles?.overlay}>\n        {showRoomLabels &&\n          rooms.map((room) => {\n            const at = toScreen(room.anchor);\n            if (at.x < -200 || at.y < -200 || at.x > size.width + 200 || at.y > size.height + 200) {\n              return null;\n            }\n            const custom = renderRoomLabel?.(room.meta, { area: room.area, faceId: room.id });\n            if (custom === null) return null;\n\n            return (\n              <g\n                key={room.id}\n                className={cx(styles.planCanvasRoomLabel, classNames?.roomLabel)}\n                style={slotStyles?.roomLabel}\n                transform={`translate(${at.x} ${at.y})`}\n                aria-hidden='true'\n              >\n                {custom !== undefined ? (\n                  custom\n                ) : (\n                  <>\n                    <text className={styles.planCanvasRoomName} textAnchor='middle'>\n                      {room.meta?.name ?? ''}\n                    </text>\n                    <text className={styles.planCanvasRoomArea} textAnchor='middle' dy='1.2em'>\n                      {formatArea(room.area)}\n                    </text>\n                  </>\n                )}\n              </g>\n            );\n          })}\n        {overlay}\n      </g>\n    </svg>\n  );\n}\n\nexport default PlanCanvas;\n","import { useCallback, useRef, useState } from 'react';\n\nimport type { FloorPlan } from '../floorPlan';\nimport type { RoomDrawerChangeReason } from './roomDrawer.types';\n\n/**\n * Undo/redo over plan snapshots.\n *\n * Snapshots rather than command objects: every mutator already returns a fresh\n * immutable plan with structural sharing, so a snapshot costs a handful of pointers.\n * Commands would mean writing and testing an inverse for each of fifteen operations,\n * and an `addWall` that split three other walls on its way has no cheap inverse at all.\n *\n * The gesture API is what keeps a 200-move drag to ONE entry: `begin` stashes the plan\n * as it was, `update` replaces the present without pushing, and `end` pushes that single\n * stashed snapshot. A gesture that was aborted, or that ended where it started, pushes\n * nothing.\n */\n\n/** Discrete edits of the same kind on the same target merge if they land within this. */\nconst MERGE_WINDOW_MS = 500;\n\nexport interface PlanHistory {\n  present: FloorPlan;\n  canUndo: boolean;\n  canRedo: boolean;\n\n  /** Stashes the current plan as the snapshot a later `endGesture` would restore to. */\n  beginGesture: () => void;\n  /** Replaces the present mid-gesture. Pushes nothing. */\n  updateGesture: (plan: FloorPlan) => void;\n  /** Pushes the stashed snapshot, if the gesture actually changed anything. */\n  endGesture: () => boolean;\n  /** Discards the gesture and restores the stashed snapshot. */\n  cancelGesture: () => FloorPlan | null;\n\n  /** A discrete edit: pushes and replaces atomically. */\n  commit: (plan: FloorPlan, reason: RoomDrawerChangeReason, targetIds?: string[]) => void;\n  /** Replaces the present without touching history, for an externally driven value. */\n  replace: (plan: FloorPlan) => void;\n\n  undo: () => FloorPlan | null;\n  redo: () => FloorPlan | null;\n  clearHistory: () => void;\n  isGestureActive: () => boolean;\n}\n\nexport function usePlanHistory(initial: FloorPlan, limit = 50): PlanHistory {\n  const [present, setPresent] = useState<FloorPlan>(initial);\n  const pastRef = useRef<FloorPlan[]>([]);\n  const futureRef = useRef<FloorPlan[]>([]);\n  const gestureStartRef = useRef<FloorPlan | null>(null);\n  const presentRef = useRef<FloorPlan>(initial);\n  const lastCommitRef = useRef<{ reason: string; targets: string; at: number } | null>(null);\n  const [, forceCounter] = useState(0);\n\n  const setBoth = useCallback((plan: FloorPlan) => {\n    presentRef.current = plan;\n    setPresent(plan);\n  }, []);\n\n  const push = useCallback(\n    (snapshot: FloorPlan) => {\n      pastRef.current = [...pastRef.current, snapshot].slice(-limit);\n      futureRef.current = [];\n    },\n    [limit],\n  );\n\n  const beginGesture = useCallback(() => {\n    gestureStartRef.current = presentRef.current;\n  }, []);\n\n  const updateGesture = useCallback(\n    (plan: FloorPlan) => {\n      setBoth(plan);\n    },\n    [setBoth],\n  );\n\n  const endGesture = useCallback(() => {\n    const start = gestureStartRef.current;\n    gestureStartRef.current = null;\n    if (!start || start === presentRef.current) return false;\n    push(start);\n    lastCommitRef.current = null;\n    forceCounter((n) => n + 1);\n    return true;\n  }, [push]);\n\n  const cancelGesture = useCallback(() => {\n    const start = gestureStartRef.current;\n    gestureStartRef.current = null;\n    if (!start) return null;\n    setBoth(start);\n    return start;\n  }, [setBoth]);\n\n  const commit = useCallback(\n    (plan: FloorPlan, reason: RoomDrawerChangeReason, targetIds: string[] = []) => {\n      if (plan === presentRef.current) return;\n      const targets = targetIds.join(',');\n      const now = Date.now();\n      const last = lastCommitRef.current;\n      // Typing a room name would otherwise flood the stack with one entry per keystroke.\n      const mergeable =\n        last !== null &&\n        last.reason === reason &&\n        last.targets === targets &&\n        now - last.at < MERGE_WINDOW_MS &&\n        pastRef.current.length > 0;\n\n      if (!mergeable) push(presentRef.current);\n      else futureRef.current = [];\n\n      lastCommitRef.current = { reason, targets, at: now };\n      setBoth(plan);\n      forceCounter((n) => n + 1);\n    },\n    [push, setBoth],\n  );\n\n  const replace = useCallback(\n    (plan: FloorPlan) => {\n      setBoth(plan);\n    },\n    [setBoth],\n  );\n\n  const undo = useCallback(() => {\n    const past = pastRef.current;\n    if (past.length === 0) return null;\n    const previous = past[past.length - 1];\n    pastRef.current = past.slice(0, -1);\n    futureRef.current = [presentRef.current, ...futureRef.current];\n    lastCommitRef.current = null;\n    setBoth(previous);\n    forceCounter((n) => n + 1);\n    return previous;\n  }, [setBoth]);\n\n  const redo = useCallback(() => {\n    const future = futureRef.current;\n    if (future.length === 0) return null;\n    const next = future[0];\n    futureRef.current = future.slice(1);\n    pastRef.current = [...pastRef.current, presentRef.current].slice(-limit);\n    lastCommitRef.current = null;\n    setBoth(next);\n    forceCounter((n) => n + 1);\n    return next;\n  }, [limit, setBoth]);\n\n  const clearHistory = useCallback(() => {\n    pastRef.current = [];\n    futureRef.current = [];\n    lastCommitRef.current = null;\n    forceCounter((n) => n + 1);\n  }, []);\n\n  const isGestureActive = useCallback(() => gestureStartRef.current !== null, []);\n\n  return {\n    present,\n    canUndo: pastRef.current.length > 0,\n    canRedo: futureRef.current.length > 0,\n    beginGesture,\n    updateGesture,\n    endGesture,\n    cancelGesture,\n    commit,\n    replace,\n    undo,\n    redo,\n    clearHistory,\n    isGestureActive,\n  };\n}\n",".roomDrawer {\n  display: flex;\n  flex-direction: column;\n  gap: var(--spacing-s, 8px);\n  color: var(--text-color);\n  position: relative;\n\n  &:focus-visible {\n    outline: 2px solid var(--focus-border-color, var(--primary-color));\n    outline-offset: 2px;\n    border-radius: var(--default-border-radius);\n  }\n\n  &-disabled {\n    opacity: 0.6;\n    pointer-events: none;\n  }\n\n  /* ── Toolbar ── */\n\n  &-toolbar {\n    display: flex;\n    align-items: center;\n    flex-wrap: wrap;\n    gap: 4px;\n    padding: 4px;\n    background: var(--background-accent);\n    border: 1px solid var(--border-color);\n    border-radius: var(--default-border-radius);\n\n    &-separator {\n      width: 1px;\n      align-self: stretch;\n      margin: 2px 4px;\n      background: var(--border-color);\n    }\n  }\n\n  &-toolButton,\n  &-viewButton {\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    min-width: 34px;\n    height: 34px;\n    padding: 0 8px;\n    border: 1px solid transparent;\n    border-radius: var(--default-border-radius);\n    background: transparent;\n    color: var(--text-color);\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n\n    &:hover:not(:disabled) {\n      background: var(--background-accent-light);\n    }\n\n    &:focus-visible {\n      outline: none;\n      border-color: var(--focus-border-color, var(--primary-color));\n    }\n\n    &:disabled {\n      opacity: 0.4;\n      cursor: default;\n    }\n  }\n\n  &-toolButtonActive {\n    background: var(--primary-color);\n    color: var(--text-on-primary);\n\n    &:hover {\n      background: var(--primary-color);\n    }\n  }\n\n  &-toolIcon {\n    pointer-events: none;\n  }\n\n  &-viewControls {\n    display: inline-flex;\n    align-items: center;\n    gap: 2px;\n    margin-left: auto;\n  }\n\n  &-zoomLabel {\n    min-width: 62px;\n    height: 34px;\n    padding: 0 6px;\n    border: 1px solid transparent;\n    border-radius: var(--default-border-radius);\n    background: transparent;\n    color: var(--text-dark);\n    font-size: 12px;\n    font-variant-numeric: tabular-nums;\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n\n    &:hover {\n      background: var(--background-accent-light);\n    }\n\n    &:focus-visible {\n      outline: none;\n      border-color: var(--focus-border-color, var(--primary-color));\n    }\n  }\n\n  /* Storeys, top one first. A strip rather than a dropdown because switching floors is\n     something you do constantly while drawing, and a dropdown costs two clicks each time. */\n  &-floorBar {\n    display: flex;\n    align-items: center;\n    gap: 2px;\n    flex-wrap: wrap;\n    padding: var(--spacing-xs, 4px);\n    background: var(--background-accent);\n    border: 1px solid var(--border-color);\n    border-radius: var(--default-border-radius);\n  }\n\n  &-floorTab {\n    height: 28px;\n    padding: 0 10px;\n    border: 1px solid transparent;\n    border-radius: var(--default-border-radius);\n    background: transparent;\n    color: var(--text-dark);\n    font-size: 12px;\n    white-space: nowrap;\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n\n    &:hover {\n      background: var(--background-accent-light);\n    }\n\n    &:focus-visible {\n      outline: none;\n      border-color: var(--focus-border-color, var(--primary-color));\n    }\n\n    &-active {\n      background: var(--primary-color);\n      border-color: var(--primary-color);\n      color: var(--text-on-primary);\n      font-weight: 600;\n\n      &:hover {\n        background: var(--primary-color);\n      }\n    }\n\n    &-add {\n      padding: 0 8px;\n      color: var(--text-dark);\n    }\n  }\n\n  /* ── Canvas ── */\n\n  &-canvasWrapper {\n    position: relative;\n    overflow: hidden;\n    border: 1px solid var(--border-color);\n    border-radius: var(--default-border-radius);\n    background: var(--background);\n\n    /* Without this the browser claims the gesture and pointermove stops firing mid-drag. */\n    touch-action: none;\n    cursor: default;\n    -webkit-tap-highlight-color: transparent;\n\n    &-drawing {\n      cursor: crosshair;\n    }\n  }\n\n  /* ── Screen-space overlay ── */\n\n  &-preview {\n    fill: var(--primary-accent);\n    fill-opacity: 0.12;\n    stroke: var(--primary-color);\n    stroke-width: 1.5;\n    stroke-dasharray: 5 4;\n  }\n\n  &-marquee {\n    fill: var(--primary-color);\n    fill-opacity: 0.1;\n    stroke: var(--primary-color);\n    stroke-width: 1;\n    stroke-dasharray: 4 3;\n  }\n\n  &-handle {\n    fill: var(--background);\n    stroke: var(--primary-color);\n    stroke-width: 2;\n\n    &-active {\n      fill: var(--primary-color);\n    }\n  }\n\n  /* A latched toggle (snap, angle lock, grid): on, but not the active tool. */\n  &-toolButtonOn {\n    border-color: var(--primary-color);\n    color: var(--primary-color);\n  }\n\n  /* ── Snap feedback ── */\n\n  &-guide {\n    stroke: var(--plan-guide-color, var(--info-color));\n    stroke-width: 1;\n    stroke-dasharray: 4 4;\n    opacity: 0.8;\n    pointer-events: none;\n\n    /* Angle lock gets the brand colour: it is the one the user asked for by name. */\n    &-angle {\n      stroke: var(--primary-color);\n      stroke-dasharray: 6 3;\n      opacity: 0.9;\n    }\n  }\n\n  &-snapMarker {\n    fill: none;\n    stroke: var(--primary-color);\n    stroke-width: 1.5;\n    pointer-events: none;\n  }\n\n  /* A measurement, not geometry: dashed and in the accent colour so it can never be\n     mistaken for a wall that was actually drawn. */\n  &-measureLine {\n    pointer-events: none;\n\n    line {\n      stroke: var(--plan-measure-color, var(--primary-color));\n      stroke-width: 1.25;\n      stroke-dasharray: 5 4;\n    }\n\n    line + line {\n      stroke-dasharray: none;\n    }\n\n    &-hovered line {\n      stroke-width: 2;\n      opacity: 1;\n    }\n  }\n\n  /* Only drawn while the ruler is the active tool, so it never competes with a corner\n     handle for the same pixel. */\n  &-measureGrip {\n    fill: var(--background);\n    stroke: var(--plan-measure-color, var(--primary-color));\n    stroke-width: 1.5;\n    cursor: grab;\n    -webkit-tap-highlight-color: transparent;\n  }\n\n  /* Hollow and smaller than a corner handle: it marks a corner that does not exist yet. */\n  &-midHandle {\n    fill: var(--background);\n    stroke: var(--primary-color);\n    stroke-width: 1.5;\n    stroke-dasharray: 2.5 2;\n  }\n\n  &-measure {\n    fill: var(--text-color);\n    font-size: 11px;\n    font-variant-numeric: tabular-nums;\n    paint-order: stroke;\n    stroke: var(--background);\n    stroke-width: 3px;\n    stroke-linejoin: round;\n\n    &-locked {\n      fill: var(--primary-color);\n      font-weight: 600;\n    }\n\n    /* How far a room has been dragged. Carries a chip behind it because it floats over the\n       room's own fill rather than sitting on a wall, where the halo alone would do. */\n    &-offset {\n      fill: var(--text-on-primary);\n      font-weight: 600;\n      paint-order: stroke;\n      stroke: var(--primary-color);\n      stroke-width: 8px;\n      stroke-linejoin: round;\n    }\n\n    /* A consequence of the edit rather than its subject: still readable, but clearly\n       secondary to the wall or corner actually being moved. */\n    &-secondary {\n      fill: var(--text-dark);\n      opacity: 0.75;\n    }\n  }\n\n  /* ── Inspector ── */\n\n  &-inspector {\n    display: flex;\n    align-items: center;\n    flex-wrap: wrap;\n    gap: 8px;\n    padding: 8px;\n    background: var(--background-accent);\n    border: 1px solid var(--border-color);\n    border-radius: var(--default-border-radius);\n\n    &-action {\n      display: inline-flex;\n      align-items: center;\n      justify-content: center;\n      gap: 6px;\n      height: 34px;\n      padding: 0 12px;\n      border: 1px solid var(--border-color);\n      border-radius: var(--default-border-radius);\n      background: var(--background);\n      color: var(--text-color);\n      font-size: 13px;\n      cursor: pointer;\n      -webkit-tap-highlight-color: transparent;\n\n      &:hover:not(:disabled) {\n        background: var(--background-accent-light);\n      }\n\n      &:disabled {\n        opacity: 0.4;\n        cursor: default;\n      }\n\n      &:focus-visible {\n        outline: none;\n        border-color: var(--focus-border-color, var(--primary-color));\n      }\n    }\n\n    &-label {\n      color: var(--text-dark);\n      font-size: 12px;\n      font-variant-numeric: tabular-nums;\n    }\n\n    /* What is selected. Carries the bar on its own for a corner, which has nothing to edit\n       but a position that is already being edited by dragging it. */\n    &-kind {\n      display: flex;\n      align-items: center;\n      flex: none;\n\n      /* Always rendered, so it is what holds the row open to a field's height whether or\n         not the selection has any fields. Setting this on the bar itself would not do:\n         it is border-box, so the figure would have to know the padding. */\n      min-height: 34px;\n      color: var(--text-dark);\n      font-size: 11px;\n      font-weight: 600;\n      letter-spacing: 0.04em;\n      text-transform: uppercase;\n      white-space: nowrap;\n    }\n\n    &-kindEmpty {\n      opacity: 0.6;\n      font-weight: 500;\n      text-transform: none;\n      letter-spacing: 0;\n      font-size: 12px;\n    }\n\n    /* Sized to their contents, so the row stays a row. A field at its natural full width\n       wrapped onto a line of its own and took the bar to three of them. They still wrap\n       when the drawer is genuinely too narrow for them. */\n    &-field {\n      flex: none;\n\n      /* Wide enough for the longest floating label, `Thickness (mm)`, plus the stepper\n         arrows: a narrower box left the label hanging over its own border. */\n      width: 142px;\n    }\n\n    &-name {\n      flex: 1 1 150px;\n      min-width: 120px;\n      max-width: 280px;\n    }\n\n    /* The one action that applies to every kind of selection, and the only destructive one,\n       so it sits apart at the end rather than in the run of fields. */\n    &-delete {\n      margin-left: auto;\n    }\n  }\n\n  /* ── Hint ── */\n\n  &-hint {\n    color: var(--text-dark);\n    font-size: 12px;\n    line-height: 1.4;\n\n    /* What an opened file needed doing to it. Louder than a hint because it is the only\n       account of what happened to the user's file. */\n    &-report {\n      color: var(--error-color, var(--text-color));\n      font-weight: 500;\n    }\n  }\n\n  /* The picker is opened from the toolbar button, so the input itself is never shown.\n     Hidden by clipping rather than `display: none`, which stops `.click()` opening the\n     dialog in some browsers. */\n  &-fileInput {\n    position: absolute;\n    width: 1px;\n    height: 1px;\n    padding: 0;\n    border: 0;\n    clip-path: inset(50%);\n    overflow: hidden;\n    white-space: nowrap;\n  }\n}\n","import {\n  useCallback,\n  useEffect,\n  useImperativeHandle,\n  useMemo,\n  useRef,\n  useState,\n  type PointerEvent as ReactPointerEvent,\n  type ReactElement,\n} from 'react';\nimport cx from 'classnames';\nimport {\n  faArrowPointer,\n  faArrowRotateLeft,\n  faArrowRotateRight,\n  faBorderAll,\n  faClone,\n  faDoorOpen,\n  faDownload,\n  faDraftingCompass,\n  faExpand,\n  faMagnet,\n  faMagnifyingGlassMinus,\n  faMagnifyingGlassPlus,\n  faAngleDown,\n  faAngleUp,\n  faLayerGroup,\n  faMinus,\n  faPlus,\n  faRulerCombined,\n  faSquare,\n  faUpload,\n  faTrash,\n  faWindowMaximize,\n} from '@fortawesome/free-solid-svg-icons';\n\nimport ActionIcon from '../actionIcon';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport { useCoarsePointer } from '../hooks/useCoarsePointer';\nimport Input from '../input';\nimport NumberInput from '../numberInput';\nimport PlanCanvas from '../planCanvas';\nimport { renderIcon } from '../utils/renderIcon';\nimport {\n  DEFAULT_WALL_ATTRS,\n  HIT_PX,\n  HIT_PX_COARSE,\n  OpeningKind,\n  SnapKind,\n  WallType,\n  addOpening,\n  addRect,\n  addWall,\n  clientToWorld,\n  commitDerivation,\n  derivePlan,\n  detachWall,\n  dissolveNode,\n  emptyFloorPlan,\n  floorBounds,\n  getFloor,\n  hitTest,\n  mergeNodes,\n  MIN_WALL_LENGTH,\n  addFloor,\n  duplicateFloor,\n  floorBelow,\n  floorIndex,\n  moveNode,\n  MIN_FLOOR_HEIGHT,\n  moveFloor,\n  planFromJson,\n  planToJson,\n  removeFloor,\n  renameFloor,\n  setFloorHeight,\n  removeOpening,\n  removeWall,\n  resolveSnap,\n  selectionsEqual,\n  setFloor,\n  splitWall,\n  updateOpening,\n  weldShortWalls,\n  worldTolerances,\n  type FloorPlan,\n  type PlanFloor,\n  type PlanParseResult,\n  type PlanPoint,\n  type PlanSelection,\n  type PlanView,\n  type SnapResult,\n} from '../floorPlan';\nimport { pointToSegment } from '../floorPlan/floorPlan.geometry';\nimport { usePlanHistory } from './usePlanHistory';\nimport type {\n  RoomDrawerChangeMeta,\n  RoomDrawerChangeReason,\n  RoomDrawerLabels,\n  RoomDrawerProps,\n  RoomDrawerTool,\n} from './roomDrawer.types';\nimport styles from './roomDrawer.module.pcss';\nimport { ThemeColor } from '../themeProvider/color.types';\n\nconst ALL_TOOLS: RoomDrawerTool[] = ['select', 'wall', 'rect', 'door', 'window', 'archway', 'measure'];\n\nconst TOOL_KEYS: Record<string, RoomDrawerTool> = {\n  v: 'select',\n  w: 'wall',\n  r: 'rect',\n  d: 'door',\n  n: 'window',\n  a: 'archway',\n  m: 'measure',\n};\n\nconst OPENING_TOOL_KIND: Partial<Record<RoomDrawerTool, OpeningKind>> = {\n  door: OpeningKind.Door,\n  window: OpeningKind.Window,\n  archway: OpeningKind.Archway,\n};\n\nconst BUILT_IN_OPENING_DEFAULTS: Record<OpeningKind, { width: number; sill: number; head: number }> = {\n  [OpeningKind.Door]: { width: 900, sill: 0, head: 2100 },\n  [OpeningKind.Window]: { width: 1200, sill: 900, head: 2100 },\n  [OpeningKind.Archway]: { width: 1200, sill: 0, head: 2100 },\n  [OpeningKind.Gap]: { width: 1000, sill: 0, head: 2500 },\n};\n\n/** Below this length a ruler drag is a slip of the hand, not a measurement. */\nconst EPS_MEASURE = 1;\n\n/** Past this many pixels a press becomes a drag rather than a click. */\nconst DRAG_THRESHOLD_PX = 4;\n\n/** Stable identity for an empty selection, so memos keyed on it do not re-run. */\nconst EMPTY_SELECTION: readonly PlanSelection[] = [];\n\ntype Gesture =\n  | { kind: 'none' }\n  | { kind: 'pan'; startClient: PlanPoint; startView: PlanView }\n  | { kind: 'pinch'; startDist: number; startView: PlanView; startMid: PlanPoint }\n  /**\n   * `origin`, `referenceAngle` and `excludeWallIds` are captured ONCE at gesture start.\n   * Recomputing them per frame makes them follow the node being dragged, so angle lock\n   * ends up measuring from a basis that rotates with the cursor, and the walls attached\n   * to the corner become snap targets for the corner itself.\n   */\n  | {\n      kind: 'node';\n      nodeId: string;\n      moved: boolean;\n      origin?: PlanPoint;\n      referenceAngle?: number;\n      excludeWallIds: ReadonlySet<string>;\n    }\n  | {\n      kind: 'wall';\n      wallId: string;\n      startWorld: PlanPoint;\n      origA: PlanPoint;\n      origB: PlanPoint;\n      moved: boolean;\n      /** Ctrl was held: pull the wall off its neighbours on the first real movement. */\n      detach?: boolean;\n      /** Which way it was pulled, once it has been. It cannot be pushed back across that line. */\n      detachedSign?: number;\n      /** A Ctrl press that has not become a drag yet, and so may still be a selection toggle. */\n      toggle?: boolean;\n    }\n  | { kind: 'opening'; openingId: string; moved: boolean }\n  | { kind: 'room'; nodeIds: string[]; startWorld: PlanPoint; orig: Record<string, PlanPoint>; moved: boolean }\n  /**\n   * The midpoint handle of a selected wall has been pressed. The split is deliberately\n   * deferred until the pointer actually moves, so a stray click on the handle leaves the\n   * wall exactly as it was instead of silently adding a corner.\n   */\n  | { kind: 'wallSplit'; wallId: string; at: PlanPoint }\n  | { kind: 'rect'; start: PlanPoint; current: PlanPoint }\n  | { kind: 'measure'; start: PlanPoint; current: PlanPoint }\n  /** Dragging one end of a measurement already taken. */\n  | { kind: 'measureEnd'; id: number; end: 'a' | 'b' }\n  /** Sliding a whole measurement, both ends together. */\n  | { kind: 'measureMove'; id: number; startWorld: PlanPoint; origA: PlanPoint; origB: PlanPoint }\n  /** Rubber-band selection over empty canvas. `base` is what was selected before it began. */\n  | {\n      kind: 'marquee';\n      start: PlanPoint;\n      current: PlanPoint;\n      additive: boolean;\n      base: readonly PlanSelection[];\n    };\n\nfunction defaultFormatLength(mm: number): string {\n  return mm >= 1000 ? `${(mm / 1000).toFixed(2)} m` : `${Math.round(mm)} mm`;\n}\n\nfunction defaultFormatArea(mm2: number): string {\n  const m2 = mm2 / 1_000_000;\n  return `${m2.toFixed(m2 < 10 ? 1 : 0)} m²`;\n}\n\nfunction defaultFormatAngle(degrees: number): string {\n  return `${Math.round(degrees * 10) / 10}°`;\n}\n\nfunction defaultFormatZoom(zoom: number): string {\n  return `${Math.round(zoom * 1000) / 10}%`;\n}\n\n/** Radians to a signed degree reading in (-180, 180], which is how a plan angle reads. */\nfunction toDegrees(radians: number): number {\n  const deg = ((((radians * 180) / Math.PI) % 360) + 360) % 360;\n  return deg > 180 ? deg - 360 : deg;\n}\n\nconst DEFAULT_LABELS: Required<RoomDrawerLabels> = {\n  tools: 'Drawing tools',\n  storeyHeightHint: 'Storey height. The floors above move to sit on top of it.',\n  sillHeightHint: 'How far above the floor the opening starts. Stored for a 3D view; it does not change the plan.',\n  select: 'Select and edit',\n  wall: 'Draw walls',\n  rect: 'Draw a room',\n  measure: 'Measure',\n  door: 'Add a door',\n  window: 'Add a window',\n  archway: 'Add an archway',\n  undo: 'Undo',\n  redo: 'Redo',\n  download: 'Download plan',\n  upload: 'Open a plan file',\n  underlay: 'Show the floor below',\n  addFloor: 'Add a floor above',\n  floorName: 'Floor name',\n  duplicateFloor: 'Copy this floor to a new one above',\n  deleteFloor: 'Delete this floor',\n  floorHeight: 'Height (mm)',\n  moveFloorUp: 'Move this floor up',\n  moveFloorDown: 'Move this floor down',\n  newRoomName: (n) => `Room ${n}`,\n  newFloorName: (n) => `Floor ${n}`,\n  copiedFloorName: (name) => `${name} copy`,\n  initialFloorName: 'Ground floor',\n  selectedFloor: 'Floor',\n  importFailed: 'That file could not be read as a floor plan',\n  snap: 'Snap to grid and geometry',\n  angleLock: (angle) => `Lock to ${angle}° angles`,\n  grid: 'Show grid',\n  zoomIn: 'Zoom in',\n  zoomOut: 'Zoom out',\n  fit: 'Fit to content',\n  roomName: 'Room name',\n  addCorner: 'Add corner',\n  thickness: 'Thickness (mm)',\n  width: 'Width (mm)',\n  sill: 'Sill (mm)',\n  deleteSelection: 'Delete selection',\n  selectedCount: (count) => `${count} selected`,\n  selectedRoom: 'Room',\n  selectedWall: 'Wall',\n  selectedCorner: 'Corner',\n  selectedDoor: 'Door',\n  selectedWindow: 'Window',\n  selectedArchway: 'Archway',\n  selectedGap: 'Gap',\n  hintSelect:\n    'Drag a corner to reshape · Drag a wall to push it, and the room next door moves with it · Ctrl-click or drag a box to select several · Delete to remove',\n  hintSelectSingle:\n    'Drag a corner to reshape · Drag a wall to push it, and the room next door moves with it · Drag the dot on a selected wall to add a corner · Delete to remove',\n  hintWall:\n    'Click to place corners · Shift for a fixed angle, L to latch it · Click the first corner or press Enter to close',\n  hintRect: 'Drag to draw a room',\n  hintMeasure: 'Drag to measure · Drag an end or the line to adjust it · Delete removes one, Escape clears them',\n  hintOpening: 'Click a wall to cut an opening into it',\n  relativeAngle: (angle) => `${angle} rel`,\n};\n\nfunction RoomDrawer(props: RoomDrawerProps) {\n  const {\n    ref,\n    value,\n    defaultValue,\n    onChange,\n    onChangeEnd,\n    floorId,\n    defaultFloorId,\n    onFloorChange,\n    showFloorBar = true,\n    showUnderlay = true,\n\n    tool: toolProp,\n    defaultTool = 'select',\n    onToolChange,\n    tools = ALL_TOOLS,\n\n    selection: selectionProp,\n    defaultSelection,\n    onSelectionChange,\n    multiSelect = true,\n    onHoverChange,\n\n    view: viewProp,\n    defaultView,\n    onViewChange,\n    minZoom = 0.005,\n    maxZoom = 2,\n    fitOnMount = true,\n\n    gridSize = 100,\n    showGrid = true,\n    snap = true,\n    angleLock = false,\n    constrainAngle = 45,\n    showSnapGuides = true,\n\n    wallThickness = DEFAULT_WALL_ATTRS.thickness,\n    wallHeight,\n    openingDefaults,\n\n    showMeasurements = true,\n    formatLength = defaultFormatLength,\n    formatArea = defaultFormatArea,\n    formatAngle = defaultFormatAngle,\n    formatZoom = defaultFormatZoom,\n\n    showToolbar = true,\n    showInspector = true,\n    showHint = true,\n    showHistoryControls = true,\n    showFileControls = true,\n    fileName,\n    onImport,\n\n    historyMode = 'internal',\n    historyLimit = 50,\n    onHistoryChange,\n\n    readOnly = false,\n    disabled = false,\n\n    canvasHeight = '420px',\n    getRoomColor,\n    renderRoomLabel,\n\n    labels,\n    icons,\n    'aria-label': ariaLabel,\n\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    ...rest\n  } = useComponentDefaults('RoomDrawer', props);\n  const text = useLabels('RoomDrawer', labels, DEFAULT_LABELS);\n  // The hooks below list these rather than `text`, which is a new object every render.\n  const { newRoomName, newFloorName, copiedFloorName, initialFloorName, relativeAngle } = text;\n\n  const coarse = useCoarsePointer();\n  const hitPx = coarse ? HIT_PX_COARSE : HIT_PX;\n\n  const rootRef = useRef<HTMLDivElement>(null);\n  const canvasWrapRef = useRef<HTMLDivElement>(null);\n  const svgRef = useRef<SVGSVGElement>(null);\n  const lastEmittedRef = useRef<FloorPlan | null>(null);\n  const gestureRef = useRef<Gesture>({ kind: 'none' });\n  const pointersRef = useRef<Map<number, PlanPoint>>(new Map());\n  const pressRef = useRef<{ client: PlanPoint; moved: boolean } | null>(null);\n  const spaceHeldRef = useRef(false);\n  const altHeldRef = useRef(false);\n  const shiftHeldRef = useRef(false);\n\n  // ─── Plan state ───────────────────────────────────────────────────────────────\n\n  const initialPlan = useMemo(\n    () => defaultValue ?? value ?? emptyFloorPlan({ floorLabel: initialFloorName }),\n    // Only ever read once, to seed the uncontrolled state.\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n    [],\n  );\n  const history = usePlanHistory(initialPlan, historyLimit);\n  const isControlled = value !== undefined;\n  const plan = isControlled ? value : history.present;\n\n  // Keep the internal history in step with an externally driven value, so undo still\n  // works for a controlled consumer that has not implemented its own.\n  useEffect(() => {\n    if (!isControlled || !value) return;\n    if (value === lastEmittedRef.current) {\n      history.replace(value);\n      return;\n    }\n    history.commit(value, 'external');\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [value, isControlled]);\n\n  const [floorIdState, setFloorIdState] = useState<string | undefined>(defaultFloorId);\n  // The floor a stale id points at is gone, so fall back to the lowest rather than\n  // rendering nothing: a plan always has at least one floor.\n  const activeFloorId = floorId ?? floorIdState ?? plan.floors[0]?.id;\n  const floor: PlanFloor = useMemo(\n    () => getFloor(plan, activeFloorId) ?? getFloor(plan) ?? emptyFloorPlan().floors[0],\n    [plan, activeFloorId],\n  );\n  const [underlayOn, setUnderlayOn] = useState(showUnderlay);\n  useEffect(() => setUnderlayOn(showUnderlay), [showUnderlay]);\n  const underlayFloor = useMemo(() => (underlayOn ? floorBelow(plan, floor.id) : null), [underlayOn, plan, floor.id]);\n\n  const selectFloor = useCallback(\n    (nextId: string) => {\n      if (nextId === floor.id) return;\n      if (floorId === undefined) setFloorIdState(nextId);\n      onFloorChange?.(nextId);\n    },\n    [floor.id, floorId, onFloorChange],\n  );\n\n  // Memoised on the floor identity, so a gesture that changed nothing re-derives nothing.\n  const derivation = useMemo(() => derivePlan(floor), [floor]);\n  const rooms = derivation.rooms;\n\n  // ─── Tool / selection / view ──────────────────────────────────────────────────\n\n  const [toolState, setToolState] = useState<RoomDrawerTool>(defaultTool);\n  const tool = toolProp ?? toolState;\n  const setTool = useCallback(\n    (next: RoomDrawerTool) => {\n      if (toolProp === undefined) setToolState(next);\n      onToolChange?.(next);\n    },\n    [toolProp, onToolChange],\n  );\n\n  const [selectionState, setSelectionState] = useState<readonly PlanSelection[]>(defaultSelection ?? EMPTY_SELECTION);\n  const selection = selectionProp !== undefined ? selectionProp : selectionState;\n  /**\n   * Mirrors `selection` synchronously, so `handle.select(x)` immediately followed by\n   * `handle.deleteSelection()` acts on `x` rather than on whatever was selected before\n   * React re-rendered.\n   */\n  const selectionRef = useRef<readonly PlanSelection[]>(selection);\n  selectionRef.current = selection;\n\n  const setSelection = useCallback(\n    (next: readonly PlanSelection[] | PlanSelection | null) => {\n      const list = next === null ? EMPTY_SELECTION : Array.isArray(next) ? next : [next as PlanSelection];\n      selectionRef.current = list;\n      if (selectionProp === undefined) setSelectionState(list);\n      onSelectionChange?.([...list]);\n    },\n    [selectionProp, onSelectionChange],\n  );\n\n  /** Adds an entry if absent, removes it if present. */\n  const toggleSelection = useCallback(\n    (entry: PlanSelection) => {\n      const current = selectionRef.current;\n      const without = current.filter((e) => !(e.type === entry.type && e.id === entry.id));\n      setSelection(without.length === current.length ? [...current, entry] : without);\n    },\n    [setSelection],\n  );\n\n  /**\n   * Exactly one thing selected. The per-entity chrome (the inspector's fields, the\n   * midpoint handle) keys off this rather than off `selection[0]`, so it disappears\n   * cleanly once a second thing joins the selection instead of silently acting on\n   * whichever entry happened to come first.\n   */\n  const single = selection.length === 1 ? selection[0] : null;\n\n  /**\n   * Every node the selection implies, which is what a translation actually moves. A room\n   * is its face's corners, a wall is its two ends, a corner is itself. Openings have no\n   * corners of their own and so translate with whatever wall they are cut into.\n   */\n  const nodeIdsForSelection = useCallback(\n    (entries: readonly PlanSelection[]): Set<string> => {\n      const ids = new Set<string>();\n      for (const entry of entries) {\n        if (entry.type === 'node') {\n          ids.add(entry.id);\n        } else if (entry.type === 'wall') {\n          const wall = floor.walls[entry.id];\n          if (wall) {\n            ids.add(wall.a);\n            ids.add(wall.b);\n          }\n        } else if (entry.type === 'room') {\n          const room = rooms.find((r) => r.roomId === entry.id);\n          for (const id of room?.nodeIds ?? []) ids.add(id);\n        }\n      }\n      return ids;\n    },\n    [floor.walls, rooms],\n  );\n\n  const selectedNodeIds = useMemo(() => nodeIdsForSelection(selection), [nodeIdsForSelection, selection]);\n\n  const [hovered, setHovered] = useState<PlanSelection | null>(null);\n  const [viewState, setViewState] = useState<PlanView>(defaultView ?? { x: 0, y: 0, zoom: 0.05 });\n  const view = viewProp ?? viewState;\n  const setView = useCallback(\n    (next: PlanView) => {\n      if (viewProp === undefined) setViewState(next);\n      onViewChange?.(next);\n    },\n    [viewProp, onViewChange],\n  );\n\n  // `measured` holds the fit back until the canvas has been measured: the fit runs in the same\n  // effect flush as the first measurement, and would otherwise fit the plan to this guess.\n  const [size, setSize] = useState({ width: 800, height: 420, measured: false });\n  const [wallChain, setWallChain] = useState<PlanPoint[]>([]);\n  const [cursor, setCursor] = useState<PlanPoint | null>(null);\n\n  /**\n   * Latched snapping and angle lock. Seeded from the props, then owned by the toolbar\n   * toggles and their hotkeys; changing the prop resets the latch.\n   */\n  const [snapEnabled, setSnapEnabled] = useState(snap);\n  const [angleLockEnabled, setAngleLockEnabled] = useState(angleLock);\n  useEffect(() => setSnapEnabled(snap), [snap]);\n  useEffect(() => setAngleLockEnabled(angleLock), [angleLock]);\n\n  const [gridVisible, setGridVisible] = useState(showGrid);\n  useEffect(() => setGridVisible(showGrid), [showGrid]);\n\n  /** The most recent snap result, so the overlay can explain what it snapped to. */\n  const lastSnapRef = useRef<SnapResult | null>(null);\n  const [snapHint, setSnapHint] = useState<SnapResult | null>(null);\n\n  /**\n   * Ruler measurements. Deliberately component state rather than plan data: a line in\n   * the wall graph would close faces, and faces become rooms, so a \"guide\" stored as\n   * geometry would fight the whole derived-room model. Nothing here reaches `onChange`,\n   * a downloaded file, or the undo stack.\n   */\n  const [measurements, setMeasurements] = useState<Array<{ id: number; a: PlanPoint; b: PlanPoint }>>([]);\n  const measureSeqRef = useRef(0);\n  const [hoveredMeasure, setHoveredMeasure] = useState<number | null>(null);\n\n  const fileInputRef = useRef<HTMLInputElement>(null);\n\n  /**\n   * What of the ruler is under a point, nearest first: an end before the line, so the\n   * ends stay grabbable where they sit on top of it. Only live while the measure tool is\n   * up, so measurements are inert scenery under every other tool.\n   */\n  const measureHit = useCallback(\n    (world: PlanPoint): { id: number; end?: 'a' | 'b' } | null => {\n      const tol = worldTolerances(hitPx, view.zoom);\n      for (let i = measurements.length - 1; i >= 0; i--) {\n        const m = measurements[i];\n        if (Math.hypot(world.x - m.a.x, world.y - m.a.y) <= tol.node) return { id: m.id, end: 'a' };\n        if (Math.hypot(world.x - m.b.x, world.y - m.b.y) <= tol.node) return { id: m.id, end: 'b' };\n      }\n      for (let i = measurements.length - 1; i >= 0; i--) {\n        const m = measurements[i];\n        if (pointToSegment(world, m.a, m.b).dist <= tol.wall) return { id: m.id };\n      }\n      return null;\n    },\n    [measurements, hitPx, view.zoom],\n  );\n\n  /**\n   * What to say about the file that was just opened, or `null` when there is nothing to\n   * say. An empty array means it failed outright; a filled one means it loaded but had to\n   * be repaired. Shown in the hint line, and cleared as soon as the user carries on.\n   */\n  const [importReport, setImportReport] = useState<string[] | null>(null);\n\n  const editable = !readOnly && !disabled;\n\n  /** Direction of the last committed chain segment, so the next one can go square to it. */\n  const chainReferenceAngle = useMemo(() => {\n    if (wallChain.length < 2) return undefined;\n    const a = wallChain[wallChain.length - 2];\n    const b = wallChain[wallChain.length - 1];\n    return Math.atan2(b.y - a.y, b.x - a.x);\n  }, [wallChain]);\n\n  // ─── Sizing ───────────────────────────────────────────────────────────────────\n\n  useEffect(() => {\n    const element = canvasWrapRef.current;\n    if (!element) return undefined;\n    const observer = new ResizeObserver(() => {\n      const rect = element.getBoundingClientRect();\n      if (rect.width > 0 && rect.height > 0) {\n        setSize({ width: Math.round(rect.width), height: Math.round(rect.height), measured: true });\n      }\n    });\n    observer.observe(element);\n    const rect = element.getBoundingClientRect();\n    if (rect.width > 0 && rect.height > 0) {\n      setSize({ width: Math.round(rect.width), height: Math.round(rect.height), measured: true });\n    }\n    return () => observer.disconnect();\n  }, []);\n\n  // ─── Coordinates ──────────────────────────────────────────────────────────────\n\n  const toWorld = useCallback(\n    (clientX: number, clientY: number): PlanPoint => {\n      const rect = canvasWrapRef.current?.getBoundingClientRect() ?? { left: 0, top: 0 };\n      return clientToWorld(clientX, clientY, rect, view);\n    },\n    [view],\n  );\n\n  const toScreen = useCallback(\n    (p: PlanPoint) => ({ x: (p.x - view.x) * view.zoom, y: (p.y - view.y) * view.zoom }),\n    [view],\n  );\n\n  const snapAt = useCallback(\n    (\n      world: PlanPoint,\n      origin?: PlanPoint,\n      excludeNodeIds?: ReadonlySet<string>,\n      referenceAngle?: number,\n      excludeWallIds?: ReadonlySet<string>,\n    ) => {\n      // Alt INVERTS the current setting rather than only switching snapping off, so it\n      // doubles as a momentary \"snap just this once\" when snapping is latched off.\n      const useSnap = snapEnabled !== altHeldRef.current;\n      // Likewise Shift: momentary lock when the latch is off, momentary release when on.\n      const lockAngle = angleLockEnabled !== shiftHeldRef.current;\n      const result = resolveSnap(world, {\n        floor,\n        pxToWorld: view.zoom > 0 ? 1 / view.zoom : 1,\n        origin,\n        referenceAngle,\n        excludeNodeIds,\n        excludeWallIds,\n        options: {\n          enabled: useSnap\n            ? [\n                SnapKind.Node,\n                SnapKind.Intersection,\n                SnapKind.Wall,\n                SnapKind.Angle,\n                SnapKind.Extension,\n                SnapKind.Alignment,\n                SnapKind.Grid,\n              ]\n            : [],\n          gridSize,\n          angleStepDeg: lockAngle ? constrainAngle : 0,\n          toleranceScreenPx: coarse ? 16 : 10,\n        },\n      });\n      lastSnapRef.current = result;\n      return result;\n    },\n    [floor, view.zoom, snapEnabled, angleLockEnabled, gridSize, constrainAngle, coarse],\n  );\n\n  /**\n   * The snapping context for dragging a corner, measured once before anything moves.\n   *\n   * `origin` is the far end of one incident wall, so angle lock has something fixed to\n   * measure from. `referenceAngle` is the direction of a wall meeting the graph at that\n   * ORIGIN, which makes \"square to the run this corner hangs off\" available. It is taken\n   * from there rather than from a second incident wall because neither of the origin\n   * wall's endpoints moves during the drag, so the basis stays put; a wall touching the\n   * dragged corner would rotate as the corner moves and the lock would chase itself.\n   *\n   * Every incident wall is excluded from snapping: they move with the corner, so leaving\n   * them in means the corner snaps to itself.\n   */\n  const nodeDragContext = useCallback(\n    (nodeId: string): { origin?: PlanPoint; referenceAngle?: number; excludeWallIds: ReadonlySet<string> } => {\n      const incident = Object.values(floor.walls).filter((w) => w.a === nodeId || w.b === nodeId);\n      // Every wall meeting the corner moves with it, so none of them may be snapped to:\n      // leaving them in means the corner snaps to itself.\n      const excludeWallIds = new Set(incident.map((w) => w.id));\n\n      const node = floor.nodes[nodeId];\n      if (!node) return { excludeWallIds };\n\n      // Angle lock constrains the DIRECTION OF TRAVEL, measured from where the corner\n      // started, which is what Shift-dragging means in every editor and makes all eight\n      // directions reachable. Measuring from a neighbouring corner instead (as this once\n      // did) only ever puts one ray near the corner you are holding: the one running\n      // along the existing wall. Everything else lands metres away, so in practice the\n      // lock appeared to work in one direction only.\n      const origin = { x: node.x, y: node.y };\n\n      // Plus the direction of a wall meeting here, so travel can also be taken parallel\n      // or square to it rather than only to the page.\n      let referenceAngle: number | undefined;\n      const first = incident[0];\n      if (first) {\n        const farId = first.a === nodeId ? first.b : first.a;\n        const far = floor.nodes[farId];\n        if (far) referenceAngle = Math.atan2(far.y - node.y, far.x - node.x);\n      }\n\n      return { origin, referenceAngle, excludeWallIds };\n    },\n    [floor],\n  );\n\n  const fitToContent = useCallback(\n    (options: { padding?: number } = {}) => {\n      const padding = options.padding ?? 24;\n      const bounds = floorBounds(floor);\n      if (!bounds || bounds.width <= 0 || bounds.height <= 0) {\n        setView({ x: -1000, y: -1000, zoom: 0.05 });\n        return;\n      }\n      const zoom = Math.min((size.width - padding * 2) / bounds.width, (size.height - padding * 2) / bounds.height);\n      const clamped = Math.max(minZoom, Math.min(maxZoom, zoom));\n      setView({\n        x: bounds.minX - (size.width / clamped - bounds.width) / 2,\n        y: bounds.minY - (size.height / clamped - bounds.height) / 2,\n        zoom: clamped,\n      });\n    },\n    [floor, size.width, size.height, minZoom, maxZoom, setView],\n  );\n\n  // Fit once, as soon as there is something to fit and a measured canvas.\n  const didFitRef = useRef(false);\n  useEffect(() => {\n    if (!fitOnMount || didFitRef.current) return;\n    if (Object.keys(floor.nodes).length === 0) return;\n    if (!size.measured) return;\n    didFitRef.current = true;\n    fitToContent();\n  }, [fitOnMount, floor, size.measured, fitToContent]);\n\n  // ─── Emitting changes ─────────────────────────────────────────────────────────\n\n  const emit = useCallback(\n    (\n      nextFloor: PlanFloor,\n      reason: RoomDrawerChangeReason,\n      committed: boolean,\n      targetIds: string[] = [],\n      /**\n       * `false` when the edit is the tail of a gesture that already pushed its own\n       * snapshot, so the two do not become two undo steps for one action.\n       */\n      options: { push?: boolean } = {},\n    ) => {\n      if (nextFloor === floor) return plan;\n      const nextPlan = setFloor(plan, nextFloor);\n      lastEmittedRef.current = nextPlan;\n\n      // History is bookkeeping, not rendering state, so it runs in BOTH modes. Skipping\n      // it while controlled left discrete edits (delete, rename, nudge) with no entry at\n      // all, and Undo then jumped back past them to whatever the last drag had stashed.\n      if (committed) {\n        if (options.push === false) history.replace(nextPlan);\n        else history.commit(nextPlan, reason, targetIds);\n      } else {\n        history.updateGesture(nextPlan);\n      }\n\n      const meta: RoomDrawerChangeMeta = { reason, committed, targetIds };\n      onChange?.(nextPlan, meta);\n      if (committed) onChangeEnd?.(nextPlan, meta);\n      return nextPlan;\n    },\n    [floor, plan, history, onChange, onChangeEnd],\n  );\n\n  /** A discrete edit: names new rooms and pushes exactly one history entry. */\n  const commitEdit = useCallback(\n    (\n      nextFloor: PlanFloor,\n      reason: RoomDrawerChangeReason,\n      targetIds: string[] = [],\n      options: { push?: boolean } = {},\n    ) => {\n      // Welding here rather than per-gesture: a drag is not the only way to squash a\n      // wall to nothing, and a plan with two corners on the same point derives as two\n      // rooms and refuses to round-trip through JSON without being \"repaired\".\n      const named = commitDerivation(weldShortWalls(nextFloor).floor, { nameFor: newRoomName }).floor;\n      return emit(named, reason, true, targetIds, options);\n    },\n    [emit, newRoomName],\n  );\n\n  useEffect(() => {\n    onHistoryChange?.({ canUndo: history.canUndo, canRedo: history.canRedo });\n  }, [history.canUndo, history.canRedo, onHistoryChange]);\n\n  // ─── Selection helpers ────────────────────────────────────────────────────────\n\n  const selectedRoom = useMemo(\n    () => (single?.type === 'room' ? (rooms.find((r) => r.roomId === single.id) ?? null) : null),\n    [single, rooms],\n  );\n  const selectedWall = single?.type === 'wall' ? floor.walls[single.id] : undefined;\n  const selectedOpening = single?.type === 'opening' ? floor.openings[single.id] : undefined;\n\n  /** Midpoint of the selected wall, where its \"add a corner\" handle sits. */\n  const selectedWallMidpoint = useMemo(() => {\n    if (!selectedWall) return null;\n    const a = floor.nodes[selectedWall.a];\n    const b = floor.nodes[selectedWall.b];\n    if (!a || !b) return null;\n    return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };\n  }, [selectedWall, floor.nodes]);\n\n  /** Splits the selected wall at its midpoint and selects the corner that appears. */\n  const addCornerToSelectedWall = useCallback(() => {\n    if (!editable || !selectedWall) return;\n    const edit = splitWall(floor, selectedWall.id, 0.5);\n    if (!edit.result.wallIds) return;\n    commitEdit(edit.floor, 'edit-wall', [edit.result.nodeId]);\n    setSelection({ type: 'node', id: edit.result.nodeId });\n  }, [editable, selectedWall, floor, commitEdit, setSelection]);\n\n  /** Removes one entry. Split out so deleting a whole selection can reuse it. */\n  const deleteOne = useCallback(\n    (\n      entry: PlanSelection,\n      from: PlanFloor,\n      /** Room ids going in the same operation, so they do not count as needing a wall. */\n      alsoDeleting: ReadonlySet<string> = new Set(),\n    ): { floor: PlanFloor; reselect?: PlanSelection } => {\n      if (entry.type === 'wall') return { floor: removeWall(from, entry.id).floor };\n      if (entry.type === 'opening') return { floor: removeOpening(from, entry.id).floor };\n\n      if (entry.type === 'node') {\n        // Deleting a corner between exactly two walls means \"straighten this out\": the\n        // two walls rejoin into one. Removing both of them instead would leave a hole\n        // where the user only wanted the bend gone. `force` is needed because the corner\n        // is normally bent by the time anyone wants rid of it, which the unforced\n        // dissolve refuses.\n        const rejoined = dissolveNode(from, entry.id, { force: true });\n        if (rejoined.result) {\n          return { floor: rejoined.floor, reselect: { type: 'wall', id: rejoined.result.wallId } };\n        }\n        // A loose end or a junction of three or more: there is nothing to rejoin, so the\n        // walls meeting there go with it.\n        let next = from;\n        for (const wall of Object.values(from.walls)) {\n          if (wall.a === entry.id || wall.b === entry.id) {\n            next = removeWall(next, wall.id, { dissolveCollinear: false }).floor;\n          }\n        }\n        return { floor: next };\n      }\n\n      // A room: drop its metadata and the walls that ONLY it uses. A wall a neighbouring\n      // room still needs survives, which is what \"walls are shared\" has to mean.\n      const room = rooms.find((r) => r.roomId === entry.id);\n      if (!room) return { floor: from };\n      const usedElsewhere = new Set<string>();\n      for (const other of rooms) {\n        if (other.roomId === entry.id) continue;\n        // A neighbour being deleted in the same breath does not need the wall either,\n        // otherwise deleting two adjacent rooms together would strand the wall they share.\n        if (other.roomId && alsoDeleting.has(other.roomId)) continue;\n        for (const id of other.wallIds) usedElsewhere.add(id);\n      }\n      let next = from;\n      for (const wallId of new Set(room.wallIds)) {\n        if (usedElsewhere.has(wallId)) continue;\n        next = removeWall(next, wallId, { dissolveCollinear: false }).floor;\n      }\n      const nextRooms = { ...next.rooms };\n      delete nextRooms[entry.id];\n      return { floor: { ...next, rooms: nextRooms } };\n    },\n    [rooms],\n  );\n\n  const deleteSelection = useCallback(() => {\n    const entries = selectionRef.current;\n    if (!editable || entries.length === 0) return;\n\n    // Rooms first, then walls, then corners. A corner deleted early can dissolve a wall\n    // that a later entry still refers to, and rooms need their walls intact to work out\n    // which of them a neighbour is still using.\n    const order: Record<PlanSelection['type'], number> = { room: 0, opening: 1, wall: 2, node: 3 };\n    const ordered = [...entries].sort((a, b) => order[a.type] - order[b.type]);\n\n    const deletingRooms = new Set(ordered.filter((e) => e.type === 'room').map((e) => e.id));\n    let next = floor;\n    let reselect: PlanSelection | undefined;\n    for (const entry of ordered) {\n      const result = deleteOne(entry, next, deletingRooms);\n      next = result.floor;\n      // Only meaningful when one thing was deleted; a bulk delete just clears.\n      if (ordered.length === 1) reselect = result.reselect;\n    }\n\n    if (next === floor) return;\n    commitEdit(\n      next,\n      'delete',\n      ordered.map((e) => e.id),\n    );\n    setSelection(reselect ?? null);\n  }, [editable, floor, deleteOne, commitEdit, setSelection]);\n\n  // ─── Pointer handling ─────────────────────────────────────────────────────────\n\n  /**\n   * The snap hint belongs to whatever is being drawn, and only `finishGesture` used to\n   * clear it — so ending a chain from the keyboard left its guide and marker painted on\n   * the canvas with nothing to explain them. Identity is preserved when there is nothing\n   * to clear, so the tool-change effect below costs no render.\n   */\n  const endWallChain = useCallback(() => {\n    setWallChain((prev) => (prev.length === 0 ? prev : []));\n    setSnapHint(null);\n  }, []);\n\n  // Switching tool abandons whatever the last one had half-drawn, hint included.\n  useEffect(() => {\n    endWallChain();\n  }, [tool, endWallChain]);\n\n  const handlePointerDown = useCallback(\n    (event: ReactPointerEvent<HTMLDivElement>) => {\n      if (disabled) return;\n      rootRef.current?.focus();\n      // Whatever the last opened file had to say, the user has read it or does not care.\n      setImportReport(null);\n      pointersRef.current.set(event.pointerId, { x: event.clientX, y: event.clientY });\n      (event.currentTarget as HTMLElement).setPointerCapture?.(event.pointerId);\n\n      // Two pointers abort whatever tool gesture was running and become a pinch.\n      if (pointersRef.current.size === 2) {\n        const [p1, p2] = [...pointersRef.current.values()];\n        if (gestureRef.current.kind !== 'none' && gestureRef.current.kind !== 'pan') {\n          history.cancelGesture();\n        }\n        gestureRef.current = {\n          kind: 'pinch',\n          startDist: Math.hypot(p2.x - p1.x, p2.y - p1.y),\n          startView: view,\n          startMid: { x: (p1.x + p2.x) / 2, y: (p1.y + p2.y) / 2 },\n        };\n        return;\n      }\n      if (pointersRef.current.size > 2) return;\n\n      pressRef.current = { client: { x: event.clientX, y: event.clientY }, moved: false };\n\n      // Pan: middle button, or space held.\n      if (event.button === 1 || spaceHeldRef.current) {\n        gestureRef.current = {\n          kind: 'pan',\n          startClient: { x: event.clientX, y: event.clientY },\n          startView: view,\n        };\n        return;\n      }\n      if (event.button !== 0) return;\n\n      const world = toWorld(event.clientX, event.clientY);\n\n      if (!editable) {\n        const hit = hitTest(floor, rooms, world, worldTolerances(hitPx, view.zoom));\n        setSelection(hit);\n        return;\n      }\n\n      if (tool === 'rect') {\n        const start = snapAt(world).point;\n        gestureRef.current = { kind: 'rect', start, current: start };\n        history.beginGesture();\n        return;\n      }\n\n      if (tool === 'measure') {\n        // No `history.beginGesture()` anywhere in here: measuring changes nothing, so\n        // there is nothing to take back and an undo entry would be a lie.\n        const grabbed = measureHit(world);\n        if (grabbed) {\n          const m = measurements.find((x) => x.id === grabbed.id)!;\n          gestureRef.current = grabbed.end\n            ? { kind: 'measureEnd', id: grabbed.id, end: grabbed.end }\n            : { kind: 'measureMove', id: grabbed.id, startWorld: world, origA: m.a, origB: m.b };\n          return;\n        }\n        const start = snapAt(world).point;\n        gestureRef.current = { kind: 'measure', start, current: start };\n        return;\n      }\n\n      if (tool === 'wall') {\n        const origin = wallChain[wallChain.length - 1];\n        const snapped = snapAt(world, origin, undefined, chainReferenceAngle).point;\n        if (wallChain.length === 0) {\n          setWallChain([snapped]);\n          return;\n        }\n        const from = wallChain[wallChain.length - 1];\n        const edit = addWall(\n          floor,\n          from,\n          snapped,\n          { thickness: wallThickness, type: WallType.Interior, height: wallHeight },\n          worldTolerances(hitPx, view.zoom).node,\n        );\n        if (edit.result.wallIds.length > 0) commitEdit(edit.floor, 'draw-wall', edit.result.wallIds);\n        // Closing the loop on the chain's own start finishes it.\n        const closes =\n          wallChain.length >= 2 &&\n          Math.hypot(snapped.x - wallChain[0].x, snapped.y - wallChain[0].y) < worldTolerances(hitPx, view.zoom).node;\n        if (closes) endWallChain();\n        else setWallChain((prev) => [...prev, snapped]);\n        return;\n      }\n\n      const openingKind = OPENING_TOOL_KIND[tool];\n      if (openingKind) {\n        const tolerances = worldTolerances(hitPx, view.zoom);\n        const hit = hitTest(floor, rooms, world, tolerances, { accept: ['wall'] });\n        if (hit?.type !== 'wall') return;\n        const wall = floor.walls[hit.id];\n        const a = floor.nodes[wall.a];\n        const b = floor.nodes[wall.b];\n        const len = Math.hypot(b.x - a.x, b.y - a.y);\n        const t = ((world.x - a.x) * (b.x - a.x) + (world.y - a.y) * (b.y - a.y)) / (len * len || 1);\n        const defaults = {\n          ...BUILT_IN_OPENING_DEFAULTS[openingKind],\n          ...openingDefaults?.[openingKind],\n        };\n        const edit = addOpening(floor, {\n          wallId: wall.id,\n          kind: openingKind,\n          offset: Math.min(Math.max(t, 0), 1) * len,\n          width: defaults.width,\n          sill: defaults.sill,\n          head: defaults.head,\n        });\n        if (edit.result.openingId) {\n          commitEdit(edit.floor, 'add-opening', [edit.result.openingId]);\n          setSelection({ type: 'opening', id: edit.result.openingId });\n        }\n        return;\n      }\n\n      // Select tool.\n      const tolerances = worldTolerances(hitPx, view.zoom);\n\n      // The selected wall's midpoint handle sits on top of the wall itself, so it has to\n      // be tested before the general hit test or the wall underneath always wins.\n      if (selectedWallMidpoint && single?.type === 'wall') {\n        const grabbed =\n          Math.hypot(world.x - selectedWallMidpoint.x, world.y - selectedWallMidpoint.y) <= tolerances.node;\n        if (grabbed) {\n          history.beginGesture();\n          gestureRef.current = {\n            kind: 'wallSplit',\n            wallId: single.id,\n            at: selectedWallMidpoint,\n          };\n          return;\n        }\n      }\n\n      const hit = hitTest(floor, rooms, world, tolerances);\n      const additive = multiSelect && (event.ctrlKey || event.metaKey);\n\n      if (!hit) {\n        // Empty canvas. With multi-select on, a drag from here is a marquee; a plain\n        // click clears. Ctrl is honoured so a stray miss does not wipe the selection.\n        if (!additive) setSelection(null);\n        gestureRef.current = multiSelect\n          ? { kind: 'marquee', start: world, current: world, additive, base: selectionRef.current }\n          : { kind: 'none' };\n        return;\n      }\n\n      if ((event.ctrlKey || event.metaKey) && hit.type === 'wall' && floor.walls[hit.id]) {\n        // Ctrl on a wall stays ambiguous until the pointer moves: a click toggles it in\n        // or out of the selection, a drag pulls it off its neighbours. The drag\n        // threshold decides, which is the same gate that stops a wobbling click from\n        // moving what it just picked up, so the toggle waits for pointerup.\n        const wall = floor.walls[hit.id];\n        if (!additive) setSelection(hit);\n        history.beginGesture();\n        gestureRef.current = {\n          kind: 'wall',\n          wallId: hit.id,\n          startWorld: world,\n          origA: { ...floor.nodes[wall.a] },\n          origB: { ...floor.nodes[wall.b] },\n          moved: false,\n          detach: true,\n          toggle: additive,\n        };\n        return;\n      }\n\n      if (additive) {\n        // Toggling is a selection change on its own: no drag follows, so that a\n        // Ctrl-click which happens to wobble does not also move what it just picked up.\n        toggleSelection(hit);\n        gestureRef.current = { kind: 'none' };\n        return;\n      }\n\n      const alreadySelected = selectionRef.current.some((entry) => entry.type === hit.type && entry.id === hit.id);\n      // Pressing something already in a multi-selection moves the whole set. Pressing\n      // anything else selects just it, which is what every editor does.\n      if (!alreadySelected) setSelection(hit);\n\n      history.beginGesture();\n\n      if (selectionRef.current.length > 1) {\n        const nodeIds = [...nodeIdsForSelection(selectionRef.current)];\n        const orig: Record<string, PlanPoint> = {};\n        for (const id of nodeIds) {\n          const node = floor.nodes[id];\n          if (node) orig[id] = { x: node.x, y: node.y };\n        }\n        gestureRef.current = { kind: 'room', nodeIds, startWorld: world, orig, moved: false };\n        return;\n      }\n\n      if (hit.type === 'node') {\n        gestureRef.current = {\n          kind: 'node',\n          nodeId: hit.id,\n          moved: false,\n          ...nodeDragContext(hit.id),\n        };\n      } else if (hit.type === 'wall') {\n        const wall = floor.walls[hit.id];\n        gestureRef.current = {\n          kind: 'wall',\n          wallId: hit.id,\n          startWorld: world,\n          origA: { ...floor.nodes[wall.a] },\n          origB: { ...floor.nodes[wall.b] },\n          moved: false,\n        };\n      } else if (hit.type === 'opening') {\n        gestureRef.current = { kind: 'opening', openingId: hit.id, moved: false };\n      } else {\n        const room = rooms.find((r) => r.roomId === hit.id);\n        const nodeIds = room ? [...new Set(room.nodeIds)] : [];\n        const orig: Record<string, PlanPoint> = {};\n        for (const id of nodeIds) {\n          const node = floor.nodes[id];\n          if (node) orig[id] = { x: node.x, y: node.y };\n        }\n        gestureRef.current = { kind: 'room', nodeIds, startWorld: world, orig, moved: false };\n      }\n    },\n    [\n      disabled,\n      editable,\n      view,\n      tool,\n      floor,\n      rooms,\n      hitPx,\n      toWorld,\n      snapAt,\n      wallChain,\n      wallThickness,\n      wallHeight,\n      openingDefaults,\n      commitEdit,\n      setSelection,\n      selectedWallMidpoint,\n      single,\n      chainReferenceAngle,\n      nodeDragContext,\n      nodeIdsForSelection,\n      multiSelect,\n      toggleSelection,\n      history,\n      endWallChain,\n      measureHit,\n      measurements,\n    ],\n  );\n\n  const handlePointerMove = useCallback(\n    (event: ReactPointerEvent<HTMLDivElement>) => {\n      if (disabled) return;\n      if (pointersRef.current.has(event.pointerId)) {\n        pointersRef.current.set(event.pointerId, { x: event.clientX, y: event.clientY });\n      }\n\n      const gesture = gestureRef.current;\n\n      if (gesture.kind === 'pinch' && pointersRef.current.size >= 2) {\n        const [p1, p2] = [...pointersRef.current.values()];\n        const distNow = Math.hypot(p2.x - p1.x, p2.y - p1.y);\n        if (gesture.startDist <= 0) return;\n        const nextZoom = Math.max(minZoom, Math.min(maxZoom, gesture.startView.zoom * (distNow / gesture.startDist)));\n        const rect = canvasWrapRef.current?.getBoundingClientRect() ?? { left: 0, top: 0 };\n        const mid = { x: (p1.x + p2.x) / 2, y: (p1.y + p2.y) / 2 };\n        // Keep the world point under the pinch midpoint where it is.\n        const worldMid = clientToWorld(gesture.startMid.x, gesture.startMid.y, rect, gesture.startView);\n        setView({\n          x: worldMid.x - (mid.x - rect.left) / nextZoom,\n          y: worldMid.y - (mid.y - rect.top) / nextZoom,\n          zoom: nextZoom,\n        });\n        return;\n      }\n\n      if (pressRef.current) {\n        const dx = event.clientX - pressRef.current.client.x;\n        const dy = event.clientY - pressRef.current.client.y;\n        if (Math.hypot(dx, dy) > DRAG_THRESHOLD_PX) pressRef.current.moved = true;\n      }\n\n      if (gesture.kind === 'pan') {\n        setView({\n          x: gesture.startView.x - (event.clientX - gesture.startClient.x) / gesture.startView.zoom,\n          y: gesture.startView.y - (event.clientY - gesture.startClient.y) / gesture.startView.zoom,\n          zoom: gesture.startView.zoom,\n        });\n        return;\n      }\n\n      const world = toWorld(event.clientX, event.clientY);\n      setCursor(world);\n\n      if (gesture.kind === 'none') {\n        if (!editable) return;\n        if (tool === 'wall' && wallChain.length > 0) {\n          // Preview where the wall will actually land, not where the pointer is.\n          const snapped = snapAt(world, wallChain[wallChain.length - 1], undefined, chainReferenceAngle);\n          setCursor(snapped.point);\n          setSnapHint(snapped);\n          return;\n        }\n        if (tool === 'select') {\n          const hit = hitTest(floor, rooms, world, worldTolerances(hitPx, view.zoom));\n          if (!selectionsEqual(hit, hovered)) {\n            setHovered(hit);\n            onHoverChange?.(hit);\n          }\n        }\n        if (tool === 'measure') setHoveredMeasure(measureHit(world)?.id ?? null);\n        return;\n      }\n\n      if (!pressRef.current?.moved) return;\n\n      if (gesture.kind === 'measureEnd') {\n        const point = snapAt(world).point;\n        setMeasurements((list) => list.map((m) => (m.id === gesture.id ? { ...m, [gesture.end]: point } : m)));\n        setCursor(point);\n        return;\n      }\n\n      if (gesture.kind === 'measureMove') {\n        const dx = world.x - gesture.startWorld.x;\n        const dy = world.y - gesture.startWorld.y;\n        setMeasurements((list) =>\n          list.map((m) =>\n            m.id === gesture.id\n              ? {\n                  ...m,\n                  a: { x: gesture.origA.x + dx, y: gesture.origA.y + dy },\n                  b: { x: gesture.origB.x + dx, y: gesture.origB.y + dy },\n                }\n              : m,\n          ),\n        );\n        return;\n      }\n\n      if (gesture.kind === 'rect' || gesture.kind === 'measure') {\n        gestureRef.current = { ...gesture, current: snapAt(world).point };\n        setCursor(snapAt(world).point);\n        return;\n      }\n\n      if (gesture.kind === 'marquee') {\n        // Unsnapped: a selection rectangle is not geometry, so pulling it onto the grid\n        // would only make it harder to enclose exactly what you meant.\n        gestureRef.current = { ...gesture, current: world };\n        setCursor(world);\n        return;\n      }\n\n      if (gesture.kind === 'wallSplit') {\n        // First real movement: cut the wall in two and hand the drag over to the new\n        // corner, so adding a corner and placing it are one continuous gesture.\n        const edit = splitWall(floor, gesture.wallId, gesture.at);\n        if (!edit.result.wallIds) {\n          gestureRef.current = { kind: 'none' };\n          return;\n        }\n        const nodeId = edit.result.nodeId;\n        // The two halves of the wall just split both move with the new corner, so they\n        // are excluded like any other incident wall. The original wall's direction is\n        // the reference, which makes \"square to the wall I am bending\" available.\n        const wasWall = floor.walls[gesture.wallId];\n        const originNode = wasWall ? edit.floor.nodes[wasWall.a] : undefined;\n        const farNode = wasWall ? edit.floor.nodes[wasWall.b] : undefined;\n        const context = {\n          origin: originNode ? { x: originNode.x, y: originNode.y } : undefined,\n          referenceAngle:\n            originNode && farNode ? Math.atan2(farNode.y - originNode.y, farNode.x - originNode.x) : undefined,\n          excludeWallIds: new Set(edit.result.wallIds),\n        };\n        const snapped = snapAt(\n          world,\n          context.origin,\n          new Set([nodeId]),\n          context.referenceAngle,\n          context.excludeWallIds,\n        ).point;\n        gestureRef.current = { kind: 'node', nodeId, moved: true, ...context };\n        setSelection({ type: 'node', id: nodeId });\n        setSnapHint(lastSnapRef.current);\n        emit(moveNode(edit.floor, nodeId, snapped).floor, 'move-node', false, [nodeId]);\n        return;\n      }\n\n      if (gesture.kind === 'node') {\n        const snapped = snapAt(\n          world,\n          gesture.origin,\n          new Set([gesture.nodeId]),\n          gesture.referenceAngle,\n          gesture.excludeWallIds,\n        ).point;\n        setSnapHint(lastSnapRef.current);\n        gestureRef.current = { ...gesture, moved: true };\n        emit(moveNode(floor, gesture.nodeId, snapped).floor, 'move-node', false, [gesture.nodeId]);\n        return;\n      }\n\n      if (gesture.kind === 'wall') {\n        // Constrained to the wall's own normal: pushing a wall sideways. Because the\n        // endpoints are shared, the neighbouring room follows, which is the point.\n        const wall = floor.walls[gesture.wallId];\n        if (!wall) return;\n        const dx = gesture.origB.x - gesture.origA.x;\n        const dy = gesture.origB.y - gesture.origA.y;\n        const len = Math.hypot(dx, dy);\n        if (len < 1e-6) return;\n        const normal = { x: -dy / len, y: dx / len };\n        const raw = (world.x - gesture.startWorld.x) * normal.x + (world.y - gesture.startWorld.y) * normal.y;\n        const step = snap !== altHeldRef.current && gridSize > 0 ? gridSize : 1;\n        const offset = Math.round(raw / step) * step;\n        // How far the wall has to move before it is worth detaching: the connectors are\n        // exactly this long, so anything shorter is a wall the model will not accept.\n        const minDetach = Math.max(step, MIN_WALL_LENGTH);\n\n        if (gesture.detach) {\n          // Nothing happens until the wall has somewhere to go: detaching at zero would\n          // mint two zero-length connectors, which the model forbids.\n          if (Math.abs(offset) < minDetach) return;\n          const edit = detachWall(floor, gesture.wallId, {\n            x: normal.x * offset,\n            y: normal.y * offset,\n          });\n          if (!edit.result) return;\n          gestureRef.current = {\n            ...gesture,\n            detach: false,\n            toggle: false,\n            detachedSign: Math.sign(offset),\n            moved: true,\n          };\n          setSelection({ type: 'wall', id: gesture.wallId });\n          emit(edit.floor, 'move-wall', false, [gesture.wallId]);\n          return;\n        }\n\n        let next = moveNode(floor, wall.a, {\n          x: gesture.origA.x + normal.x * offset,\n          y: gesture.origA.y + normal.y * offset,\n        }).floor;\n        next = moveNode(next, wall.b, {\n          x: gesture.origB.x + normal.x * offset,\n          y: gesture.origB.y + normal.y * offset,\n        }).floor;\n\n        if (gesture.detachedSign && Math.abs(offset) < MIN_WALL_LENGTH) {\n          // Pushed back onto the line it came off. The connectors have no length left,\n          // so their ends weld and the rest of the drag is an ordinary wall drag: the\n          // corner that survives a weld is the one that was already there, which is\n          // exactly the corner the wall was pulled off.\n          next = weldShortWalls(next).floor;\n          gestureRef.current = { ...gesture, detachedSign: undefined, moved: true };\n        } else {\n          gestureRef.current = { ...gesture, moved: true };\n        }\n        emit(next, 'move-wall', false, [gesture.wallId]);\n        return;\n      }\n\n      if (gesture.kind === 'opening') {\n        const opening = floor.openings[gesture.openingId];\n        if (!opening) return;\n        const wall = floor.walls[opening.wallId];\n        const a = floor.nodes[wall.a];\n        const b = floor.nodes[wall.b];\n        const len = Math.hypot(b.x - a.x, b.y - a.y);\n        const t = ((world.x - a.x) * (b.x - a.x) + (world.y - a.y) * (b.y - a.y)) / (len * len || 1);\n        gestureRef.current = { ...gesture, moved: true };\n        emit(\n          updateOpening(floor, gesture.openingId, { offset: Math.min(Math.max(t, 0), 1) * len }).floor,\n          'move-opening',\n          false,\n          [gesture.openingId],\n        );\n        return;\n      }\n\n      if (gesture.kind === 'room') {\n        const rawDx = world.x - gesture.startWorld.x;\n        const rawDy = world.y - gesture.startWorld.y;\n        const step = snap !== altHeldRef.current && gridSize > 0 ? gridSize : 1;\n        const dx = Math.round(rawDx / step) * step;\n        const dy = Math.round(rawDy / step) * step;\n        let next = floor;\n        for (const id of gesture.nodeIds) {\n          const orig = gesture.orig[id];\n          if (!orig) continue;\n          next = moveNode(next, id, { x: orig.x + dx, y: orig.y + dy }).floor;\n        }\n        gestureRef.current = { ...gesture, moved: true };\n        emit(next, 'move-room', false, gesture.nodeIds);\n      }\n    },\n    [\n      disabled,\n      editable,\n      tool,\n      floor,\n      rooms,\n      hovered,\n      hitPx,\n      view.zoom,\n      minZoom,\n      maxZoom,\n      gridSize,\n      snap,\n      toWorld,\n      snapAt,\n      emit,\n      setView,\n      setSelection,\n      wallChain,\n      chainReferenceAngle,\n      onHoverChange,\n      measureHit,\n    ],\n  );\n\n  const finishGesture = useCallback(\n    (event: ReactPointerEvent<HTMLDivElement>, cancelled: boolean) => {\n      pointersRef.current.delete(event.pointerId);\n      const gesture = gestureRef.current;\n      gestureRef.current = { kind: 'none' };\n      const moved = pressRef.current?.moved ?? false;\n      pressRef.current = null;\n      setSnapHint(null);\n\n      if (gesture.kind === 'none' || gesture.kind === 'pan' || gesture.kind === 'pinch') return;\n\n      if (gesture.kind === 'marquee') {\n        // The marquee never touches the plan, so it has no gesture snapshot to unwind.\n        if (cancelled || !moved) {\n          if (cancelled) setSelection([...gesture.base]);\n          return;\n        }\n        const minX = Math.min(gesture.start.x, gesture.current.x);\n        const maxX = Math.max(gesture.start.x, gesture.current.x);\n        const minY = Math.min(gesture.start.y, gesture.current.y);\n        const maxY = Math.max(gesture.start.y, gesture.current.y);\n\n        // Corners only. A room is not a primitive you can enclose, it is whatever its\n        // corners form, and selecting the corners is what makes a drag move it. Walls and\n        // rooms light up on their own wherever all of their corners are in the set.\n        const caught: PlanSelection[] = [];\n        for (const node of Object.values(floor.nodes)) {\n          if (node.x >= minX && node.x <= maxX && node.y >= minY && node.y <= maxY) {\n            caught.push({ type: 'node', id: node.id });\n          }\n        }\n\n        if (gesture.additive) {\n          const existing = new Set(gesture.base.map((e) => `${e.type}:${e.id}`));\n          setSelection([...gesture.base, ...caught.filter((e) => !existing.has(`${e.type}:${e.id}`))]);\n        } else {\n          setSelection(caught);\n        }\n        return;\n      }\n\n      if (cancelled) {\n        const restored = history.cancelGesture();\n        if (restored) onChange?.(restored, { reason: 'external', committed: false, targetIds: [] });\n        return;\n      }\n\n      if (gesture.kind === 'measureEnd' || gesture.kind === 'measureMove') return;\n\n      if (gesture.kind === 'measure') {\n        // A measurement stays on the canvas after the drag, so it can be read while\n        // drawing against it. A tap that went nowhere leaves nothing behind.\n        const span = Math.hypot(gesture.current.x - gesture.start.x, gesture.current.y - gesture.start.y);\n        if (moved && span > EPS_MEASURE) {\n          measureSeqRef.current += 1;\n          setMeasurements((list) => [...list, { id: measureSeqRef.current, a: gesture.start, b: gesture.current }]);\n        }\n        return;\n      }\n\n      if (gesture.kind === 'rect') {\n        if (!moved) {\n          history.cancelGesture();\n          return;\n        }\n        const edit = addRect(\n          floor,\n          gesture.start,\n          gesture.current,\n          { thickness: wallThickness, type: WallType.Interior, height: wallHeight },\n          worldTolerances(hitPx, view.zoom).node,\n        );\n        if (edit.result.wallIds.length === 0) {\n          history.cancelGesture();\n          return;\n        }\n        // One entry for the whole gesture, not one per pointermove.\n        const pushedRect = history.endGesture();\n        commitEdit(edit.floor, 'draw-rect', edit.result.wallIds, { push: !pushedRect });\n        return;\n      }\n\n      if (!moved) {\n        history.cancelGesture();\n        // A Ctrl press on a wall that never became a detach drag was a selection toggle.\n        if (gesture.kind === 'wall' && gesture.toggle) {\n          toggleSelection({ type: 'wall', id: gesture.wallId });\n        }\n        return;\n      }\n\n      if (gesture.kind === 'node') {\n        // Dropping a node onto another welds them.\n        const node = floor.nodes[gesture.nodeId];\n        if (node) {\n          const tolerance = worldTolerances(hitPx, view.zoom).node;\n          const target = Object.values(floor.nodes).find(\n            (other) => other.id !== gesture.nodeId && Math.hypot(other.x - node.x, other.y - node.y) <= tolerance,\n          );\n          if (target) {\n            const pushedWeld = history.endGesture();\n            commitEdit(mergeNodes(floor, target.id, gesture.nodeId).floor, 'move-node', [target.id], {\n              push: !pushedWeld,\n            });\n            setSelection({ type: 'node', id: target.id });\n            return;\n          }\n        }\n      }\n\n      const pushedDrag = history.endGesture();\n      const reason: RoomDrawerChangeReason =\n        gesture.kind === 'wall'\n          ? 'move-wall'\n          : gesture.kind === 'opening'\n            ? 'move-opening'\n            : gesture.kind === 'room'\n              ? 'move-room'\n              : 'move-node';\n      commitEdit(floor, reason, [], { push: !pushedDrag });\n    },\n    [floor, history, hitPx, view.zoom, wallThickness, wallHeight, commitEdit, setSelection, toggleSelection, onChange],\n  );\n\n  // Wheel zoom needs a non-passive listener: React's synthetic wheel handler is passive\n  // and cannot preventDefault, so the page would scroll instead.\n  useEffect(() => {\n    const element = canvasWrapRef.current;\n    if (!element || disabled) return undefined;\n    function onWheel(e: WheelEvent) {\n      e.preventDefault();\n      const rect = element!.getBoundingClientRect();\n      const factor = Math.exp(-e.deltaY * 0.002);\n      setView(\n        (() => {\n          const current = viewRef.current;\n          const nextZoom = Math.max(minZoom, Math.min(maxZoom, current.zoom * factor));\n          const world = clientToWorld(e.clientX, e.clientY, rect, current);\n          return {\n            x: world.x - (e.clientX - rect.left) / nextZoom,\n            y: world.y - (e.clientY - rect.top) / nextZoom,\n            zoom: nextZoom,\n          };\n        })(),\n      );\n    }\n    element.addEventListener('wheel', onWheel, { passive: false });\n    return () => element.removeEventListener('wheel', onWheel);\n  }, [disabled, minZoom, maxZoom, setView]);\n\n  const viewRef = useRef(view);\n  viewRef.current = view;\n\n  // ─── Keyboard ─────────────────────────────────────────────────────────────────\n\n  const zoomBy = useCallback(\n    (factor: number) => {\n      const nextZoom = Math.max(minZoom, Math.min(maxZoom, view.zoom * factor));\n      const centreWorld = {\n        x: view.x + size.width / 2 / view.zoom,\n        y: view.y + size.height / 2 / view.zoom,\n      };\n      setView({\n        x: centreWorld.x - size.width / 2 / nextZoom,\n        y: centreWorld.y - size.height / 2 / nextZoom,\n        zoom: nextZoom,\n      });\n    },\n    [view, size, minZoom, maxZoom, setView],\n  );\n\n  const undo = useCallback(() => {\n    if (historyMode === 'none') return;\n    const target = history.undo();\n    if (!target) return;\n    lastEmittedRef.current = target;\n    onChange?.(target, { reason: 'undo', committed: true, targetIds: [] });\n    onChangeEnd?.(target, { reason: 'undo', committed: true, targetIds: [] });\n  }, [historyMode, history, onChange, onChangeEnd]);\n\n  const redo = useCallback(() => {\n    if (historyMode === 'none') return;\n    const target = history.redo();\n    if (!target) return;\n    lastEmittedRef.current = target;\n    onChange?.(target, { reason: 'redo', committed: true, targetIds: [] });\n    onChangeEnd?.(target, { reason: 'redo', committed: true, targetIds: [] });\n  }, [historyMode, history, onChange, onChangeEnd]);\n\n  /** Selects every room, or every corner when the plan has no enclosed rooms yet. */\n  const selectAll = useCallback(() => {\n    const asRooms = rooms.filter((r) => r.roomId).map((r) => ({ type: 'room', id: r.roomId! }) as PlanSelection);\n    if (asRooms.length > 0) {\n      setSelection(asRooms);\n      return;\n    }\n    setSelection(Object.keys(floor.nodes).map((id) => ({ type: 'node', id }) as PlanSelection));\n  }, [rooms, floor.nodes, setSelection]);\n\n  const nudge = useCallback(\n    (dx: number, dy: number) => {\n      if (!editable || selectedNodeIds.size === 0) return;\n      const step = gridSize > 0 ? gridSize : 100;\n      const amount = shiftHeldRef.current ? step * 10 : step;\n      let next = floor;\n      for (const id of selectedNodeIds) {\n        const node = next.nodes[id];\n        if (node) next = moveNode(next, id, { x: node.x + dx * amount, y: node.y + dy * amount }).floor;\n      }\n      commitEdit(next, 'nudge', [...selectedNodeIds]);\n    },\n    [editable, selectedNodeIds, floor, gridSize, commitEdit],\n  );\n\n  const handleKeyDown = useCallback(\n    (event: React.KeyboardEvent<HTMLDivElement>) => {\n      if (disabled) return;\n      // The old implementation put this on `window`, so any mounted drawer swallowed\n      // Delete and `+` for the whole page. Scoping it to the focused root fixes that.\n      const target = event.target as HTMLElement;\n      if (target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement) {\n        if (event.key === 'Escape') (target as HTMLElement).blur();\n        return;\n      }\n\n      if (event.key === 'Shift') shiftHeldRef.current = true;\n      if (event.key === 'Alt') altHeldRef.current = true;\n      if (event.key === ' ' && !spaceHeldRef.current) {\n        spaceHeldRef.current = true;\n        event.preventDefault();\n        return;\n      }\n\n      const meta = event.metaKey || event.ctrlKey;\n      if (meta && event.key.toLowerCase() === 'z') {\n        event.preventDefault();\n        if (event.shiftKey) redo();\n        else undo();\n        return;\n      }\n      if (meta && event.key.toLowerCase() === 'a') {\n        event.preventDefault();\n        if (multiSelect) selectAll();\n        return;\n      }\n      if (meta && event.key.toLowerCase() === 'y') {\n        event.preventDefault();\n        redo();\n        return;\n      }\n\n      if (event.key === 'Escape') {\n        if (wallChain.length > 0) endWallChain();\n        // Clears the ruler before the selection: with measurements on screen, Escape\n        // most likely means \"put that away\".\n        else if (measurements.length > 0) setMeasurements([]);\n        else setSelection(null);\n        return;\n      }\n      if (event.key === 'Enter' && tool === 'wall') {\n        endWallChain();\n        return;\n      }\n      if (event.key === 'Delete' || event.key === 'Backspace') {\n        event.preventDefault();\n        // With the ruler up, Delete belongs to the ruler and NOTHING else. It takes the\n        // measurement under the pointer, and with none there it does nothing: falling\n        // through to the selection would let a measuring tool quietly destroy a room that\n        // was selected minutes earlier with another tool.\n        if (tool === 'measure') {\n          if (hoveredMeasure !== null) {\n            setMeasurements((list) => list.filter((m) => m.id !== hoveredMeasure));\n            setHoveredMeasure(null);\n          }\n          return;\n        }\n        deleteSelection();\n        return;\n      }\n      if (event.key === '+' || event.key === '=') {\n        zoomBy(1.25);\n        return;\n      }\n      if (event.key === '-') {\n        zoomBy(1 / 1.25);\n        return;\n      }\n      if (event.key === '0') {\n        fitToContent();\n        return;\n      }\n      if (event.key.startsWith('Arrow')) {\n        event.preventDefault();\n        const map: Record<string, [number, number]> = {\n          ArrowLeft: [-1, 0],\n          ArrowRight: [1, 0],\n          ArrowUp: [0, -1],\n          ArrowDown: [0, 1],\n        };\n        const delta = map[event.key];\n        if (delta) nudge(delta[0], delta[1]);\n        return;\n      }\n\n      const lower = event.key.toLowerCase();\n      if (lower === 's') {\n        setSnapEnabled((on) => !on);\n        return;\n      }\n      if (lower === 'l') {\n        setAngleLockEnabled((on) => !on);\n        return;\n      }\n      if (lower === 'g') {\n        setGridVisible((on) => !on);\n        return;\n      }\n\n      const toolKey = TOOL_KEYS[event.key.toLowerCase()];\n      if (toolKey && editable && tools.includes(toolKey)) {\n        setTool(toolKey);\n        endWallChain();\n      }\n    },\n    [\n      disabled,\n      editable,\n      tools,\n      tool,\n      wallChain,\n      measurements.length,\n      hoveredMeasure,\n      redo,\n      undo,\n      setSelection,\n      endWallChain,\n      deleteSelection,\n      zoomBy,\n      fitToContent,\n      nudge,\n      setTool,\n      multiSelect,\n      selectAll,\n    ],\n  );\n\n  // ─── Files ────────────────────────────────────────────────────────────────────\n\n  /**\n   * Swaps the whole plan out as one history entry. Opening a file and the `setPlan`\n   * handle share it, so a file and a programmatic replacement behave identically, and\n   * both stay undoable: opening a file over an hour's work is exactly the mistake that\n   * most needs taking back.\n   */\n  const replacePlan = useCallback(\n    (next: FloorPlan, reason: RoomDrawerChangeReason, options?: { resetHistory?: boolean }) => {\n      lastEmittedRef.current = next;\n      if (options?.resetHistory) {\n        history.clearHistory();\n        history.replace(next);\n      } else {\n        history.commit(next, reason);\n      }\n      const meta: RoomDrawerChangeMeta = { reason, committed: true, targetIds: [] };\n      onChange?.(next, meta);\n      onChangeEnd?.(next, meta);\n    },\n    [history, onChange, onChangeEnd],\n  );\n\n  // ─── Floors ───────────────────────────────────────────────────────────────────\n\n  /**\n   * Floor operations go through `emit` like any other edit, so each is one undo step and\n   * reaches a controlled parent through `onChange`. Adding or copying a storey also\n   * switches to it: nobody adds a floor in order to keep looking at the one below.\n   */\n  const addFloorAt = useCallback(\n    (options: { label?: string } = {}) => {\n      const { plan: next, floor: added } = addFloor(plan, {\n        nameFor: newFloorName,\n        ...options,\n        index: floorIndex(plan, floor.id) + 1,\n      });\n      replacePlan(next, 'add-floor');\n      selectFloor(added.id);\n      return added.id;\n    },\n    [plan, floor.id, replacePlan, selectFloor, newFloorName],\n  );\n\n  const duplicateActiveFloor = useCallback(() => {\n    const { plan: next, floor: copy } = duplicateFloor(plan, floor.id, { label: copiedFloorName(floor.label) });\n    if (!copy) return null;\n    replacePlan(next, 'duplicate-floor');\n    selectFloor(copy.id);\n    return copy.id;\n  }, [plan, floor.id, floor.label, replacePlan, selectFloor, copiedFloorName]);\n\n  const removeFloorById = useCallback(\n    (id?: string) => {\n      const target = id ?? floor.id;\n      const next = removeFloor(plan, target);\n      if (next === plan) return; // the last floor, which stays\n      replacePlan(next, 'remove-floor');\n      if (target === floor.id) {\n        // Step down to the storey below where there is one, so the view lands somewhere\n        // adjacent rather than at the bottom of the building.\n        const was = floorIndex(plan, target);\n        selectFloor(next.floors[Math.max(0, was - 1)].id);\n      }\n    },\n    [plan, floor.id, replacePlan, selectFloor],\n  );\n\n  const moveFloorTo = useCallback(\n    (id: string, toIndex: number) => {\n      const next = moveFloor(plan, id, toIndex);\n      if (next !== plan) replacePlan(next, 'move-floor');\n    },\n    [plan, replacePlan],\n  );\n\n  const setFloorHeightById = useCallback(\n    (id: string, height: number) => {\n      const next = setFloorHeight(plan, id, height);\n      if (next !== plan) replacePlan(next, 'edit-floor');\n    },\n    [plan, replacePlan],\n  );\n\n  const renameFloorById = useCallback(\n    (id: string, label: string) => {\n      const next = renameFloor(plan, id, label);\n      if (next !== plan) replacePlan(next, 'rename-floor');\n    },\n    [plan, replacePlan],\n  );\n\n  const downloadPlan = useCallback(\n    (name?: string) => {\n      const chosen = (name ?? fileName ?? plan.name ?? '').trim() || 'floor-plan';\n      const url = URL.createObjectURL(new Blob([planToJson(plan)], { type: 'application/json' }));\n      const link = document.createElement('a');\n      link.href = url;\n      link.download = chosen.endsWith('.json') ? chosen : `${chosen}.json`;\n      link.click();\n      // Revoking in the same tick cancels the download in some browsers, so let the task\n      // queue drain first.\n      setTimeout(() => URL.revokeObjectURL(url), 0);\n    },\n    [fileName, plan],\n  );\n\n  const loadJson = useCallback(\n    (text: string): PlanParseResult => {\n      const result = planFromJson(text);\n      if (result.plan) replacePlan(result.plan, 'import');\n      // Silence is the report for a clean file: the canvas changing says it worked.\n      setImportReport(result.plan && result.issues.length === 0 ? null : result.issues);\n      onImport?.(result);\n      return result;\n    },\n    [onImport, replacePlan],\n  );\n\n  const openFile = useCallback(\n    (file: File) => {\n      file\n        .text()\n        .then((text) => loadJson(text))\n        .catch((error: Error) => {\n          const issues = [`Could not read that file: ${error.message}`];\n          setImportReport(issues);\n          onImport?.({ plan: null, issues });\n        });\n    },\n    [loadJson, onImport],\n  );\n\n  const clearMeasurements = useCallback(() => setMeasurements([]), []);\n\n  const handleKeyUp = useCallback((event: React.KeyboardEvent<HTMLDivElement>) => {\n    if (event.key === 'Shift') shiftHeldRef.current = false;\n    if (event.key === 'Alt') altHeldRef.current = false;\n    if (event.key === ' ') spaceHeldRef.current = false;\n  }, []);\n\n  // ─── Imperative handle ────────────────────────────────────────────────────────\n\n  useImperativeHandle(\n    ref,\n    () => ({\n      getPlan: () => plan,\n      setPlan: (next, options) => replacePlan(next, 'import', options),\n      clear: () => replacePlan(emptyFloorPlan({ floorLabel: initialFloorName }), 'clear'),\n      getFloorId: () => floor.id,\n      setFloorId: selectFloor,\n      addFloor: (options) => addFloorAt(options),\n      duplicateFloor: duplicateActiveFloor,\n      removeFloor: removeFloorById,\n      renameFloor: renameFloorById,\n      moveFloor: moveFloorTo,\n      setFloorHeight: setFloorHeightById,\n      clearMeasurements,\n      download: downloadPlan,\n      loadJson,\n      undo,\n      redo,\n      canUndo: () => historyMode !== 'none' && history.canUndo,\n      canRedo: () => historyMode !== 'none' && history.canRedo,\n      clearHistory: history.clearHistory,\n      getSelection: () => [...selectionRef.current],\n      select: setSelection,\n      addToSelection: (next) => {\n        const additions = Array.isArray(next) ? next : [next as PlanSelection];\n        const existing = new Set(selectionRef.current.map((e) => `${e.type}:${e.id}`));\n        setSelection([...selectionRef.current, ...additions.filter((e) => !existing.has(`${e.type}:${e.id}`))]);\n      },\n      toggleSelection,\n      selectAll,\n      deleteSelection,\n      getView: () => view,\n      setView,\n      zoomBy,\n      fitToContent,\n      toWorld,\n      toScreen,\n      getSvgElement: () => svgRef.current,\n    }),\n    [\n      plan,\n      history,\n      replacePlan,\n      initialFloorName,\n      floor,\n      selectFloor,\n      addFloorAt,\n      duplicateActiveFloor,\n      removeFloorById,\n      renameFloorById,\n      moveFloorTo,\n      setFloorHeightById,\n      clearMeasurements,\n      downloadPlan,\n      loadJson,\n      undo,\n      redo,\n      historyMode,\n      setSelection,\n      toggleSelection,\n      selectAll,\n      deleteSelection,\n      view,\n      setView,\n      zoomBy,\n      fitToContent,\n      toWorld,\n      toScreen,\n    ],\n  );\n\n  // ─── Overlay (screen space) ───────────────────────────────────────────────────\n\n  const gesture = gestureRef.current;\n  const overlay = useMemo(() => {\n    if (!editable) return null;\n    const parts: ReactElement[] = [];\n\n    // The ruler: measurements already taken, plus the one in flight. Drawn in screen\n    // space, so ticks and figures stay the same size at any zoom.\n    const rulerLines = [\n      ...measurements,\n      ...(gesture.kind === 'measure' ? [{ id: -1, a: gesture.start, b: gesture.current }] : []),\n    ];\n    for (const line of rulerLines) {\n      const a = toScreen(line.a);\n      const b = toScreen(line.b);\n      const length = Math.hypot(line.b.x - line.a.x, line.b.y - line.a.y);\n      if (length < EPS_MEASURE) continue;\n      const angle = Math.atan2(line.b.y - line.a.y, line.b.x - line.a.x);\n      // End ticks, perpendicular to the run, so a measurement reads as a dimension\n      // rather than as something that was drawn.\n      const nx = -Math.sin(angle) * 5;\n      const ny = Math.cos(angle) * 5;\n      const mid = { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };\n      parts.push(\n        <g\n          key={`measure-${line.id}`}\n          className={cx(\n            styles.roomDrawerMeasureLine,\n            hoveredMeasure === line.id && styles.roomDrawerMeasureLineHovered,\n            classNames?.measureLine,\n          )}\n        >\n          <line x1={a.x} y1={a.y} x2={b.x} y2={b.y} />\n          <line x1={a.x - nx} y1={a.y - ny} x2={a.x + nx} y2={a.y + ny} />\n          <line x1={b.x - nx} y1={b.y - ny} x2={b.x + nx} y2={b.y + ny} />\n        </g>,\n      );\n      // Grab handles, only while the ruler is the active tool: under any other tool a\n      // measurement is inert scenery and its handles would compete with the plan's own.\n      if (tool === 'measure' && line.id >= 0) {\n        for (const [end, p] of [\n          ['a', a],\n          ['b', b],\n        ] as const) {\n          parts.push(\n            <circle\n              key={`measure-grip-${line.id}-${end}`}\n              className={styles.roomDrawerMeasureGrip}\n              cx={p.x}\n              cy={p.y}\n              r={4}\n            />,\n          );\n        }\n      }\n      parts.push(\n        <text\n          key={`measure-label-${line.id}`}\n          className={cx(styles.roomDrawerMeasure, classNames?.measureLine)}\n          x={mid.x}\n          y={mid.y - 8}\n          textAnchor='middle'\n        >\n          {`${formatLength(length)} · ${formatAngle(toDegrees(angle))}`}\n        </text>,\n      );\n    }\n\n    // Snap guides: the construction lines explaining what the pointer locked onto.\n    // resolveSnap has always produced these; without drawing them, snapping reads as the\n    // app quietly moving the pointer somewhere the user did not ask for.\n    if (showSnapGuides && snapHint) {\n      snapHint.guides.forEach((guide, i) => {\n        const a = toScreen(guide.a);\n        const b = toScreen(guide.b);\n        parts.push(\n          <line\n            key={`guide-${i}`}\n            className={cx(styles.roomDrawerGuide, guide.kind === SnapKind.Angle && styles.roomDrawerGuideAngle)}\n            x1={a.x}\n            y1={a.y}\n            x2={b.x}\n            y2={b.y}\n          />,\n        );\n      });\n\n      // A marker on the point itself, so a node or wall snap is visible too, not just\n      // the constraint-style snaps that come with a line.\n      if (snapHint.kind !== SnapKind.None && snapHint.kind !== SnapKind.Grid) {\n        const p = toScreen(snapHint.point);\n        parts.push(\n          <rect\n            key='snap-marker'\n            className={styles.roomDrawerSnapMarker}\n            x={p.x - 4}\n            y={p.y - 4}\n            width={8}\n            height={8}\n          />,\n        );\n      }\n    }\n\n    // Rect preview.\n    if (gesture.kind === 'rect') {\n      const a = toScreen(gesture.start);\n      const b = toScreen(gesture.current);\n      const left = Math.min(a.x, b.x);\n      const top = Math.min(a.y, b.y);\n      const right = Math.max(a.x, b.x);\n      const bottom = Math.max(a.y, b.y);\n      parts.push(\n        <rect\n          key='rect-preview'\n          className={styles.roomDrawerPreview}\n          x={left}\n          y={top}\n          width={right - left}\n          height={bottom - top}\n        />,\n      );\n\n      // Both sides and the area, each sitting on what it measures. The figures are the\n      // wall CENTRELINES, which is also what the finished room reports its area from, so\n      // the number does not change the moment the drag ends.\n      const width = Math.abs(gesture.current.x - gesture.start.x);\n      const height = Math.abs(gesture.current.y - gesture.start.y);\n      if (showMeasurements && (width > 0 || height > 0)) {\n        parts.push(\n          <text\n            key='rect-width'\n            className={styles.roomDrawerMeasure}\n            x={(left + right) / 2}\n            y={top - 8}\n            textAnchor='middle'\n          >\n            {formatLength(width)}\n          </text>,\n          <text\n            key='rect-height'\n            className={styles.roomDrawerMeasure}\n            x={left - 8}\n            y={(top + bottom) / 2}\n            textAnchor='end'\n            dominantBaseline='central'\n          >\n            {formatLength(height)}\n          </text>,\n        );\n        if (width > 0 && height > 0) {\n          parts.push(\n            <text\n              key='rect-area'\n              className={cx(styles.roomDrawerMeasure, styles.roomDrawerMeasureSecondary)}\n              x={(left + right) / 2}\n              y={(top + bottom) / 2}\n              textAnchor='middle'\n              dominantBaseline='central'\n            >\n              {formatArea(width * height)}\n            </text>,\n          );\n        }\n      }\n    }\n\n    // Wall chain preview.\n    if (tool === 'wall' && wallChain.length > 0) {\n      const points = [...wallChain, cursor].filter(Boolean) as PlanPoint[];\n      const screen = points.map(toScreen);\n      parts.push(\n        <polyline\n          key='chain'\n          className={styles.roomDrawerPreview}\n          points={screen.map((p) => `${p.x},${p.y}`).join(' ')}\n          fill='none'\n        />,\n      );\n      screen.forEach((p, i) => {\n        parts.push(<circle key={`chain-dot-${i}`} className={styles.roomDrawerHandle} cx={p.x} cy={p.y} r={4} />);\n      });\n      if (showMeasurements && cursor && wallChain.length > 0) {\n        const from = wallChain[wallChain.length - 1];\n        const length = Math.hypot(cursor.x - from.x, cursor.y - from.y);\n        const mid = toScreen({ x: (from.x + cursor.x) / 2, y: (from.y + cursor.y) / 2 });\n        const locked = snapHint?.kind === SnapKind.Angle;\n        parts.push(\n          <text\n            key='chain-len'\n            className={cx(styles.roomDrawerMeasure, locked && styles.roomDrawerMeasureLocked)}\n            x={mid.x}\n            y={mid.y - 8}\n            textAnchor='middle'\n          >\n            {`${formatLength(length)} · ${\n              // Relative to the previous segment when the lock is measuring from it, since\n              // \"90° to the last wall\" is the number the user is actually aiming for.\n              snapHint?.angleRelativeTo === 'reference' && chainReferenceAngle !== undefined\n                ? relativeAngle(\n                    formatAngle(toDegrees(Math.atan2(cursor.y - from.y, cursor.x - from.x) - chainReferenceAngle)),\n                  )\n                : formatAngle(toDegrees(Math.atan2(cursor.y - from.y, cursor.x - from.x)))\n            }`}\n          </text>,\n        );\n      }\n    }\n\n    // A handle on every corner the selection implies, so a multi-selection shows exactly\n    // what a drag would move. Corners picked explicitly are filled; ones that came along\n    // because their wall or room was selected are outlined.\n    const explicitNodes = new Set(selection.filter((e) => e.type === 'node').map((e) => e.id));\n    for (const id of selectedNodeIds) {\n      const node = floor.nodes[id];\n      if (!node) continue;\n      const p = toScreen(node);\n      parts.push(\n        <circle\n          key={`handle-${id}`}\n          className={cx(styles.roomDrawerHandle, explicitNodes.has(id) && styles.roomDrawerHandleActive)}\n          cx={p.x}\n          cy={p.y}\n          r={5}\n        />,\n      );\n    }\n\n    // Marquee rectangle.\n    if (gesture.kind === 'marquee' && pressRef.current?.moved) {\n      const a = toScreen(gesture.start);\n      const b = toScreen(gesture.current);\n      parts.push(\n        <rect\n          key='marquee'\n          className={styles.roomDrawerMarquee}\n          x={Math.min(a.x, b.x)}\n          y={Math.min(a.y, b.y)}\n          width={Math.abs(b.x - a.x)}\n          height={Math.abs(b.y - a.y)}\n        />,\n      );\n    }\n\n    // The selected wall's \"add a corner\" handle. Hollow and smaller than a real corner,\n    // so it reads as something that is not there yet rather than as an existing node.\n    if (selectedWallMidpoint && gesture.kind !== 'node') {\n      const p = toScreen(selectedWallMidpoint);\n      parts.push(<circle key='midpoint' className={styles.roomDrawerMidHandle} cx={p.x} cy={p.y} r={4} />);\n    }\n\n    // Dragging a whole ROOM is a rigid translation of every node in its face, so its own\n    // walls and area are all frozen; the only things changing are how far it has moved\n    // and any wall straddling its boundary. Reporting its own dimensions here would put\n    // five stuck numbers on screen.\n    if (showMeasurements && gesture.kind === 'room' && gesture.moved) {\n      const moving = new Set(gesture.nodeIds);\n\n      const sampleId = gesture.nodeIds.find((id) => floor.nodes[id] && gesture.orig[id]);\n      if (sampleId) {\n        const now = floor.nodes[sampleId];\n        const was = gesture.orig[sampleId];\n        const dx = now.x - was.x;\n        const dy = now.y - was.y;\n        const room = single?.type === 'room' ? rooms.find((r) => r.roomId === single.id) : undefined;\n        const at = toScreen(room ? room.anchor : now);\n        parts.push(\n          <text\n            key='room-offset'\n            className={cx(styles.roomDrawerMeasure, styles.roomDrawerMeasureOffset)}\n            x={at.x}\n            y={at.y - 26}\n            textAnchor='middle'\n          >\n            {`${formatLength(Math.hypot(dx, dy))} · ${formatAngle(toDegrees(Math.atan2(dy, dx)))}`}\n          </text>,\n        );\n      }\n\n      // A wall with both ends moving travels with the room and keeps its shape; one with\n      // neither is untouched. Only the ones bridging the two are being stretched.\n      for (const wall of Object.values(floor.walls)) {\n        if (moving.has(wall.a) === moving.has(wall.b)) continue;\n        const a = floor.nodes[wall.a];\n        const b = floor.nodes[wall.b];\n        if (!a || !b) continue;\n        const tail = moving.has(wall.a) ? a : b;\n        const head = moving.has(wall.a) ? b : a;\n        const mid = toScreen({ x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 });\n        parts.push(\n          <text\n            key={`room-stretch-${wall.id}`}\n            className={cx(styles.roomDrawerMeasure, styles.roomDrawerMeasureSecondary)}\n            x={mid.x}\n            y={mid.y - 8}\n            textAnchor='middle'\n          >\n            {`${formatLength(Math.hypot(head.x - tail.x, head.y - tail.y))} · ${formatAngle(toDegrees(Math.atan2(head.y - tail.y, head.x - tail.x)))}`}\n          </text>,\n        );\n      }\n    }\n\n    // A CORNER reports every wall meeting it, since reshaping it changes all of them. A\n    // WALL reports itself plus the walls running off its ends: dragging a wall moves both\n    // its endpoints, so its own length is the one number that cannot change. Single\n    // selections only; a multi-selection moves rigidly and uses the offset readout above.\n    if (showMeasurements && (single?.type === 'node' || single?.type === 'wall')) {\n      const measured: Array<{ wall: (typeof floor.walls)[string]; from: string; primary: boolean }> = [];\n      const seen = new Set<string>();\n      const add = (wallId: string, from: string, primary: boolean) => {\n        if (seen.has(wallId)) return;\n        const wall = floor.walls[wallId];\n        if (!wall) return;\n        seen.add(wallId);\n        measured.push({ wall, from, primary });\n      };\n\n      if (single.type === 'node') {\n        for (const wall of Object.values(floor.walls)) {\n          if (wall.a === single.id || wall.b === single.id) add(wall.id, single.id, true);\n        }\n      } else {\n        const selected = floor.walls[single.id];\n        if (selected) {\n          add(selected.id, selected.a, true);\n          // Everything attached at either end. These are what actually resize as the\n          // selected wall is pushed sideways.\n          for (const wall of Object.values(floor.walls)) {\n            if (wall.id === selected.id) continue;\n            for (const end of [selected.a, selected.b]) {\n              if (wall.a === end || wall.b === end) add(wall.id, end, false);\n            }\n          }\n        }\n      }\n\n      const locked = snapHint?.kind === SnapKind.Angle && gesture.kind !== 'none';\n      for (const { wall, from, primary } of measured) {\n        const a = floor.nodes[wall.a];\n        const b = floor.nodes[wall.b];\n        if (!a || !b) continue;\n        // Measured outward from the shared end, so the angle describes the wall as it\n        // leaves that point rather than depending on which end it was stored from.\n        const tail = wall.a === from ? a : b;\n        const head = wall.a === from ? b : a;\n        const mid = toScreen({ x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 });\n        parts.push(\n          <text\n            key={`measure-${wall.id}`}\n            className={cx(\n              styles.roomDrawerMeasure,\n              !primary && styles.roomDrawerMeasureSecondary,\n              primary && locked && styles.roomDrawerMeasureLocked,\n            )}\n            x={mid.x}\n            y={mid.y - 8}\n            textAnchor='middle'\n          >\n            {`${formatLength(Math.hypot(head.x - tail.x, head.y - tail.y))} · ${formatAngle(toDegrees(Math.atan2(head.y - tail.y, head.x - tail.x)))}`}\n          </text>,\n        );\n      }\n    }\n\n    return <>{parts}</>;\n  }, [\n    editable,\n    gesture,\n    tool,\n    wallChain,\n    cursor,\n    selection,\n    floor,\n    rooms,\n    single,\n    selectedNodeIds,\n    selectedWallMidpoint,\n    showMeasurements,\n    showSnapGuides,\n    snapHint,\n    chainReferenceAngle,\n    formatLength,\n    formatArea,\n    formatAngle,\n    relativeAngle,\n    measurements,\n    hoveredMeasure,\n    classNames?.measureLine,\n    toScreen,\n  ]);\n\n  // ─── Chrome ───────────────────────────────────────────────────────────────────\n\n  const TOOL_META: Record<RoomDrawerTool, { icon: typeof faSquare; label: string; key: string }> = {\n    select: { icon: faArrowPointer, label: text.select, key: 'V' },\n    wall: { icon: faMinus, label: text.wall, key: 'W' },\n    rect: { icon: faSquare, label: text.rect, key: 'R' },\n    door: { icon: faDoorOpen, label: text.door, key: 'D' },\n    window: { icon: faWindowMaximize, label: text.window, key: 'N' },\n    archway: { icon: faExpand, label: text.archway, key: 'A' },\n    measure: { icon: faRulerCombined, label: text.measure, key: 'M' },\n  };\n\n  const hint = (() => {\n    if (!editable) return '';\n    if (tool === 'wall') return text.hintWall;\n    if (tool === 'rect') return text.hintRect;\n    if (tool === 'measure') return text.hintMeasure;\n    if (OPENING_TOOL_KIND[tool]) return text.hintOpening;\n    return multiSelect ? text.hintSelect : text.hintSelectSingle;\n  })();\n\n  /**\n   * What the inspector calls the current selection. It is the only thing in the bar for a\n   * corner, which has nothing to edit that dragging it does not already do.\n   */\n  const selectionNoun = (() => {\n    if (selection.length === 0) return text.selectedFloor;\n    if (selection.length > 1) return text.selectedCount(selection.length);\n    if (selectedOpening) {\n      const nouns: Record<OpeningKind, string> = {\n        [OpeningKind.Door]: text.selectedDoor,\n        [OpeningKind.Window]: text.selectedWindow,\n        [OpeningKind.Archway]: text.selectedArchway,\n        [OpeningKind.Gap]: text.selectedGap,\n      };\n      return nouns[selectedOpening.kind];\n    }\n    if (selectedWall) return text.selectedWall;\n    if (single?.type === 'room') return text.selectedRoom;\n    return text.selectedCorner;\n  })();\n\n  const visibleTools = editable ? tools : [];\n\n  return (\n    <div\n      {...rest}\n      ref={rootRef}\n      className={cx(styles.roomDrawer, disabled && styles.roomDrawerDisabled, className, classNames?.root)}\n      style={{ ...style, ...slotStyles?.root }}\n      role='application'\n      aria-label={ariaLabel ?? 'Floor plan editor'}\n      tabIndex={disabled ? -1 : 0}\n      onKeyDown={handleKeyDown}\n      onKeyUp={handleKeyUp}\n    >\n      {showToolbar && (\n        <div\n          className={cx(styles.roomDrawerToolbar, classNames?.toolbar)}\n          style={slotStyles?.toolbar}\n          role='toolbar'\n          aria-label={text.tools}\n        >\n          {visibleTools.map((t) => {\n            const meta = TOOL_META[t];\n            return (\n              <button\n                key={t}\n                type='button'\n                className={cx(\n                  styles.roomDrawerToolButton,\n                  t === tool && styles.roomDrawerToolButtonActive,\n                  classNames?.toolButton,\n                )}\n                style={slotStyles?.toolButton}\n                title={`${meta.label} (${meta.key})`}\n                aria-label={meta.label}\n                aria-pressed={t === tool}\n                onClick={() => {\n                  setTool(t);\n                  endWallChain();\n                }}\n              >\n                {renderIcon(icons?.[t] ?? meta.icon, {\n                  className: cx(styles.roomDrawerToolIcon, classNames?.toolIcon),\n                  style: slotStyles?.toolIcon,\n                })}\n              </button>\n            );\n          })}\n\n          {visibleTools.length > 0 && <span className={styles.roomDrawerToolbarSeparator} />}\n\n          {editable && (\n            <>\n              {(\n                [\n                  {\n                    on: snapEnabled,\n                    toggle: () => setSnapEnabled((v) => !v),\n                    icon: icons?.snap ?? faMagnet,\n                    label: text.snap,\n                    key: 'S',\n                  },\n                  {\n                    on: angleLockEnabled,\n                    toggle: () => setAngleLockEnabled((v) => !v),\n                    icon: icons?.angleLock ?? faDraftingCompass,\n                    label: text.angleLock(constrainAngle),\n                    key: 'L',\n                  },\n                  {\n                    on: gridVisible,\n                    toggle: () => setGridVisible((v) => !v),\n                    icon: icons?.grid ?? faBorderAll,\n                    label: text.grid,\n                    key: 'G',\n                  },\n                  // Only worth offering when there is a storey below to trace.\n                  ...(floorBelow(plan, floor.id)\n                    ? [\n                        {\n                          on: underlayOn,\n                          toggle: () => setUnderlayOn((v) => !v),\n                          icon: icons?.underlay ?? faLayerGroup,\n                          label: text.underlay,\n                          key: 'B',\n                        },\n                      ]\n                    : []),\n                ] as const\n              ).map((toggle) => (\n                <button\n                  key={toggle.key}\n                  type='button'\n                  className={cx(\n                    styles.roomDrawerToolButton,\n                    toggle.on && styles.roomDrawerToolButtonOn,\n                    classNames?.toolButton,\n                  )}\n                  style={slotStyles?.toolButton}\n                  title={`${toggle.label} (${toggle.key})`}\n                  aria-label={toggle.label}\n                  aria-pressed={toggle.on}\n                  onClick={toggle.toggle}\n                >\n                  {renderIcon(toggle.icon, {\n                    className: cx(styles.roomDrawerToolIcon, classNames?.toolIcon),\n                    style: slotStyles?.toolIcon,\n                  })}\n                </button>\n              ))}\n              <span className={styles.roomDrawerToolbarSeparator} />\n            </>\n          )}\n\n          {showHistoryControls && historyMode !== 'none' && editable && (\n            <>\n              <button\n                type='button'\n                className={cx(styles.roomDrawerViewButton, classNames?.viewButton)}\n                style={slotStyles?.viewButton}\n                title={text.undo}\n                aria-label={text.undo}\n                disabled={!history.canUndo}\n                onClick={undo}\n              >\n                {renderIcon(icons?.undo ?? faArrowRotateLeft)}\n              </button>\n              <button\n                type='button'\n                className={cx(styles.roomDrawerViewButton, classNames?.viewButton)}\n                style={slotStyles?.viewButton}\n                title={text.redo}\n                aria-label={text.redo}\n                disabled={!history.canRedo}\n                onClick={redo}\n              >\n                {renderIcon(icons?.redo ?? faArrowRotateRight)}\n              </button>\n              <span className={styles.roomDrawerToolbarSeparator} />\n            </>\n          )}\n\n          {showFileControls && (\n            <>\n              <button\n                type='button'\n                className={cx(styles.roomDrawerViewButton, classNames?.viewButton)}\n                style={slotStyles?.viewButton}\n                title={text.download}\n                aria-label={text.download}\n                onClick={() => downloadPlan()}\n              >\n                {renderIcon(icons?.download ?? faDownload)}\n              </button>\n              {editable && (\n                <button\n                  type='button'\n                  className={cx(styles.roomDrawerViewButton, classNames?.viewButton)}\n                  style={slotStyles?.viewButton}\n                  title={text.upload}\n                  aria-label={text.upload}\n                  onClick={() => fileInputRef.current?.click()}\n                >\n                  {renderIcon(icons?.upload ?? faUpload)}\n                </button>\n              )}\n              <span className={styles.roomDrawerToolbarSeparator} />\n            </>\n          )}\n\n          <div className={cx(styles.roomDrawerViewControls, classNames?.viewControls)} style={slotStyles?.viewControls}>\n            <button\n              type='button'\n              className={cx(styles.roomDrawerViewButton, classNames?.viewButton)}\n              style={slotStyles?.viewButton}\n              title={text.zoomOut}\n              aria-label={text.zoomOut}\n              onClick={() => zoomBy(1 / 1.25)}\n            >\n              {renderIcon(icons?.zoomOut ?? faMagnifyingGlassMinus)}\n            </button>\n            <button\n              type='button'\n              className={cx(styles.roomDrawerZoomLabel, classNames?.zoomLabel)}\n              style={slotStyles?.zoomLabel}\n              title={text.fit}\n              onClick={() => fitToContent()}\n            >\n              {formatZoom(view.zoom)}\n            </button>\n            <button\n              type='button'\n              className={cx(styles.roomDrawerViewButton, classNames?.viewButton)}\n              style={slotStyles?.viewButton}\n              title={text.zoomIn}\n              aria-label={text.zoomIn}\n              onClick={() => zoomBy(1.25)}\n            >\n              {renderIcon(icons?.zoomIn ?? faMagnifyingGlassPlus)}\n            </button>\n          </div>\n        </div>\n      )}\n\n      {showFloorBar && plan.floors.length > 0 && (\n        <div\n          className={cx(styles.roomDrawerFloorBar, classNames?.floorBar)}\n          style={slotStyles?.floorBar}\n          role='tablist'\n          aria-label={text.selectedFloor}\n        >\n          {/* Top storey first, so the strip reads the way a building does in section\n              rather than the way the array happens to be ordered. */}\n          {[...plan.floors].reverse().map((f) => (\n            <button\n              key={f.id}\n              type='button'\n              role='tab'\n              className={cx(\n                styles.roomDrawerFloorTab,\n                f.id === floor.id && styles.roomDrawerFloorTabActive,\n                classNames?.floorTab,\n              )}\n              style={slotStyles?.floorTab}\n              aria-selected={f.id === floor.id}\n              onClick={() => selectFloor(f.id)}\n            >\n              {f.label}\n            </button>\n          ))}\n          {editable && (\n            <button\n              type='button'\n              className={cx(styles.roomDrawerFloorTab, styles.roomDrawerFloorTabAdd)}\n              title={text.addFloor}\n              aria-label={text.addFloor}\n              onClick={() => addFloorAt()}\n            >\n              {renderIcon(icons?.addFloor ?? faPlus)}\n            </button>\n          )}\n        </div>\n      )}\n\n      <div\n        ref={canvasWrapRef}\n        className={cx(\n          styles.roomDrawerCanvasWrapper,\n          editable && tool !== 'select' && styles.roomDrawerCanvasWrapperDrawing,\n          classNames?.canvasWrapper,\n        )}\n        style={{ height: canvasHeight, ...slotStyles?.canvasWrapper }}\n        // The middle button pans the plan. Left alone, the browser starts its autoscroll on the\n        // same press and scrolls the page away underneath the pan.\n        onMouseDown={(e) => {\n          if (!disabled && e.button === 1) e.preventDefault();\n        }}\n        onPointerDown={handlePointerDown}\n        onPointerMove={handlePointerMove}\n        onPointerUp={(e) => finishGesture(e, false)}\n        onPointerCancel={(e) => finishGesture(e, true)}\n      >\n        <PlanCanvas\n          ref={svgRef}\n          floor={floor}\n          underlay={underlayFloor}\n          rooms={rooms}\n          view={view}\n          size={size}\n          selection={selection}\n          hovered={hovered}\n          interactiveRooms={false}\n          showGrid={gridVisible}\n          gridSize={gridSize}\n          showRoomLabels\n          formatArea={formatArea}\n          getRoomColor={getRoomColor}\n          renderRoomLabel={renderRoomLabel}\n          overlay={overlay}\n          classNames={classNames}\n          styles={slotStyles}\n        />\n      </div>\n\n      {showInspector && editable && (\n        <div className={cx(styles.roomDrawerInspector, classNames?.inspector)} style={slotStyles?.inspector}>\n          <span\n            className={cx(\n              styles.roomDrawerInspectorKind,\n              selection.length === 0 && styles.roomDrawerInspectorKindEmpty,\n              classNames?.inspectorLabel,\n            )}\n          >\n            {selectionNoun}\n          </span>\n\n          {selection.length === 0 && (\n            <>\n              <Input\n                className={cx(styles.roomDrawerInspectorName, classNames?.inspectorField)}\n                style={slotStyles?.inspectorField}\n                label={text.floorName}\n                value={floor.label}\n                onChange={(next) => renameFloorById(floor.id, next)}\n              />\n              <NumberInput\n                className={cx(styles.roomDrawerInspectorField, classNames?.inspectorField)}\n                style={slotStyles?.inspectorField}\n                label={text.floorHeight}\n                title={text.storeyHeightHint}\n                value={floor.height}\n                decimalScale={0}\n                min={MIN_FLOOR_HEIGHT}\n                step={100}\n                showArrows\n                onChange={(next) => {\n                  if (next === undefined) return;\n                  setFloorHeightById(floor.id, next);\n                }}\n              />\n              {/* The strip lists storeys top first, so \"up\" is a higher index. Only\n                  offered when there is somewhere to go. */}\n              {plan.floors.length > 1 && (\n                <>\n                  <button\n                    type='button'\n                    className={cx(styles.roomDrawerInspectorAction, classNames?.inspectorField)}\n                    style={slotStyles?.inspectorField}\n                    title={text.moveFloorUp}\n                    aria-label={text.moveFloorUp}\n                    disabled={floorIndex(plan, floor.id) >= plan.floors.length - 1}\n                    onClick={() => moveFloorTo(floor.id, floorIndex(plan, floor.id) + 1)}\n                  >\n                    {renderIcon(icons?.moveFloorUp ?? faAngleUp)}\n                  </button>\n                  <button\n                    type='button'\n                    className={cx(styles.roomDrawerInspectorAction, classNames?.inspectorField)}\n                    style={slotStyles?.inspectorField}\n                    title={text.moveFloorDown}\n                    aria-label={text.moveFloorDown}\n                    disabled={floorIndex(plan, floor.id) <= 0}\n                    onClick={() => moveFloorTo(floor.id, floorIndex(plan, floor.id) - 1)}\n                  >\n                    {renderIcon(icons?.moveFloorDown ?? faAngleDown)}\n                  </button>\n                </>\n              )}\n              <button\n                type='button'\n                className={cx(styles.roomDrawerInspectorAction, classNames?.inspectorField)}\n                style={slotStyles?.inspectorField}\n                title={text.duplicateFloor}\n                onClick={() => duplicateActiveFloor()}\n              >\n                {renderIcon(icons?.duplicateFloor ?? faClone)}\n              </button>\n              {plan.floors.length > 1 && (\n                <ActionIcon\n                  color={ThemeColor.Error}\n                  className={styles.roomDrawerInspectorDelete}\n                  icon={icons?.delete ?? faTrash}\n                  title={text.deleteFloor}\n                  onClick={() => removeFloorById()}\n                />\n              )}\n            </>\n          )}\n\n          {selectedRoom?.meta && (\n            <>\n              <Input\n                className={cx(styles.roomDrawerInspectorName, classNames?.inspectorField)}\n                style={slotStyles?.inspectorField}\n                label={text.roomName}\n                value={selectedRoom.meta.name}\n                onChange={(next) => {\n                  const room = selectedRoom.meta!;\n                  commitEdit({ ...floor, rooms: { ...floor.rooms, [room.id]: { ...room, name: next } } }, 'edit-room', [\n                    room.id,\n                  ]);\n                }}\n              />\n              <span className={cx(styles.roomDrawerInspectorLabel, classNames?.inspectorLabel)}>\n                {formatArea(selectedRoom.area)}\n              </span>\n            </>\n          )}\n\n          {selectedWall && (\n            <button\n              type='button'\n              className={cx(styles.roomDrawerInspectorAction, classNames?.inspectorField)}\n              style={slotStyles?.inspectorField}\n              onClick={addCornerToSelectedWall}\n            >\n              {text.addCorner}\n            </button>\n          )}\n\n          {selectedWall && (\n            <NumberInput\n              className={cx(styles.roomDrawerInspectorField, classNames?.inspectorField)}\n              style={slotStyles?.inspectorField}\n              label={text.thickness}\n              value={selectedWall.thickness}\n              decimalScale={0}\n              min={10}\n              showArrows\n              step={10}\n              onChange={(next) => {\n                if (next === undefined) return;\n                commitEdit(\n                  {\n                    ...floor,\n                    walls: { ...floor.walls, [selectedWall.id]: { ...selectedWall, thickness: next } },\n                  },\n                  'edit-wall',\n                  [selectedWall.id],\n                );\n              }}\n            />\n          )}\n\n          {selectedOpening && (\n            <>\n              <NumberInput\n                className={cx(styles.roomDrawerInspectorField, classNames?.inspectorField)}\n                style={slotStyles?.inspectorField}\n                label={text.width}\n                value={selectedOpening.width}\n                decimalScale={0}\n                min={100}\n                step={50}\n                showArrows\n                onChange={(next) => {\n                  if (next === undefined) return;\n                  commitEdit(updateOpening(floor, selectedOpening.id, { width: next }).floor, 'edit-opening', [\n                    selectedOpening.id,\n                  ]);\n                }}\n              />\n              <NumberInput\n                className={cx(styles.roomDrawerInspectorField, classNames?.inspectorField)}\n                style={slotStyles?.inspectorField}\n                label={text.sill}\n                title={text.sillHeightHint}\n                showArrows\n                value={selectedOpening.sill}\n                decimalScale={0}\n                min={0}\n                step={50}\n                onChange={(next) => {\n                  if (next === undefined) return;\n                  commitEdit(updateOpening(floor, selectedOpening.id, { sill: next }).floor, 'edit-opening', [\n                    selectedOpening.id,\n                  ]);\n                }}\n              />\n            </>\n          )}\n\n          {selection.length > 0 && (\n            <ActionIcon\n              color={ThemeColor.Error}\n              className={styles.roomDrawerInspectorDelete}\n              icon={icons?.delete ?? faTrash}\n              title={text.deleteSelection}\n              onClick={deleteSelection}\n            />\n          )}\n        </div>\n      )}\n\n      {showFileControls && editable && (\n        <input\n          ref={fileInputRef}\n          type='file'\n          accept='application/json,.json'\n          className={styles.roomDrawerFileInput}\n          tabIndex={-1}\n          aria-hidden='true'\n          onChange={(event) => {\n            const file = event.target.files?.[0];\n            // Cleared so picking the same file twice running still fires a change.\n            event.target.value = '';\n            if (file) openFile(file);\n          }}\n        />\n      )}\n\n      {showHint && (importReport !== null || hint) && (\n        <div\n          className={cx(styles.roomDrawerHint, importReport !== null && styles.roomDrawerHintReport, classNames?.hint)}\n          style={slotStyles?.hint}\n          role={importReport !== null ? 'status' : undefined}\n        >\n          {importReport === null ? hint : importReport.length === 0 ? text.importFailed : importReport.join(' · ')}\n        </div>\n      )}\n    </div>\n  );\n}\n\nexport default RoomDrawer;\n",".roomViewer {\n  display: flex;\n  flex-direction: column;\n  gap: var(--spacing-s, 8px);\n  position: relative;\n\n  /* Storeys, top first, the way a building reads in section. */\n  &-floorBar {\n    display: flex;\n    align-items: center;\n    gap: 2px;\n    flex-wrap: wrap;\n    padding: var(--spacing-xs, 4px);\n    background: var(--background-accent);\n    border: 1px solid var(--border-color);\n    border-radius: var(--default-border-radius);\n  }\n\n  &-floorTab {\n    height: 28px;\n    padding: 0 10px;\n    border: 1px solid transparent;\n    border-radius: var(--default-border-radius);\n    background: transparent;\n    color: var(--text-dark);\n    font-size: 12px;\n    white-space: nowrap;\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n\n    &:hover {\n      background: var(--background-accent-light);\n    }\n\n    &:focus-visible {\n      outline: none;\n      border-color: var(--focus-border-color, var(--primary-color));\n    }\n\n    &-active {\n      background: var(--primary-color);\n      border-color: var(--primary-color);\n      color: var(--text-on-primary);\n      font-weight: 600;\n\n      &:hover {\n        background: var(--primary-color);\n      }\n    }\n  }\n\n  &-canvasWrapper {\n    position: relative;\n    overflow: hidden;\n    border: 1px solid var(--border-color);\n    border-radius: var(--default-border-radius);\n    background: var(--background);\n\n    &-pannable {\n      /* Without this the browser claims the gesture and pointermove stops firing. */\n      touch-action: none;\n      cursor: grab;\n      -webkit-tap-highlight-color: transparent;\n    }\n  }\n\n  &-viewControls {\n    position: absolute;\n    right: var(--spacing-s, 8px);\n    bottom: var(--spacing-s, 8px);\n    display: flex;\n    gap: 2px;\n    padding: 2px;\n    background: var(--background-accent);\n    border: 1px solid var(--border-color);\n    border-radius: var(--default-border-radius);\n  }\n\n  &-viewButton {\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    width: 28px;\n    height: 28px;\n    border: 1px solid transparent;\n    border-radius: var(--default-border-radius);\n    background: transparent;\n    color: var(--text-color);\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n\n    &:hover {\n      background: var(--background-accent-light);\n    }\n\n    &:focus-visible {\n      outline: none;\n      border-color: var(--focus-border-color, var(--primary-color));\n    }\n  }\n\n  &-empty {\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    height: 100%;\n    color: var(--text-dark);\n    font-size: 13px;\n  }\n\n  /* Announces the selection to a screen reader. A room selected by clicking its shape is\n     otherwise silent, and there is nowhere useful for focus to land. */\n  &-live {\n    position: absolute;\n    width: 1px;\n    height: 1px;\n    padding: 0;\n    border: 0;\n    clip-path: inset(50%);\n    overflow: hidden;\n    white-space: nowrap;\n  }\n}\n","import { useCallback, useEffect, useImperativeHandle, useId, useMemo, useRef, useState } from 'react';\nimport type { PointerEvent as ReactPointerEvent } from 'react';\nimport cx from 'classnames';\nimport { faExpand, faMagnifyingGlassMinus, faMagnifyingGlassPlus } from '@fortawesome/free-solid-svg-icons';\n\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport {\n  clientToWorld,\n  derivePlan,\n  floorBounds,\n  getFloor,\n  type PlanFloor,\n  type PlanSelection,\n  type PlanView,\n} from '../floorPlan';\nimport PlanCanvas from '../planCanvas';\nimport { renderIcon } from '../utils/renderIcon';\nimport type { RoomViewerLabels, RoomViewerProps } from './roomViewer.types';\nimport styles from './roomViewer.module.pcss';\n\nconst MIN_ZOOM = 0.002;\nconst MAX_ZOOM = 4;\n/** Past this many pixels a press is a pan rather than a click on a room. */\nconst PAN_THRESHOLD_PX = 4;\n\nfunction formatAreaDefault(areaMm2: number): string {\n  const m2 = areaMm2 / 1e6;\n  return `${m2 < 10 ? m2.toFixed(1) : Math.round(m2)} m²`;\n}\n\nconst DEFAULT_LABELS: Required<RoomViewerLabels> = {\n  floors: 'Floors',\n  zoomIn: 'Zoom in',\n  zoomOut: 'Zoom out',\n  fit: 'Fit to view',\n  empty: 'Nothing to show',\n  unnamedRoom: 'Unnamed room',\n  selectedRoom: (name, area) => `${name} selected, ${area}`,\n  noRoomSelected: 'No room selected',\n};\n\n/**\n * Read-only view of a floor plan.\n *\n * It renders the same `planCanvas` the drawer does, from the same `FloorPlan`, so the\n * two cannot drift apart. Selection and hover are React state rather than classes pushed\n * onto DOM nodes, which is what lets them be controlled from outside.\n */\nfunction RoomViewer(props: RoomViewerProps) {\n  const {\n    ref,\n    plan,\n    activeFloorId,\n    defaultActiveFloorId,\n    onFloorChange,\n    selectedRoomId,\n    defaultSelectedRoomId = null,\n    onSelect,\n    hoveredRoomId,\n    onHoverChange,\n    view,\n    defaultView,\n    onViewChange,\n    interactive = true,\n    pannable = true,\n    fitOnMount = true,\n    showFloorBar = true,\n    showViewControls = true,\n    showGrid = false,\n    showRoomLabels = true,\n    showOpeningSymbols = true,\n    getRoomColor,\n    renderRoomLabel,\n    getRoomLabel,\n    formatArea = formatAreaDefault,\n    height = 360,\n    emptyState,\n    labels,\n    icons,\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    'aria-label': ariaLabel,\n    ...rest\n  } = useComponentDefaults('RoomViewer', props);\n  const text = useLabels('RoomViewer', labels, DEFAULT_LABELS);\n\n  const wrapperRef = useRef<HTMLDivElement>(null);\n  const svgRef = useRef<SVGSVGElement>(null);\n  const liveId = useId().replace(/:/g, '');\n\n  const floors = plan?.floors ?? [];\n\n  // ─── Floor ────────────────────────────────────────────────────────────────────\n\n  const [floorIdState, setFloorIdState] = useState<string | undefined>(defaultActiveFloorId);\n  const currentFloorId = activeFloorId ?? floorIdState ?? floors[0]?.id;\n  const floor: PlanFloor | null = useMemo(\n    () => (plan ? (getFloor(plan, currentFloorId) ?? getFloor(plan)) : null),\n    [plan, currentFloorId],\n  );\n\n  const selectFloor = useCallback(\n    (nextId: string) => {\n      if (activeFloorId === undefined) setFloorIdState(nextId);\n      onFloorChange?.(nextId);\n    },\n    [activeFloorId, onFloorChange],\n  );\n\n  const rooms = useMemo(() => (floor ? derivePlan(floor).rooms : []), [floor]);\n\n  // ─── Selection and hover ──────────────────────────────────────────────────────\n\n  const [selectedState, setSelectedState] = useState<string | null>(defaultSelectedRoomId);\n  const selected = selectedRoomId !== undefined ? selectedRoomId : selectedState;\n\n  const [hoveredState, setHoveredState] = useState<string | null>(null);\n  const hovered = hoveredRoomId !== undefined ? hoveredRoomId : hoveredState;\n\n  const selectRoom = useCallback(\n    (roomId: string | null) => {\n      if (selectedRoomId === undefined) setSelectedState(roomId);\n      const room = roomId ? (rooms.find((r) => r.roomId === roomId) ?? null) : null;\n      onSelect?.(roomId, room, floor?.id ?? '');\n    },\n    [selectedRoomId, rooms, onSelect, floor?.id],\n  );\n\n  const setHovered = useCallback(\n    (roomId: string | null) => {\n      if (hoveredRoomId === undefined) setHoveredState(roomId);\n      onHoverChange?.(roomId);\n    },\n    [hoveredRoomId, onHoverChange],\n  );\n\n  // A selection made by clicking a shape is silent to a screen reader, so it is\n  // announced separately rather than relying on focus landing somewhere useful.\n  const announcement = (() => {\n    const room = selected ? rooms.find((r) => r.roomId === selected) : undefined;\n    if (!room) return text.noRoomSelected;\n    return text.selectedRoom(room.meta?.name || text.unnamedRoom, formatArea(room.area));\n  })();\n\n  // ─── View ─────────────────────────────────────────────────────────────────────\n\n  const [viewState, setViewState] = useState<PlanView>(defaultView ?? { x: 0, y: 0, zoom: 0.05 });\n  const currentView = view ?? viewState;\n  const viewRef = useRef(currentView);\n  viewRef.current = currentView;\n\n  const applyView = useCallback(\n    (next: PlanView) => {\n      if (view === undefined) setViewState(next);\n      onViewChange?.(next);\n    },\n    [view, onViewChange],\n  );\n\n  const userMovedRef = useRef(false);\n  const [size, setSize] = useState({ width: 800, height: 360 });\n  useEffect(() => {\n    const element = wrapperRef.current;\n    if (!element) return;\n    const measure = () => {\n      const rect = element.getBoundingClientRect();\n      if (rect.width > 0 && rect.height > 0) {\n        setSize({ width: Math.round(rect.width), height: Math.round(rect.height) });\n      }\n    };\n    const observer = new ResizeObserver(measure);\n    observer.observe(element);\n    measure();\n    return () => observer.disconnect();\n  }, []);\n\n  const fitToContent = useCallback(\n    (options: { padding?: number } = {}) => {\n      const padding = options.padding ?? 24;\n      const bounds = floor ? floorBounds(floor) : null;\n      if (!bounds || bounds.width <= 0 || bounds.height <= 0) {\n        applyView({ x: -1000, y: -1000, zoom: 0.05 });\n        return;\n      }\n      const zoom = Math.max(\n        MIN_ZOOM,\n        Math.min(\n          MAX_ZOOM,\n          Math.min((size.width - padding * 2) / bounds.width, (size.height - padding * 2) / bounds.height),\n        ),\n      );\n      applyView({\n        x: bounds.minX - (size.width / zoom - bounds.width) / 2,\n        y: bounds.minY - (size.height / zoom - bounds.height) / 2,\n        zoom,\n      });\n    },\n    [floor, size.width, size.height, applyView],\n  );\n\n  /**\n   * Refit on anything that changes what \"fitted\" means — the floor, and the canvas size,\n   * which is not known until the element has been measured. The moment the viewer pans or\n   * zooms, the view is theirs and we stop touching it.\n   */\n  useEffect(() => {\n    if (!fitOnMount || userMovedRef.current) return;\n    if (!floor || size.width <= 0 || Object.keys(floor.nodes).length === 0) return;\n    fitToContent();\n  }, [fitOnMount, floor, size.width, size.height, fitToContent]);\n\n  const zoomBy = useCallback(\n    (factor: number, centre?: { x: number; y: number }) => {\n      userMovedRef.current = true;\n      const v = viewRef.current;\n      const next = Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, v.zoom * factor));\n      if (next === v.zoom) return;\n      const cx0 = centre?.x ?? size.width / 2;\n      const cy0 = centre?.y ?? size.height / 2;\n      // Keep the world point under the cursor where it is.\n      applyView({\n        x: v.x + cx0 / v.zoom - cx0 / next,\n        y: v.y + cy0 / v.zoom - cy0 / next,\n        zoom: next,\n      });\n    },\n    [applyView, size.width, size.height],\n  );\n\n  // ─── Pan and pinch ────────────────────────────────────────────────────────────\n\n  const pointersRef = useRef(new Map<number, { x: number; y: number }>());\n  const panRef = useRef<{ startClient: { x: number; y: number }; startView: PlanView; moved: boolean } | null>(null);\n  const pinchRef = useRef<{ dist: number; view: PlanView; mid: { x: number; y: number } } | null>(null);\n\n  const handlePointerDown = useCallback(\n    (event: ReactPointerEvent<HTMLDivElement>) => {\n      if (!pannable) return;\n      pointersRef.current.set(event.pointerId, { x: event.clientX, y: event.clientY });\n      // Deliberately NOT capturing the pointer yet. While a capture is active the click\n      // is dispatched to the capturing element instead of the shape under the cursor, so\n      // capturing here would mean a room could never be clicked. The capture is taken in\n      // the move handler, once the press has actually turned into a pan.\n      if (pointersRef.current.size === 2) {\n        const [p1, p2] = [...pointersRef.current.values()];\n        pinchRef.current = {\n          dist: Math.hypot(p2.x - p1.x, p2.y - p1.y),\n          view: viewRef.current,\n          mid: { x: (p1.x + p2.x) / 2, y: (p1.y + p2.y) / 2 },\n        };\n        panRef.current = null;\n        return;\n      }\n      panRef.current = {\n        startClient: { x: event.clientX, y: event.clientY },\n        startView: viewRef.current,\n        moved: false,\n      };\n    },\n    [pannable],\n  );\n\n  const handlePointerMove = useCallback(\n    (event: ReactPointerEvent<HTMLDivElement>) => {\n      if (pointersRef.current.has(event.pointerId)) {\n        pointersRef.current.set(event.pointerId, { x: event.clientX, y: event.clientY });\n      }\n\n      const pinch = pinchRef.current;\n      if (pinch && pointersRef.current.size >= 2) {\n        const [p1, p2] = [...pointersRef.current.values()];\n        const now = Math.hypot(p2.x - p1.x, p2.y - p1.y);\n        if (pinch.dist <= 0) return;\n        const zoom = Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, pinch.view.zoom * (now / pinch.dist)));\n        const rect = wrapperRef.current?.getBoundingClientRect() ?? { left: 0, top: 0 };\n        const mid = { x: (p1.x + p2.x) / 2, y: (p1.y + p2.y) / 2 };\n        userMovedRef.current = true;\n        const worldMid = clientToWorld(pinch.mid.x, pinch.mid.y, rect, pinch.view);\n        applyView({\n          x: worldMid.x - (mid.x - rect.left) / zoom,\n          y: worldMid.y - (mid.y - rect.top) / zoom,\n          zoom,\n        });\n        return;\n      }\n\n      const pan = panRef.current;\n      if (!pan) return;\n      const dx = event.clientX - pan.startClient.x;\n      const dy = event.clientY - pan.startClient.y;\n      if (!pan.moved && Math.hypot(dx, dy) <= PAN_THRESHOLD_PX) return;\n      if (!pan.moved) {\n        (event.currentTarget as HTMLElement).setPointerCapture?.(event.pointerId);\n      }\n      pan.moved = true;\n      userMovedRef.current = true;\n      applyView({\n        x: pan.startView.x - dx / pan.startView.zoom,\n        y: pan.startView.y - dy / pan.startView.zoom,\n        zoom: pan.startView.zoom,\n      });\n    },\n    [applyView],\n  );\n\n  const handlePointerUp = useCallback((event: ReactPointerEvent<HTMLDivElement>) => {\n    pointersRef.current.delete(event.pointerId);\n    if (pointersRef.current.size < 2) pinchRef.current = null;\n    panRef.current = null;\n  }, []);\n\n  // React's onWheel is passive, so it cannot preventDefault and the page scrolls instead.\n  useEffect(() => {\n    const element = wrapperRef.current;\n    if (!element || !pannable) return;\n    const onWheel = (event: WheelEvent) => {\n      event.preventDefault();\n      const rect = element.getBoundingClientRect();\n      zoomBy(event.deltaY < 0 ? 1.1 : 1 / 1.1, {\n        x: event.clientX - rect.left,\n        y: event.clientY - rect.top,\n      });\n    };\n    element.addEventListener('wheel', onWheel, { passive: false });\n    return () => element.removeEventListener('wheel', onWheel);\n  }, [pannable, zoomBy]);\n\n  // ─── Handle ───────────────────────────────────────────────────────────────────\n\n  useImperativeHandle(\n    ref,\n    () => ({\n      fitToContent,\n      getView: () => viewRef.current,\n      setView: applyView,\n      zoomBy: (factor: number) => zoomBy(factor),\n      select: selectRoom,\n      getFloorId: () => floor?.id ?? null,\n      setFloorId: selectFloor,\n      getSvgElement: () => svgRef.current,\n    }),\n    [fitToContent, applyView, zoomBy, selectRoom, floor?.id, selectFloor],\n  );\n\n  // ─── Render ───────────────────────────────────────────────────────────────────\n\n  const selection: readonly PlanSelection[] = useMemo(\n    () => (selected ? [{ type: 'room', id: selected } as PlanSelection] : []),\n    [selected],\n  );\n  const hoveredSelection: PlanSelection | null = useMemo(\n    () => (hovered ? { type: 'room', id: hovered } : null),\n    [hovered],\n  );\n\n  const isEmpty = !floor || Object.keys(floor.walls).length === 0;\n\n  return (\n    <div\n      {...rest}\n      className={cx(styles.roomViewer, className, classNames?.root)}\n      style={{ ...style, ...slotStyles?.root }}\n      aria-label={ariaLabel ?? 'Floor plan'}\n    >\n      {showFloorBar && floors.length > 1 && (\n        <div\n          className={cx(styles.roomViewerFloorBar, classNames?.floorBar)}\n          style={slotStyles?.floorBar}\n          role='tablist'\n          aria-label={text.floors}\n        >\n          {/* Top storey first, the way a building reads in section. */}\n          {[...floors].reverse().map((f) => (\n            <button\n              key={f.id}\n              type='button'\n              role='tab'\n              className={cx(\n                styles.roomViewerFloorTab,\n                f.id === floor?.id && styles.roomViewerFloorTabActive,\n                classNames?.floorTab,\n              )}\n              style={slotStyles?.floorTab}\n              aria-selected={f.id === floor?.id}\n              onClick={() => selectFloor(f.id)}\n            >\n              {f.label}\n            </button>\n          ))}\n        </div>\n      )}\n\n      <div\n        ref={wrapperRef}\n        className={cx(\n          styles.roomViewerCanvasWrapper,\n          pannable && styles.roomViewerCanvasWrapperPannable,\n          classNames?.canvasWrapper,\n        )}\n        style={{ height, ...slotStyles?.canvasWrapper }}\n        // Any button pans, the middle one included. Left alone, the browser starts its autoscroll\n        // on a middle press and scrolls the page away underneath the pan.\n        onMouseDown={(e) => {\n          if (pannable && e.button === 1) e.preventDefault();\n        }}\n        onPointerDown={handlePointerDown}\n        onPointerMove={handlePointerMove}\n        onPointerUp={handlePointerUp}\n        onPointerCancel={handlePointerUp}\n      >\n        {isEmpty ? (\n          <div className={cx(styles.roomViewerEmpty, classNames?.empty)} style={slotStyles?.empty}>\n            {emptyState ?? text.empty}\n          </div>\n        ) : (\n          <PlanCanvas\n            ref={svgRef}\n            floor={floor}\n            rooms={rooms}\n            view={currentView}\n            size={size}\n            selection={selection}\n            hovered={hoveredSelection}\n            interactiveRooms={interactive}\n            showGrid={showGrid}\n            showRoomLabels={showRoomLabels}\n            showOpeningSymbols={showOpeningSymbols}\n            getRoomColor={getRoomColor}\n            renderRoomLabel={renderRoomLabel}\n            getRoomLabel={\n              getRoomLabel ?? ((room, { area }) => `${room?.name || text.unnamedRoom}, ${formatArea(area)}`)\n            }\n            formatArea={formatArea}\n            onRoomActivate={interactive ? (roomId) => selectRoom(roomId) : undefined}\n            onBackgroundActivate={interactive ? () => selectRoom(null) : undefined}\n            onHoverChange={interactive ? (target) => setHovered(target?.type === 'room' ? target.id : null) : undefined}\n            classNames={{\n              canvas: classNames?.canvas,\n              grid: classNames?.grid,\n              rooms: classNames?.rooms,\n              roomFill: classNames?.roomFill,\n              roomLabel: classNames?.roomLabel,\n              walls: classNames?.walls,\n              wallBody: classNames?.wallBody,\n              openings: classNames?.openings,\n              opening: classNames?.opening,\n            }}\n            styles={{\n              canvas: slotStyles?.canvas,\n              grid: slotStyles?.grid,\n              rooms: slotStyles?.rooms,\n              roomFill: slotStyles?.roomFill,\n              roomLabel: slotStyles?.roomLabel,\n              walls: slotStyles?.walls,\n              wallBody: slotStyles?.wallBody,\n              openings: slotStyles?.openings,\n              opening: slotStyles?.opening,\n            }}\n            aria-label={ariaLabel ?? 'Floor plan'}\n          />\n        )}\n\n        {showViewControls && !isEmpty && (\n          <div className={cx(styles.roomViewerViewControls, classNames?.viewControls)} style={slotStyles?.viewControls}>\n            <button\n              type='button'\n              className={cx(styles.roomViewerViewButton, classNames?.viewButton)}\n              style={slotStyles?.viewButton}\n              title={text.zoomOut}\n              aria-label={text.zoomOut}\n              onClick={() => zoomBy(1 / 1.2)}\n            >\n              {renderIcon(icons?.zoomOut ?? faMagnifyingGlassMinus)}\n            </button>\n            <button\n              type='button'\n              className={cx(styles.roomViewerViewButton, classNames?.viewButton)}\n              style={slotStyles?.viewButton}\n              title={text.fit}\n              aria-label={text.fit}\n              onClick={() => {\n                userMovedRef.current = false;\n                fitToContent();\n              }}\n            >\n              {renderIcon(icons?.fit ?? faExpand)}\n            </button>\n            <button\n              type='button'\n              className={cx(styles.roomViewerViewButton, classNames?.viewButton)}\n              style={slotStyles?.viewButton}\n              title={text.zoomIn}\n              aria-label={text.zoomIn}\n              onClick={() => zoomBy(1.2)}\n            >\n              {renderIcon(icons?.zoomIn ?? faMagnifyingGlassPlus)}\n            </button>\n          </div>\n        )}\n      </div>\n\n      <div id={liveId} className={styles.roomViewerLive} role='status' aria-live='polite'>\n        {announcement}\n      </div>\n    </div>\n  );\n}\n\nexport default RoomViewer;\n","import type { CSSProperties, ReactNode, Ref } from 'react';\nimport type { HtmlProps } from '../types/slots.types';\n\nexport enum ScrollFadeAxis {\n  Vertical = 'vertical',\n  Horizontal = 'horizontal',\n}\n\nexport interface ScrollFadeProps extends HtmlProps<HTMLDivElement> {\n  /** The scrolling content. */\n  children?: ReactNode;\n  /** Which way it scrolls. Default `ScrollFadeAxis.Vertical`. */\n  axis?: ScrollFadeAxis;\n  /** How far the fade runs from each edge, any CSS length. Default `24px`. */\n  fadeLength?: string;\n  /** Fade both ends whether or not content is hidden there. Default `false`. */\n  isAlwaysFaded?: boolean;\n  className?: string;\n  style?: CSSProperties;\n  /** The scrolling element itself. */\n  ref?: Ref<HTMLDivElement>;\n}\n","/* The mask itself lives in `styles/scrollFade.module.pcss`, shared with every component that has\n   a scroll port of its own. All this adds is the scrolling — hence two classes and no shared base:\n   an empty parent rule is dropped from the module, so `styles.scrollFade` would be undefined. */\n.scrollFade-vertical {\n  overflow-y: auto;\n}\n\n.scrollFade-horizontal {\n  overflow-x: auto;\n}\n","import { useRef, type CSSProperties, type ReactElement } from 'react';\nimport cx from 'classnames';\nimport { useScrollEdges } from 'components/common/hooks/useScrollEdges';\nimport { mergeRefs } from 'components/common/utils/mergeRefs';\n\nimport { ScrollFadeAxis, type ScrollFadeProps } from './scrollFade.types';\nimport styles from './scrollFade.module.pcss';\nimport fadeStyles from 'components/common/styles/scrollFade.module.pcss';\n\nfunction ScrollFade({\n  children = null,\n  axis = ScrollFadeAxis.Vertical,\n  fadeLength,\n  isAlwaysFaded = false,\n  className,\n  style,\n  ref,\n  ...rest\n}: ScrollFadeProps): ReactElement {\n  const scrollRef = useRef<HTMLDivElement>(null);\n  const isHorizontal = axis === ScrollFadeAxis.Horizontal;\n  const { hasStart, hasEnd } = useScrollEdges(scrollRef, {\n    axis,\n    isEnabled: !isAlwaysFaded,\n  });\n\n  return (\n    <div\n      {...rest}\n      ref={mergeRefs(scrollRef, ref)}\n      className={cx(\n        isHorizontal ? styles.scrollFadeHorizontal : styles.scrollFadeVertical,\n        fadeStyles.scrollFade,\n        isHorizontal ? fadeStyles.scrollFadeX : fadeStyles.scrollFadeY,\n        (isAlwaysFaded || hasStart) && fadeStyles.scrollFadeFadedStart,\n        (isAlwaysFaded || hasEnd) && fadeStyles.scrollFadeFadedEnd,\n        className,\n      )}\n      // The shared sheet's private length, not `--scroll-fade-length`: an inline custom property\n      // would otherwise reach a `ScrollFade` nested in the content.\n      style={{\n        ...(fadeLength ? ({ '--custom-scroll-fade-length': fadeLength } as CSSProperties) : undefined),\n        ...style,\n      }}\n    >\n      {children}\n    </div>\n  );\n}\n\nexport default ScrollFade;\n","import type { CSSProperties, ReactNode } from 'react';\nimport type { IconContent } from '../types/icon.types';\nimport type { SlotClassNames, SlotStyles, HtmlProps } from '../types/slots.types';\nimport type { IconAction } from '../types/actions.types';\n\nexport enum SectionHeaderSize {\n  Small = 'small',\n  Medium = 'medium',\n  Large = 'large',\n}\n\nexport type SectionHeaderAction = IconAction;\n\nexport type SectionHeaderSlots =\n  | 'root'\n  | 'identity'\n  | 'leading'\n  | 'titles'\n  | 'title'\n  | 'subtitle'\n  | 'trailing'\n  | 'left'\n  | 'center'\n  | 'actions';\n\nexport interface SectionHeaderProps extends Omit<HtmlProps, 'title'> {\n  title?: ReactNode;\n  subtitle?: ReactNode;\n  icon?: IconContent;\n  thumbnail?: string | ReactNode;\n  left?: ReactNode;\n  center?: ReactNode;\n  actions?: SectionHeaderAction[] | ReactNode;\n  level?: 1 | 2 | 3 | 4 | 5 | 6;\n  size?: SectionHeaderSize;\n  divider?: boolean;\n  className?: string;\n  style?: CSSProperties;\n  classNames?: SlotClassNames<SectionHeaderSlots>;\n  styles?: SlotStyles<SectionHeaderSlots>;\n}\n",".sectionHeader {\n  display: flex;\n  flex-wrap: wrap;\n  gap: var(--spacing-m, 16px);\n  align-items: center;\n  color: var(--text-color);\n\n  &-leading {\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    width: var(--section-header-leading-size);\n    height: var(--section-header-leading-size);\n    color: var(--primary-color);\n    background: var(--background-accent);\n    border-radius: var(--default-border-radius);\n    overflow: hidden;\n    background-size: cover;\n    background-position: center;\n    flex-shrink: 0;\n    font-size: calc(var(--section-header-leading-size) * 0.5);\n  }\n\n  /* Leading icon/thumbnail + titles form one unbreakable flex item. Without the\n     wrapper they're siblings on the wrapping root, and since flex breaks lines by\n     hypothetical size (flex-basis: auto = content width) rather than the shrunk\n     size, a long title drops to its own row and orphans the icon above it. */\n  &-identity {\n    display: flex;\n    align-items: center;\n    gap: var(--spacing-m, 16px);\n    flex: 0 1 auto;\n    min-width: 0;\n  }\n\n  &-titles {\n    display: flex;\n    flex-flow: column;\n    flex: 0 1 auto;\n    gap: 2px;\n    min-width: 0;\n  }\n\n  &-title {\n    margin: 0;\n    font-size: var(--section-header-title-size);\n    font-weight: var(--header-font-weight, 600);\n    line-height: 1.2;\n    white-space: nowrap;\n    overflow: hidden;\n    text-overflow: ellipsis;\n  }\n\n  &-subtitle {\n    color: var(--text-dark);\n    font-weight: 300;\n    font-size: var(--section-header-subtitle-size);\n    display: -webkit-box;\n    -webkit-line-clamp: 2;\n    line-clamp: 2;\n    -webkit-box-orient: vertical;\n    overflow: hidden;\n  }\n\n  &-trailing {\n    display: flex;\n    flex-wrap: wrap;\n    align-items: center;\n    justify-content: space-between;\n    gap: var(--spacing-m, 16px);\n    flex: 1 1 auto;\n  }\n\n  &-left {\n    display: flex;\n    align-items: center;\n    flex-shrink: 0;\n  }\n\n  &-center {\n    display: flex;\n    align-items: center;\n    min-width: 0;\n  }\n\n  &-actions {\n    display: flex;\n    gap: var(--spacing-s, 8px);\n    flex-wrap: wrap;\n    flex-shrink: 0;\n    align-items: center;\n    justify-content: flex-end;\n  }\n\n  &-divider {\n    margin-top: var(--spacing-s, 8px);\n  }\n\n  &-size {\n    &-small {\n      --section-header-title-size: var(--header-small-font-size, 16px);\n      --section-header-subtitle-size: 12px;\n      --section-header-leading-size: calc(var(--section-header-title-size) * 2.2);\n    }\n\n    &-medium {\n      --section-header-title-size: var(--header-medium-font-size, 18px);\n      --section-header-subtitle-size: 14px;\n      --section-header-leading-size: calc(var(--section-header-title-size) * 2.2);\n    }\n\n    &-large {\n      --section-header-title-size: var(--header-font-size, 24px);\n      --section-header-subtitle-size: 15px;\n      --section-header-leading-size: calc(var(--section-header-title-size) * 2.2);\n    }\n  }\n}\n\n@media (width <= 640px) {\n  .sectionHeader-center:not(:empty) {\n    flex: 1 1 100%;\n  }\n}\n","import React from 'react';\nimport type { ReactElement } from 'react';\nimport cx from 'classnames';\n\nimport { SectionHeaderSize } from './sectionHeader.types';\nimport type { SectionHeaderProps, SectionHeaderAction } from './sectionHeader.types';\nimport styles from './sectionHeader.module.pcss';\nimport ActionIcon from '../actionIcon';\nimport Divider from '../divider';\nimport { useComponentDefaults } from '../configProvider';\nimport { renderIcon } from '../utils/renderIcon';\n\nconst CLASS_BY_SIZE: Record<SectionHeaderSize, string> = {\n  [SectionHeaderSize.Small]: styles.sectionHeaderSizeSmall,\n  [SectionHeaderSize.Medium]: styles.sectionHeaderSizeMedium,\n  [SectionHeaderSize.Large]: styles.sectionHeaderSizeLarge,\n};\n\nfunction SectionHeader(props: SectionHeaderProps): ReactElement {\n  const {\n    title,\n    subtitle,\n    icon,\n    thumbnail,\n    left,\n    center,\n    actions,\n    level = 2,\n    size = SectionHeaderSize.Medium,\n    divider = false,\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    ...rest\n  } = useComponentDefaults('SectionHeader', props);\n\n  const thumbnailUrl = typeof thumbnail === 'string' && thumbnail.startsWith('http') ? thumbnail : undefined;\n  const hasThumbnail = thumbnail != null && thumbnail !== '';\n  const hasLeading = hasThumbnail || icon != null;\n\n  const Heading = `h${level}` as keyof React.JSX.IntrinsicElements;\n\n  return (\n    <>\n      <div\n        {...rest}\n        className={cx(styles.sectionHeader, CLASS_BY_SIZE[size], className, classNames?.root)}\n        style={{ ...style, ...slotStyles?.root }}\n      >\n        <div className={cx(styles.sectionHeaderIdentity, classNames?.identity)} style={slotStyles?.identity}>\n          {hasLeading && (\n            <div\n              className={cx(styles.sectionHeaderLeading, classNames?.leading)}\n              style={{\n                ...(thumbnailUrl ? { backgroundImage: `url(${thumbnailUrl})` } : {}),\n                ...slotStyles?.leading,\n              }}\n            >\n              {hasThumbnail ? !thumbnailUrl && thumbnail : icon ? renderIcon(icon) : null}\n            </div>\n          )}\n\n          <div className={cx(styles.sectionHeaderTitles, classNames?.titles)} style={slotStyles?.titles}>\n            <Heading\n              className={cx(styles.sectionHeaderTitle, classNames?.title)}\n              style={slotStyles?.title}\n              // The title renders on a single line and truncates with an ellipsis,\n              // so expose the full text on hover when it's plain text.\n              title={typeof title === 'string' ? title : undefined}\n            >\n              {title}\n            </Heading>\n            {subtitle != null && subtitle !== '' && (\n              <div className={cx(styles.sectionHeaderSubtitle, classNames?.subtitle)} style={slotStyles?.subtitle}>\n                {subtitle}\n              </div>\n            )}\n          </div>\n        </div>\n\n        <div className={cx(styles.sectionHeaderTrailing, classNames?.trailing)} style={slotStyles?.trailing}>\n          <div className={cx(styles.sectionHeaderLeft, classNames?.left)} style={slotStyles?.left}>\n            {left}\n          </div>\n\n          <div className={cx(styles.sectionHeaderCenter, classNames?.center)} style={slotStyles?.center}>\n            {center}\n          </div>\n\n          <div className={cx(styles.sectionHeaderActions, classNames?.actions)} style={slotStyles?.actions}>\n            {Array.isArray(actions)\n              ? (actions as SectionHeaderAction[]).map((action, index) => (\n                  <ActionIcon\n                    key={index}\n                    title={action.title}\n                    onClick={\n                      action.onClick\n                        ? (e: React.MouseEvent<HTMLButtonElement>) => {\n                            e.stopPropagation();\n                            action.onClick?.(e);\n                          }\n                        : undefined\n                    }\n                    icon={action.icon}\n                  />\n                ))\n              : (actions as React.ReactNode)}\n          </div>\n        </div>\n      </div>\n      {divider && <Divider className={styles.sectionHeaderDivider} />}\n    </>\n  );\n}\n\nexport default SectionHeader;\n",".slider {\n  /* Theme tokens resolved once, each with a built-in fallback. Set any of the\n     public `--slider-*` variables via ThemeProvider or `style` to override. */\n\n  /* Not --background-accent-light, which is Card's own surface: on a Card the track vanished. A\n     tint of the text colour shows on any surface. */\n  --custom-slider-track-color: var(--slider-track-color, color-mix(in srgb, currentcolor 20%, transparent));\n  --custom-slider-range-color: var(--slider-range-color, var(--primary-color));\n  --custom-slider-thumb-color: var(--slider-thumb-color, var(--primary-color));\n\n  /* calc, not em: an unregistered custom property substitutes as tokens, so an em would\n     resolve against whichever element uses it, and the mark label reads the thumb size while\n     sitting on the smaller --text-small-font-size. */\n  --custom-slider-thumb-size: var(--slider-thumb-size, calc(var(--default-font-size) * 1.125));\n  --custom-slider-track-height: var(--slider-track-height, calc(var(--default-font-size) * 0.375));\n\n  position: relative;\n  display: flex;\n  align-items: center;\n  box-sizing: border-box;\n\n  /* Fill the container, and allow shrinking when used inside a flex row so the\n     track never collapses to nothing next to a sibling (e.g. a value readout). */\n  width: 100%;\n  min-width: 0;\n  height: var(--custom-slider-thumb-size);\n  padding: 0 calc(var(--custom-slider-thumb-size) / 2);\n  cursor: pointer;\n  -webkit-tap-highlight-color: transparent;\n  touch-action: none;\n  user-select: none;\n\n  &:focus-visible {\n    outline: none;\n  }\n\n  /* Two selectors because focus lands in different places per mode: on the root for a single\n     thumb, on the thumb itself in range mode, where each thumb is its own slider. */\n  &:focus-visible .slider-thumb,\n  & .slider-thumb:focus-visible {\n    outline: none;\n    box-shadow: 0 0 0 0.25em color-mix(in srgb, var(--custom-slider-range-color) 30%, transparent);\n  }\n\n  &-disabled {\n    cursor: not-allowed;\n    opacity: 0.5;\n  }\n\n  /* Reserve room under the track for mark labels. */\n  &-has-mark-labels {\n    margin-bottom: 1.375em;\n  }\n\n  &-track {\n    position: relative;\n    flex: 1;\n    height: var(--custom-slider-track-height);\n    background-color: var(--custom-slider-track-color);\n    border-radius: 999px;\n  }\n\n  &-range {\n    position: absolute;\n    left: 0;\n    top: 0;\n    bottom: 0;\n    background-color: var(--custom-slider-range-color);\n    background-image: var(--primary-gradient, none);\n    border-radius: 999px;\n    transition: width 0.12s ease;\n  }\n\n  &-thumb {\n    position: absolute;\n    top: 50%;\n    width: var(--custom-slider-thumb-size);\n    height: var(--custom-slider-thumb-size);\n    transform: translate(-50%, -50%);\n    background-color: var(--custom-slider-thumb-color);\n    border-radius: 50%;\n    box-shadow: var(--card-shadow);\n    transition:\n      left 0.12s ease,\n      box-shadow 0.15s ease;\n  }\n\n  /* No lag between pointer and thumb/fill while dragging. */\n  &-dragging &-range,\n  &-dragging &-thumb {\n    transition: box-shadow 0.15s ease;\n  }\n\n  &-mark {\n    position: absolute;\n    top: 50%;\n    width: 0.1875em;\n    height: 0.1875em;\n    transform: translate(-50%, -50%);\n    border-radius: 50%;\n    background-color: var(--background);\n    pointer-events: none;\n  }\n\n  &-mark-label {\n    position: absolute;\n    top: calc(50% + var(--custom-slider-thumb-size));\n    transform: translateX(-50%);\n    font-size: var(--text-small-font-size, 12px);\n    color: var(--text-dark);\n    white-space: nowrap;\n    pointer-events: none;\n  }\n\n  /* These em are against this bubble's own --text-small-font-size, not the slider's base, so\n     the ratios differ from everywhere else in this file: 8px and 2px of 12px. */\n  &-value {\n    position: absolute;\n    bottom: calc(100% + 0.6667em);\n    left: 50%;\n    transform: translateX(-50%);\n    padding: 0.1667em 0.6667em;\n    border-radius: var(--default-border-radius);\n    background-color: var(--background-accent-light);\n    color: var(--text-color);\n    font-size: var(--text-small-font-size, 12px);\n    white-space: nowrap;\n    pointer-events: none;\n    font-variant-numeric: tabular-nums;\n  }\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .slider-range,\n  .slider-thumb {\n    transition: none;\n  }\n}\n","import React, { useEffect, useRef, useState, type ReactElement } from 'react';\nimport cx from 'classnames';\nimport styles from './slider.module.pcss';\nimport type { RangeSliderProps, SingleSliderProps, SliderProps } from './slider.types';\n\n/**\n * Every prop of both variants, optional. Destructuring the union directly cannot reach the\n * range-only props, and leaving them in `...rest` spreads them onto the DOM node.\n */\ntype AnySliderProps = Omit<SingleSliderProps, 'onChange' | 'onChangeEnd' | 'value' | 'defaultValue' | 'range'> &\n  Partial<Pick<RangeSliderProps, 'minRange' | 'thumbLabels'>> & {\n    range?: boolean;\n    value?: number | [number, number];\n    defaultValue?: number | [number, number];\n    onChange?: (value: never) => void;\n    onChangeEnd?: (value: never) => void;\n  };\n\n/** Count decimal places in a step so snapped values don't accumulate float error. */\nfunction decimalsOf(step: number): number {\n  const s = String(step);\n  const dot = s.indexOf('.');\n  return dot === -1 ? 0 : s.length - dot - 1;\n}\n\nexport default function Slider(props: SliderProps): ReactElement {\n  const {\n    min = 0,\n    max = 100,\n    step = 1,\n    disabled = false,\n    marks,\n    showValue = false,\n    formatValue,\n    size,\n    tabIndex = 0,\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    'aria-label': ariaLabel,\n    range,\n    value,\n    defaultValue,\n    onChange,\n    onChangeEnd,\n    minRange = 0,\n    thumbLabels,\n    ...rest\n  } = props as AnySliderProps;\n\n  // The union guarantees each handler matches `range`, but that link does not survive\n  // destructuring, so the pair is widened once here rather than cast at every call.\n  const emitChange = onChange as ((v: number | [number, number]) => void) | undefined;\n  const emitEnd = onChangeEnd as ((v: number | [number, number]) => void) | undefined;\n\n  const isRange = range === true;\n\n  const trackRef = useRef<HTMLDivElement>(null);\n  const rootRef = useRef<HTMLDivElement>(null);\n  const thumbRefs = useRef<(HTMLDivElement | null)[]>([]);\n  const draggingIndexRef = useRef<number | null>(null);\n  const [dragging, setDragging] = useState(false);\n\n  const decimals = decimalsOf(step);\n\n  function clamp(v: number): number {\n    return Math.min(max, Math.max(min, v));\n  }\n\n  function snap(v: number): number {\n    const snapped = Math.round((v - min) / step) * step + min;\n    return Number(clamp(snapped).toFixed(decimals));\n  }\n\n  /** Both modes run on an array internally; only the edges of the component know the difference. */\n  function toValues(raw: number | [number, number] | undefined): number[] {\n    if (isRange) {\n      const pair = (raw as [number, number] | undefined) ?? [min, max];\n      return [clamp(pair[0]), clamp(pair[1])];\n    }\n    return [clamp((raw as number | undefined) ?? min)];\n  }\n\n  /** Reshapes a stored pair or single if `range` is flipped after mount. */\n  function fitToMode(values: number[]): number[] {\n    const expected = isRange ? 2 : 1;\n    if (values.length === expected) return values;\n    return isRange ? [values[0], max] : [values[0]];\n  }\n\n  const isControlled = value !== undefined;\n  const [internal, setInternal] = useState<number[]>(() => toValues(defaultValue));\n  const current = isControlled ? toValues(value) : fitToMode(internal);\n\n  // Latest committed values, for reading inside pointer handlers without stale closures. Synced\n  // after render (never written during render) and optimistically in commit() so a drag burst\n  // dedupes before the next render lands.\n  const currentRef = useRef(current);\n  useEffect(() => {\n    currentRef.current = current;\n  }, [current]);\n\n  function payloadOf(next: number[]): number | [number, number] {\n    return isRange ? [next[0], next[1]] : next[0];\n  }\n\n  /** How far this thumb may travel: the track, narrowed by its neighbour and `minRange`. */\n  function boundsFor(index: number, values: number[]): [number, number] {\n    if (!isRange) return [min, max];\n    return index === 0 ? [min, values[1] - minRange] : [values[0] + minRange, max];\n  }\n\n  function commit(index: number, raw: number, ended: boolean) {\n    const previous = currentRef.current;\n    const [lower, upper] = boundsFor(index, previous);\n\n    // Clamped after snapping, and rounded again: a bound derived from a neighbour plus `minRange`\n    // is off the step grid and carries float error (0.3 - 0.1 is 0.19999999999999998).\n    let snapped = snap(raw);\n    if (snapped < lower) snapped = Number(Math.max(min, lower).toFixed(decimals));\n    if (snapped > upper) snapped = Number(Math.min(max, upper).toFixed(decimals));\n\n    // A keyboard step that lands on the same value still ends a gesture, but nothing changed,\n    // so only onChangeEnd fires.\n    if (snapped === previous[index]) {\n      if (ended) emitEnd?.(payloadOf(previous));\n      return;\n    }\n\n    const next = [...previous];\n    next[index] = snapped;\n    currentRef.current = next;\n    if (!isControlled) setInternal(next);\n    emitChange?.(payloadOf(next));\n    if (ended) emitEnd?.(payloadOf(next));\n  }\n\n  function valueFromClientX(clientX: number): number {\n    const track = trackRef.current;\n    if (!track) return currentRef.current[0];\n    const rect = track.getBoundingClientRect();\n    const ratio = rect.width ? (clientX - rect.left) / rect.width : 0;\n    return min + Math.min(1, Math.max(0, ratio)) * (max - min);\n  }\n\n  /**\n   * The thumb closest to the press. On a tie the press direction decides, which is what keeps the\n   * upper thumb reachable when the two sit on the same value and every distance ties.\n   */\n  function nearestThumb(target: number): number {\n    if (!isRange) return 0;\n    const [lo, hi] = currentRef.current;\n    const toLo = Math.abs(target - lo);\n    const toHi = Math.abs(target - hi);\n    if (toLo === toHi) return target > hi ? 1 : 0;\n    return toLo < toHi ? 0 : 1;\n  }\n\n  function handlePointerDown(e: React.PointerEvent<HTMLDivElement>) {\n    if (disabled) return;\n    e.preventDefault();\n    const target = valueFromClientX(e.clientX);\n    const index = nearestThumb(target);\n\n    (isRange ? thumbRefs.current[index] : rootRef.current)?.focus();\n    rootRef.current?.setPointerCapture?.(e.pointerId);\n    draggingIndexRef.current = index;\n    setDragging(true);\n    commit(index, target, false);\n  }\n\n  function handlePointerMove(e: React.PointerEvent<HTMLDivElement>) {\n    const index = draggingIndexRef.current;\n    if (index === null) return;\n    commit(index, valueFromClientX(e.clientX), false);\n  }\n\n  function handlePointerUp(e: React.PointerEvent<HTMLDivElement>) {\n    if (draggingIndexRef.current === null) return;\n    draggingIndexRef.current = null;\n    setDragging(false);\n    rootRef.current?.releasePointerCapture?.(e.pointerId);\n    emitEnd?.(payloadOf(currentRef.current));\n  }\n\n  function handleKeyDown(e: React.KeyboardEvent<HTMLDivElement>, index: number) {\n    if (disabled) return;\n    const page = step * 10;\n    const from = current[index];\n    let next: number | null = null;\n\n    switch (e.key) {\n      case 'ArrowRight':\n      case 'ArrowUp':\n        next = from + step;\n        break;\n      case 'ArrowLeft':\n      case 'ArrowDown':\n        next = from - step;\n        break;\n      case 'PageUp':\n        next = from + page;\n        break;\n      case 'PageDown':\n        next = from - page;\n        break;\n      case 'Home':\n        next = min;\n        break;\n      case 'End':\n        next = max;\n        break;\n      default:\n        return;\n    }\n    e.preventDefault();\n    commit(index, next, true);\n  }\n\n  const fractionOf = (v: number) => (max > min ? (clamp(v) - min) / (max - min) : 0);\n  const percentOf = (v: number) => `${fractionOf(v) * 100}%`;\n\n  const rangeStyle = isRange\n    ? { left: percentOf(current[0]), width: `${(fractionOf(current[1]) - fractionOf(current[0])) * 100}%` }\n    : { width: percentOf(current[0]) };\n\n  const hasMarkLabels = !!marks?.some((m) => m.label != null);\n  const textOf = (v: number) => (formatValue ? formatValue(v) : v);\n\n  function thumbAriaProps(index: number) {\n    const v = current[index];\n    const text = textOf(v);\n    const [lower, upper] = boundsFor(index, current);\n    return {\n      role: 'slider' as const,\n      'aria-valuemin': Number(Math.max(min, lower).toFixed(decimals)),\n      'aria-valuemax': Number(Math.min(max, upper).toFixed(decimals)),\n      'aria-valuenow': v,\n      'aria-valuetext': typeof text === 'string' || typeof text === 'number' ? String(text) : undefined,\n      'aria-orientation': 'horizontal' as const,\n      'aria-disabled': disabled || undefined,\n    };\n  }\n\n  // A single-thumb slider is the slider; a two-thumb one is a group of two, each its own slider.\n  const rootAria = isRange\n    ? { role: 'group' as const, 'aria-label': ariaLabel, 'aria-disabled': disabled || undefined }\n    : { ...thumbAriaProps(0), 'aria-label': ariaLabel, tabIndex: disabled ? -1 : tabIndex };\n\n  return (\n    <div\n      {...rest}\n      {...rootAria}\n      ref={rootRef}\n      className={cx(\n        styles.slider,\n        disabled && styles.sliderDisabled,\n        dragging && styles.sliderDragging,\n        hasMarkLabels && styles.sliderHasMarkLabels,\n        className,\n        classNames?.root,\n      )}\n      style={\n        { ...(size ? { '--slider-thumb-size': size } : null), ...style, ...slotStyles?.root } as React.CSSProperties\n      }\n      onPointerDown={handlePointerDown}\n      onPointerMove={handlePointerMove}\n      onPointerUp={handlePointerUp}\n      onKeyDown={isRange ? undefined : (e) => handleKeyDown(e, 0)}\n    >\n      <div ref={trackRef} className={cx(styles.sliderTrack, classNames?.track)} style={slotStyles?.track}>\n        <div className={cx(styles.sliderRange, classNames?.range)} style={{ ...rangeStyle, ...slotStyles?.range }} />\n        {marks?.map((mark) => {\n          const left = percentOf(mark.value);\n          return (\n            <React.Fragment key={mark.value}>\n              <span className={cx(styles.sliderMark, classNames?.mark)} style={{ left, ...slotStyles?.mark }} />\n              {mark.label != null && (\n                <span\n                  className={cx(styles.sliderMarkLabel, classNames?.markLabel)}\n                  style={{ left, ...slotStyles?.markLabel }}\n                >\n                  {mark.label}\n                </span>\n              )}\n            </React.Fragment>\n          );\n        })}\n        {current.map((v, index) => (\n          <div\n            key={index}\n            ref={(el) => {\n              thumbRefs.current[index] = el;\n            }}\n            className={cx(styles.sliderThumb, classNames?.thumb)}\n            style={{ left: percentOf(v), ...slotStyles?.thumb }}\n            {...(isRange\n              ? {\n                  ...thumbAriaProps(index),\n                  'aria-label': thumbLabels?.[index] ?? (index === 0 ? 'From' : 'To'),\n                  tabIndex: disabled ? -1 : tabIndex,\n                  onKeyDown: (e: React.KeyboardEvent<HTMLDivElement>) => handleKeyDown(e, index),\n                }\n              : {})}\n          >\n            {showValue && (\n              <span className={cx(styles.sliderValue, classNames?.value)} style={slotStyles?.value}>\n                {textOf(v)}\n              </span>\n            )}\n          </div>\n        ))}\n      </div>\n    </div>\n  );\n}\n",".splitButton {\n  display: inline-flex;\n}\n\n/* The class is doubled to boost specificity above Button's own .button rule (both are plain\n   single classes, so specificity would otherwise tie and the outcome would depend on which\n   CSS module happens to load first in the bundle). This guarantees the flattened corner always\n   wins regardless of load order. */\n.splitButtonPrimary.splitButtonPrimary {\n  border-top-right-radius: 0;\n  border-bottom-right-radius: 0;\n}\n\n.splitButtonTrigger.splitButtonTrigger {\n  border-top-left-radius: 0;\n  border-bottom-left-radius: 0;\n  margin-left: -1px;\n}\n","import { useState } from 'react';\nimport cx from 'classnames';\nimport { faChevronDown } from '@fortawesome/free-solid-svg-icons';\n\nimport Button from '../button';\nimport { ButtonStyleType, ButtonSize } from '../button/button.types';\nimport ButtonGroup from '../buttonGroup';\nimport Menu from '../menu';\nimport { PopoverAlign, PopoverPlacement } from '../popover/popover.types';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport type { SplitButtonLabels, SplitButtonProps } from './splitButton.types';\n\nimport styles from './splitButton.module.pcss';\nimport { ThemeColor } from '../themeProvider/color.types';\n\nconst DEFAULT_LABELS: Required<SplitButtonLabels> = {\n  moreActions: 'More actions',\n};\n\nfunction SplitButton(props: SplitButtonProps) {\n  const {\n    children,\n    onClick = () => {},\n    actions = [],\n    styleType = ButtonStyleType.Filled,\n    color = ThemeColor.Primary,\n    size = ButtonSize.Medium,\n    disabled = false,\n    isLoading = false,\n    align = PopoverAlign.End,\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    labels,\n    ...rest\n  } = useComponentDefaults('SplitButton', props);\n  const text = useLabels('SplitButton', labels, DEFAULT_LABELS);\n\n  const [isOpen, setIsOpen] = useState(false);\n\n  // Adjusting state during render (not in an effect) on a prop change is the documented React\n  // pattern for this: closing the menu as soon as `disabled` becomes true, without an extra\n  // render tick or a setState-in-effect lint violation.\n  const [prevDisabled, setPrevDisabled] = useState(disabled);\n  if (disabled !== prevDisabled) {\n    setPrevDisabled(disabled);\n    if (disabled) setIsOpen(false);\n  }\n\n  return (\n    <ButtonGroup\n      {...rest}\n      className={cx(styles.splitButton, classNames?.root, className)}\n      style={{ ...style, ...slotStyles?.root }}\n    >\n      <Button\n        styleType={styleType}\n        color={color}\n        size={size}\n        disabled={disabled}\n        isLoading={isLoading}\n        onClick={onClick}\n        className={cx(styles.splitButtonPrimary, classNames?.primaryButton)}\n        style={slotStyles?.primaryButton}\n      >\n        {children}\n      </Button>\n\n      {/* The list, its keys, its focus and its menu semantics are Menu's; this\n          component only welds a second button to the first. */}\n      <Menu\n        items={actions}\n        isOpen={isOpen}\n        onOpenChange={setIsOpen}\n        placement={PopoverPlacement.Bottom}\n        align={align}\n        classNames={{ menu: classNames?.menu, item: classNames?.menuItem }}\n        styles={{ menu: slotStyles?.menu, item: slotStyles?.menuItem }}\n      >\n        <Button\n          styleType={styleType}\n          color={color}\n          size={size}\n          disabled={disabled}\n          icon={faChevronDown}\n          aria-label={text.moreActions}\n          className={cx(styles.splitButtonTrigger, classNames?.triggerButton)}\n          style={slotStyles?.triggerButton}\n        />\n      </Menu>\n    </ButtonGroup>\n  );\n}\n\nexport default SplitButton;\n",".stepper {\n  /* Shape tokens resolved once, each with a built-in fallback. Set any of the public\n     `--stepper-*` variables via ThemeProvider (theme-wide) or `style` (per instance) to\n     override. The colours come from `color`, through the --tone properties. */\n  --custom-stepper-marker-size: var(--stepper-marker-size, 40px);\n  --custom-stepper-marker-radius: var(--stepper-marker-radius, var(--default-border-radius));\n  --custom-stepper-connector-thickness: var(--stepper-connector-thickness, 3px);\n  --custom-stepper-connector-idle-thickness: var(--stepper-connector-idle-thickness, 1px);\n  --custom-stepper-gap: var(--stepper-gap, 12px);\n\n  display: flex;\n  margin: 0;\n  padding: 0;\n  list-style: none;\n  color: var(--text-color);\n\n  /* Keeps a horizontal connector's z-index: -1 inside the stepper, rather than behind whatever\n     the stepper sits on. */\n  isolation: isolate;\n\n  &-vertical {\n    flex-direction: column;\n  }\n\n  &-step {\n    position: relative;\n  }\n\n  /* Entry: the marker and its labels. A <button> when clickable, so it resets one. */\n  &-entry {\n    display: flex;\n    gap: var(--custom-stepper-gap);\n    box-sizing: border-box;\n    margin: 0;\n    padding: 0;\n    border: 0;\n    background: none;\n    color: inherit;\n    font: inherit;\n    text-align: inherit;\n    user-select: none;\n  }\n\n  &-clickable {\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n  }\n\n  /* Not on -clickable: the current step is a focusable button too, disabled through ARIA. */\n  &-entry:focus-visible {\n    outline: 2px solid var(--focus-border-color, var(--primary-color));\n    outline-offset: 4px;\n    border-radius: var(--default-border-radius);\n  }\n\n  &-entry-disabled {\n    opacity: 0.5;\n  }\n\n  /* Marker --------------------------------------------------------------- */\n  &-entry-number {\n    width: var(--custom-stepper-marker-size);\n    height: var(--custom-stepper-marker-size);\n    box-sizing: border-box;\n    flex-shrink: 0;\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    border: 1px solid var(--border-color);\n    color: var(--text-dark);\n    border-radius: var(--custom-stepper-marker-radius);\n    font-weight: 600;\n    font-size: calc(var(--custom-stepper-marker-size) * 0.4);\n    box-shadow: inset 0 0 0 0 var(--tone-fill);\n    transition:\n      border-color 0.4s,\n      color 0.4s,\n      background-color 0.4s,\n      box-shadow 0.4s,\n      transform 0.4s;\n  }\n\n  &-entry-reached &-entry-number {\n    border: 2px solid var(--tone);\n    color: var(--tone-text);\n    transform: scale3d(1.1, 1.1, 1);\n  }\n\n  /* Fills from the border inward, an inset shadow grown past the centre. 0.75 of the marker is\n     the old 30px at the default 40px, so the fill closes before the transition ends. */\n  &-entry-done &-entry-number {\n    border-color: var(--tone-fill);\n    color: var(--tone-contrast);\n    background-color: var(--tone-fill);\n    box-shadow: inset 0 0 0 calc(var(--custom-stepper-marker-size) * 0.75) var(--tone-fill);\n    transform: none;\n  }\n\n  /* The marker carries the error tone class itself, so --tone is the error colour here. */\n  &-entry-error &-entry-number {\n    border: 2px solid var(--tone);\n    color: var(--tone-text);\n    transform: none;\n  }\n\n  /* 1.25em of the marker's font-size, which is 0.4 of the marker: half the marker. */\n  &-checkmark {\n    width: 1.25em;\n    height: 1.25em;\n  }\n\n  &-checkmark path {\n    stroke: currentcolor;\n    stroke-dasharray: 5;\n    stroke-dashoffset: 5;\n    animation: showCheckmark 0.4s 0.2s forwards;\n  }\n\n  /* Labels --------------------------------------------------------------- */\n  &-entry-labels {\n    display: flex;\n    flex-direction: column;\n    gap: 2px;\n    min-width: 0;\n    align-self: center;\n    overflow-wrap: anywhere;\n  }\n\n  &-entry-label {\n    color: var(--text-color);\n  }\n\n  &-description {\n    color: var(--text-dark);\n    font-size: var(--text-small-font-size, 0.85em);\n  }\n\n  &-optional {\n    color: var(--text-light);\n    font-size: var(--text-small-font-size, 0.85em);\n    font-style: italic;\n  }\n\n  &-visually-hidden {\n    position: absolute;\n    width: 1px;\n    height: 1px;\n    margin: -1px;\n    padding: 0;\n    overflow: hidden;\n    clip: rect(0, 0, 0, 0);\n    white-space: nowrap;\n    border: 0;\n  }\n\n  /* Connector ------------------------------------------------------------ */\n  &-connector {\n    height: var(--custom-stepper-connector-idle-thickness);\n    background-size: 200% 100%;\n    background-image: linear-gradient(90deg, var(--tone) 0 50%, var(--border-color) 50% 100%);\n    background-position: 100% 0;\n    transition:\n      background-position 0.6s,\n      height 0.4s,\n      width 0.4s;\n\n    &-filled {\n      height: var(--custom-stepper-connector-thickness);\n      background-position: 0 0;\n    }\n  }\n\n  /* Horizontal ------------------------------------------------------------ */\n  &:not(.stepper-vertical) {\n    align-items: flex-start;\n  }\n\n  &-label-top:not(.stepper-vertical) {\n    align-items: flex-end;\n  }\n\n  /* Equal columns, so a label wraps within its step's share of the width instead of spilling\n     past the stepper's edge or into the next label. */\n  &:not(.stepper-vertical) &-step {\n    flex: 1 1 0;\n    min-width: 0;\n    display: flex;\n    justify-content: center;\n  }\n\n  &:not(.stepper-vertical) &-entry {\n    flex-direction: column;\n    align-items: center;\n    min-width: 0;\n    gap: calc(var(--custom-stepper-gap) / 2);\n    text-align: center;\n  }\n\n  &-label-top:not(.stepper-vertical) &-entry {\n    flex-direction: column-reverse;\n  }\n\n  /* From this marker's edge to the next one's: the steps are equal columns, so the next marker's\n     centre is 100% further along. Under the markers (z-index), because the current one's\n     scale(1.1) reaches over the connector's ends. */\n  &:not(.stepper-vertical) &-connector {\n    position: absolute;\n    z-index: -1;\n    top: calc(var(--custom-stepper-marker-size) / 2);\n    left: calc(50% + var(--custom-stepper-marker-size) / 2);\n    right: calc(var(--custom-stepper-marker-size) / 2 - 50%);\n    translate: 0 -50%;\n  }\n\n  &-label-top:not(.stepper-vertical) &-connector {\n    top: auto;\n    bottom: calc(var(--custom-stepper-marker-size) / 2);\n    translate: 0 50%;\n  }\n\n  /* Vertical -------------------------------------------------------------- */\n  &-vertical &-step {\n    display: grid;\n    grid-template-columns: var(--custom-stepper-marker-size) 1fr;\n    column-gap: var(--custom-stepper-gap);\n  }\n\n  &-vertical &-entry {\n    grid-column: 1 / -1;\n    align-items: stretch;\n  }\n\n  &-marker-cell {\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    flex-shrink: 0;\n    width: var(--custom-stepper-marker-size);\n  }\n\n  &-rail-segment {\n    flex: 1 1 0;\n    min-height: 0;\n    width: var(--custom-stepper-connector-idle-thickness);\n    background-size: 100% 200%;\n    background-image: linear-gradient(180deg, var(--tone) 0 50%, var(--border-color) 50% 100%);\n    background-position: 0 100%;\n    transition:\n      background-position 0.4s,\n      width 0.4s;\n  }\n\n  &-rail-filled {\n    width: var(--custom-stepper-connector-thickness);\n    background-position: 0 0;\n  }\n\n  &-rail-hidden {\n    background-image: none;\n    background-color: transparent;\n  }\n\n  &-vertical &-connector {\n    grid-column: 1;\n    justify-self: center;\n    width: var(--custom-stepper-connector-idle-thickness);\n    height: 100%;\n    min-height: 24px;\n    background-size: 100% 200%;\n    background-image: linear-gradient(180deg, var(--tone) 0 50%, var(--border-color) 50% 100%);\n    background-position: 0 100%;\n    transition:\n      background-position 0.4s,\n      width 0.4s;\n\n    &-filled {\n      width: var(--custom-stepper-connector-thickness);\n      background-position: 0 0;\n    }\n  }\n\n  &-vertical &-content {\n    grid-column: 2;\n  }\n\n  &-vertical.stepper-label-left &-step {\n    grid-template-columns: 1fr var(--custom-stepper-marker-size);\n  }\n\n  &-vertical.stepper-label-left &-entry {\n    flex-direction: row-reverse;\n  }\n\n  &-vertical.stepper-label-left &-connector {\n    grid-column: 2;\n  }\n\n  &-vertical.stepper-label-left &-content {\n    grid-column: 1;\n    text-align: right;\n  }\n\n  &-content {\n    overflow: hidden;\n    transition: height 0.25s ease;\n  }\n\n  &-content-inner {\n    padding: 4px 0 12px;\n  }\n\n  /* Style types ------------------------------------------------------------ */\n  &-outlined &-entry-done &-entry-number {\n    border-color: var(--tone);\n    color: var(--tone-text);\n    background-color: transparent;\n    box-shadow: none;\n  }\n\n  &-dots {\n    --stepper-marker-size: 16px;\n    --stepper-marker-radius: 50%;\n    --stepper-gap: 10px;\n\n    & .stepper-entry-number {\n      font-size: 0;\n      border-width: 2px;\n    }\n\n    /* The dots variant shows no marker content at all: the number is killed by\n       font-size: 0 above, and every element child (the checkmark, a FontAwesome\n       icon, a custom icon element) is hidden here — matching on `svg` alone would\n       miss a custom icon that isn't one. */\n    & .stepper-entry-number > * {\n      display: none;\n    }\n  }\n}\n\n@keyframes showCheckmark {\n  to {\n    stroke-dashoffset: 0;\n  }\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .stepper-content,\n  .stepper-entry-number,\n  .stepper-connector,\n  .stepper-rail-segment {\n    transition: none;\n  }\n\n  .stepper-checkmark path {\n    animation: none;\n    stroke-dashoffset: 0;\n  }\n}\n","import type React from 'react';\nimport type { IconContent } from '../types/icon.types';\nimport type { SlotClassNames, SlotStyles, HtmlProps } from '../types/slots.types';\nimport type { ColorValue } from '../themeProvider/color.types';\n\n/** Direction the steps run in. */\nexport enum StepperOrientation {\n  Horizontal = 'horizontal',\n  Vertical = 'vertical',\n}\n\n/** How the step markers are drawn. */\nexport enum StepperStyleType {\n  /** Numbered markers, filled solid on completion. The default. */\n  Filled = 'filled',\n  /** Numbered markers that stay ringed when completed, with a check in the colour. */\n  Outlined = 'outlined',\n  /** Minimal dots without numbers, for a compact onboarding or carousel style. */\n  Dots = 'dots',\n}\n\n/**\n * Which step bodies (`step.content`) are expanded. Only affects vertical\n * steppers with per-step `content`; ignored otherwise.\n */\nexport enum StepperCollapse {\n  /** Every step body stays expanded. */\n  None = 'none',\n  /** Only the current step's body is expanded (classic vertical wizard). */\n  ActiveOnly = 'activeOnly',\n  /** Completed (past) step bodies collapse; current and upcoming stay expanded. */\n  Completed = 'completed',\n}\n\n/**\n * Where the label block sits relative to the marker. Horizontal steppers use\n * `Top`/`Bottom`; vertical steppers use `Left`/`Right`. An out-of-orientation\n * value falls back to that orientation's default.\n */\nexport enum StepperLabelPlacement {\n  Top = 'top',\n  Bottom = 'bottom',\n  Left = 'left',\n  Right = 'right',\n}\n\nexport type StepperSlots =\n  | 'root'\n  | 'entry'\n  | 'entryNumber'\n  | 'entryLabel'\n  | 'connector'\n  | 'description'\n  | 'content'\n  | 'optional'\n  | 'icon';\n\nexport interface StepperLabels {\n  /** Hint under the label of a step with `optional: true`. */\n  optional?: string;\n  /** Status of a completed step, read out by screen readers. */\n  completed?: string;\n  /** Status of a step not reached yet, read out by screen readers. */\n  notCompleted?: string;\n  /** Status of a step with `error`, read out by screen readers. */\n  error?: string;\n}\n\nexport interface StepperStep {\n  /** Primary label for the step. */\n  label: React.ReactNode;\n  /** Secondary line shown under the label. */\n  description?: React.ReactNode;\n  /** Collapsible body content, rendered under the label (vertical only). */\n  content?: React.ReactNode;\n  /** Icon shown in the marker instead of the step number — a FontAwesome icon or any node. */\n  icon?: IconContent;\n  /** Marks the step as errored, drawing its marker in the theme's `--error-color`. */\n  error?: boolean;\n  /** `true` renders the `optional` label as a hint; a node renders custom hint text. */\n  optional?: boolean | React.ReactNode;\n  /** Disables this step: never clickable, dimmed. */\n  disabled?: boolean;\n}\n\nexport interface StepperProps extends HtmlProps<HTMLOListElement> {\n  steps?: StepperStep[];\n  /** Zero-based index of the current step. `steps.length` marks every step completed. */\n  currentStep?: number;\n  /** Direction the steps run in (default `Horizontal`). */\n  orientation?: StepperOrientation;\n  onStepClicked?: ((index: number) => void) | null;\n  /** Allow clicking any step, not just completed ones. */\n  nonLinear?: boolean;\n  /** Hide each step's label, description and hint visually. Screen readers still read them. */\n  hideLabels?: boolean;\n  /** How the markers are drawn (default `Filled`). */\n  styleType?: StepperStyleType;\n  /** A `ThemeColor` (default `Primary`) or any CSS colour, such as `'var(--brand)'`. */\n  color?: ColorValue;\n  /** Which step bodies are expanded. Vertical `content` only (default `None`). */\n  collapse?: StepperCollapse;\n  /** Label position relative to the marker (default: horizontal `Bottom`, vertical `Right`). */\n  labelPlacement?: StepperLabelPlacement;\n  labels?: StepperLabels;\n  className?: string;\n  style?: React.CSSProperties;\n  classNames?: SlotClassNames<StepperSlots>;\n  styles?: SlotStyles<StepperSlots>;\n}\n","import React, { useEffect, useRef, useState } from 'react';\nimport cx from 'classnames';\nimport styles from './stepper.module.pcss';\nimport {\n  StepperCollapse,\n  StepperLabelPlacement,\n  StepperOrientation,\n  StepperStyleType,\n  type StepperLabels,\n  type StepperProps,\n  type StepperStep,\n} from './stepper.types';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport { ThemeColor } from '../themeProvider/color.types';\nimport { toneProps, useToneCheck } from '../styles/tone';\nimport { renderIcon } from '../utils/renderIcon';\n\nconst STYLE_CLASS: Record<StepperStyleType, string> = {\n  [StepperStyleType.Filled]: styles.stepperFilled,\n  [StepperStyleType.Outlined]: styles.stepperOutlined,\n  [StepperStyleType.Dots]: styles.stepperDots,\n};\n\nconst PLACEMENT_CLASS: Record<StepperLabelPlacement, string> = {\n  [StepperLabelPlacement.Top]: styles.stepperLabelTop,\n  [StepperLabelPlacement.Bottom]: styles.stepperLabelBottom,\n  [StepperLabelPlacement.Left]: styles.stepperLabelLeft,\n  [StepperLabelPlacement.Right]: styles.stepperLabelRight,\n};\n\nconst DEFAULT_LABELS: Required<StepperLabels> = {\n  optional: 'Optional',\n  completed: 'Completed',\n  notCompleted: 'Not completed',\n  error: 'Error',\n};\n\nconst ERROR_TONE = toneProps(ThemeColor.Error);\n\nfunction Checkmark() {\n  return (\n    <svg\n      className={styles.stepperCheckmark}\n      strokeLinejoin='round'\n      strokeLinecap='round'\n      xmlns='http://www.w3.org/2000/svg'\n      viewBox='-0.2 -1.2 3.4 2'\n    >\n      <path d='M 0 0 l 1 1 l 2 -2' strokeWidth='0.4' fill='none' />\n    </svg>\n  );\n}\n\nfunction StepBody({\n  open,\n  className,\n  style,\n  children,\n}: {\n  open: boolean;\n  className?: string;\n  style?: React.CSSProperties;\n  children: React.ReactNode;\n}) {\n  const innerRef = useRef<HTMLDivElement>(null);\n  const [height, setHeight] = useState<number | null>(null);\n\n  useEffect(() => {\n    const el = innerRef.current;\n    if (!el) return;\n    const observer = new ResizeObserver(() => setHeight(el.offsetHeight));\n    observer.observe(el);\n    return () => observer.disconnect();\n  }, []);\n\n  return (\n    <div\n      className={cx(styles.stepperContent, className)}\n      style={{ ...style, height: open ? (height ?? 'auto') : 0 }}\n      inert={!open}\n    >\n      <div ref={innerRef} className={styles.stepperContentInner}>\n        {children}\n      </div>\n    </div>\n  );\n}\n\nfunction resolvePlacement(placement: StepperLabelPlacement | undefined, isVertical: boolean): StepperLabelPlacement {\n  const verticalValues = [StepperLabelPlacement.Left, StepperLabelPlacement.Right];\n  if (isVertical) {\n    return placement && verticalValues.includes(placement) ? placement : StepperLabelPlacement.Right;\n  }\n  return placement && !verticalValues.includes(placement) ? placement : StepperLabelPlacement.Bottom;\n}\n\nexport default function Stepper(props: StepperProps) {\n  const {\n    steps = [],\n    currentStep = 0,\n    orientation = StepperOrientation.Horizontal,\n    onStepClicked = null,\n    nonLinear = false,\n    hideLabels = false,\n    styleType = StepperStyleType.Filled,\n    color = ThemeColor.Primary,\n    collapse = StepperCollapse.None,\n    labelPlacement,\n    labels,\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    ...rest\n  } = useComponentDefaults('Stepper', props);\n  const text = useLabels('Stepper', labels, DEFAULT_LABELS);\n  const tone = toneProps(color);\n  useToneCheck('Stepper', [color]);\n  const isVertical = orientation === StepperOrientation.Vertical;\n  const placement = resolvePlacement(labelPlacement, isVertical);\n\n  return (\n    <ol\n      {...rest}\n      // WebKit drops the list role from a list styled `list-style: none`.\n      role='list'\n      className={cx(\n        styles.stepper,\n        tone.className,\n        STYLE_CLASS[styleType],\n        PLACEMENT_CLASS[placement],\n        { [styles.stepperVertical]: isVertical },\n        classNames?.root,\n        className,\n      )}\n      style={{ ...tone.style, ...slotStyles?.root, ...style }}\n    >\n      {steps.map((step: StepperStep, index: number) => {\n        const isCurrent = index === currentStep;\n        const isDone = index < currentStep;\n        const isReached = index <= currentStep;\n        const hasError = !!step.error;\n        // nonLinear: every enabled step except the current one is clickable.\n        // linear: only completed steps are clickable.\n        const isClickable = !!onStepClicked && !step.disabled && (nonLinear ? !isCurrent : isDone);\n        // The current step stays a button, aria-disabled rather than `disabled`: the step just\n        // activated becomes current, and swapping or disabling its button drops focus to the body.\n        const isButton = isClickable || (!!onStepClicked && !step.disabled && isCurrent);\n        const status = hasError ? text.error : isDone ? text.completed : isCurrent ? null : text.notCompleted;\n\n        const markerContent = step.icon ? (\n          renderIcon(step.icon, { className: classNames?.icon, style: slotStyles?.icon })\n        ) : isDone && !hasError ? (\n          <Checkmark />\n        ) : (\n          index + 1\n        );\n\n        // Hidden from screen readers: the list already gives the position, and the status text\n        // says what the checkmark shows.\n        const marker = (\n          <span\n            aria-hidden\n            className={cx(styles.stepperEntryNumber, hasError && ERROR_TONE.className, classNames?.entryNumber)}\n            style={slotStyles?.entryNumber}\n          >\n            {markerContent}\n          </span>\n        );\n\n        const optionalNode = step.optional === true ? text.optional : step.optional !== false ? step.optional : null;\n\n        // The {' '}s keep the parts apart in a button's accessible name (\"Account Completed\", not\n        // \"AccountCompleted\"). A flex container does not render white space, so the layout is unchanged.\n        const labelBlock = (\n          <span className={cx(styles.stepperEntryLabels, { [styles.stepperVisuallyHidden]: hideLabels })}>\n            <span className={cx(styles.stepperEntryLabel, classNames?.entryLabel)} style={slotStyles?.entryLabel}>\n              {step.label}\n            </span>\n            {step.description != null && (\n              <>\n                {' '}\n                <span\n                  className={cx(styles.stepperDescription, classNames?.description)}\n                  style={slotStyles?.description}\n                >\n                  {step.description}\n                </span>\n              </>\n            )}\n            {optionalNode != null && (\n              <>\n                {' '}\n                <span className={cx(styles.stepperOptional, classNames?.optional)} style={slotStyles?.optional}>\n                  {optionalNode}\n                </span>\n              </>\n            )}\n            {status != null && (\n              <>\n                {' '}\n                <span className={styles.stepperVisuallyHidden}>{status}</span>\n              </>\n            )}\n          </span>\n        );\n\n        const railSegment = (variant: 'lead' | 'trail') => {\n          const hidden = variant === 'lead' ? index === 0 : index === steps.length - 1;\n          const filled = variant === 'lead' ? isReached : isDone;\n          return (\n            <span\n              className={cx(styles.stepperRailSegment, {\n                [styles.stepperRailFilled]: !hidden && filled,\n                [styles.stepperRailHidden]: hidden,\n              })}\n            />\n          );\n        };\n\n        const entryContent = (\n          <>\n            {isVertical ? (\n              <span className={styles.stepperMarkerCell}>\n                {railSegment('lead')}\n                {marker}\n                {railSegment('trail')}\n              </span>\n            ) : (\n              marker\n            )}\n            {labelBlock}\n          </>\n        );\n\n        const entryClassName = cx(\n          styles.stepperEntry,\n          { [styles.stepperEntryReached]: isReached },\n          { [styles.stepperEntryDone]: isDone && !hasError },\n          { [styles.stepperEntryError]: hasError },\n          { [styles.stepperEntryDisabled]: step.disabled },\n          { [styles.stepperClickable]: isClickable },\n          classNames?.entry,\n        );\n\n        return (\n          <li key={index} className={styles.stepperStep} aria-current={isCurrent && !isButton ? 'step' : undefined}>\n            {isButton ? (\n              <button\n                type='button'\n                className={entryClassName}\n                style={slotStyles?.entry}\n                aria-current={isCurrent ? 'step' : undefined}\n                aria-disabled={!isClickable || undefined}\n                onClick={isClickable ? () => onStepClicked?.(index) : undefined}\n              >\n                {entryContent}\n              </button>\n            ) : (\n              <div className={entryClassName} style={slotStyles?.entry}>\n                {entryContent}\n              </div>\n            )}\n            {index < steps.length - 1 && (\n              <div\n                className={cx(\n                  styles.stepperConnector,\n                  { [styles.stepperConnectorFilled]: isDone },\n                  classNames?.connector,\n                )}\n                style={slotStyles?.connector}\n              />\n            )}\n            {isVertical && step.content != null && (\n              <StepBody\n                open={\n                  collapse === StepperCollapse.None || (collapse === StepperCollapse.ActiveOnly ? isCurrent : !isDone)\n                }\n                className={classNames?.content}\n                style={slotStyles?.content}\n              >\n                {step.content}\n              </StepBody>\n            )}\n          </li>\n        );\n      })}\n    </ol>\n  );\n}\n","/**\n * Coordinates every mounted `Sticky` so ones that overlap horizontally stack\n * underneath each other (in DOM order) instead of pinning to the same spot.\n * Non-overlapping ones (e.g. side-by-side halves under a full-width one) land\n * at the same top instead of being pushed down by each other.\n */\n\nexport interface StickyStackMetrics {\n  /** Current (unstuck-flow) top of the root element, from getBoundingClientRect. */\n  top: number;\n  left: number;\n  width: number;\n  contentHeight: number;\n  /** The element this entry sticks relative to (its scroll parent), or null for the viewport.\n   *  Stacking only considers overlap between entries sharing the same container — two Stickies\n   *  in unrelated scroll panels should never push each other down just because they happen to\n   *  share horizontal DOM coordinates. */\n  container: HTMLElement | null;\n  /** The element the entry is confined to, or null when nothing confines it.\n   *  Entries sharing one leave together. */\n  boundary: HTMLElement | null;\n  /** Bottom edge of that element (less its inset), in the same coordinate space as `top`. */\n  boundaryBottom: number | null;\n}\n\nexport interface StickyStackEntry {\n  node: HTMLElement;\n  offsetTop: number;\n  disabled: boolean;\n  getMetrics: () => StickyStackMetrics | null;\n}\n\nexport interface StickyStackResult {\n  isStuck: boolean;\n  /** Effective `top` to pin at while stuck — offsetTop plus the height of any stuck, overlapping entries above it. */\n  top: number;\n}\n\ntype ResultListener = (result: StickyStackResult) => void;\n\nfunction rangesOverlap(aLeft: number, aRight: number, bLeft: number, bRight: number): boolean {\n  return aLeft < bRight && bLeft < aRight;\n}\n\nfunction compareDomOrder(a: HTMLElement, b: HTMLElement): number {\n  if (a === b) return 0;\n  const position = a.compareDocumentPosition(b);\n  if (position & Node.DOCUMENT_POSITION_FOLLOWING) return -1;\n  if (position & Node.DOCUMENT_POSITION_PRECEDING) return 1;\n  return 0;\n}\n\nclass StickyStack {\n  private entries = new Map<symbol, StickyStackEntry>();\n  private listeners = new Map<symbol, ResultListener>();\n  private results = new Map<symbol, StickyStackResult>();\n  private rafId = 0;\n  private listenerCount = 0;\n\n  schedule = (): void => {\n    cancelAnimationFrame(this.rafId);\n    this.rafId = requestAnimationFrame(this.recompute);\n  };\n\n  register(entry: StickyStackEntry, onChange: ResultListener): symbol {\n    const id = Symbol('sticky');\n    this.entries.set(id, entry);\n    this.listeners.set(id, onChange);\n    this.startListening();\n    return id;\n  }\n\n  updateEntry(id: symbol, patch: Partial<Pick<StickyStackEntry, 'offsetTop' | 'disabled'>>): void {\n    const entry = this.entries.get(id);\n    if (entry) Object.assign(entry, patch);\n  }\n\n  unregister(id: symbol): void {\n    if (!this.entries.has(id)) return;\n    this.entries.delete(id);\n    this.listeners.delete(id);\n    this.results.delete(id);\n    this.stopListening();\n  }\n\n  recompute = (): void => {\n    const ordered = [...this.entries.entries()].sort(([, a], [, b]) => compareDomOrder(a.node, b.node));\n    const placedByContainer = new Map<HTMLElement | null, { left: number; right: number; bottom: number }[]>();\n    const rows: { id: symbol; metrics: StickyStackMetrics; baseTop: number; isStuck: boolean }[] = [];\n\n    for (const [id, entry] of ordered) {\n      const metrics = entry.getMetrics();\n      if (!metrics) continue;\n\n      let placed = placedByContainer.get(metrics.container);\n      if (!placed) {\n        placed = [];\n        placedByContainer.set(metrics.container, placed);\n      }\n\n      const overlapping = placed.filter((p) =>\n        rangesOverlap(p.left, p.right, metrics.left, metrics.left + metrics.width),\n      );\n      const baseTop = overlapping.reduce((max, p) => Math.max(max, p.bottom), entry.offsetTop);\n      const isStuck = !entry.disabled && metrics.top <= baseTop;\n\n      if (isStuck) {\n        // Never hold space below the boundary this entry is confined to: the\n        // shared shift below has pulled it up to that edge, and an entry under it\n        // must stack on where it sits, not on where it would sit unconfined.\n        const bottom = baseTop + metrics.contentHeight;\n        placed.push({\n          left: metrics.left,\n          right: metrics.left + metrics.width,\n          bottom: metrics.boundaryBottom == null ? bottom : Math.min(bottom, metrics.boundaryBottom),\n        });\n      }\n      rows.push({ id, metrics, baseTop: isStuck ? baseTop : entry.offsetTop, isStuck });\n    }\n\n    // A boundary pulls everything confined to it up by one shared amount, the\n    // largest any of them needs. Letting each clamp its own bottom edge instead\n    // would collapse a stacked group onto that edge, sliding the lower ones up\n    // through the ones above.\n    const shiftByBoundary = new Map<HTMLElement, number>();\n    for (const { metrics, baseTop, isStuck } of rows) {\n      const { boundary, boundaryBottom, contentHeight } = metrics;\n      if (!isStuck || boundary == null || boundaryBottom == null) continue;\n      const needed = Math.min(0, boundaryBottom - (baseTop + contentHeight));\n      const current = shiftByBoundary.get(boundary);\n      if (current == null || needed < current) shiftByBoundary.set(boundary, needed);\n    }\n\n    // An entry on its way out stays `isStuck`, so its placeholder keeps holding\n    // the flow slot and the exit isn't a jump.\n    for (const { id, metrics, baseTop, isStuck } of rows) {\n      const shift = isStuck && metrics.boundary ? (shiftByBoundary.get(metrics.boundary) ?? 0) : 0;\n      const result: StickyStackResult = { isStuck, top: baseTop + shift };\n      const prev = this.results.get(id);\n      if (prev && prev.isStuck === result.isStuck && prev.top === result.top) continue;\n      this.results.set(id, result);\n      this.listeners.get(id)?.(result);\n    }\n  };\n\n  private startListening(): void {\n    if (this.listenerCount === 0) {\n      window.addEventListener('scroll', this.schedule, { passive: true, capture: true });\n      window.addEventListener('resize', this.schedule);\n    }\n    this.listenerCount++;\n  }\n\n  private stopListening(): void {\n    this.listenerCount--;\n    if (this.listenerCount === 0) {\n      window.removeEventListener('scroll', this.schedule, { capture: true } as EventListenerOptions);\n      window.removeEventListener('resize', this.schedule);\n    }\n  }\n}\n\nexport const stickyStack = new StickyStack();\n",".sticky {\n  display: block;\n\n  &-content {\n    /* No positioning by default — the content lives in normal flow until it sticks. */\n  }\n\n  &-content-stuck {\n    /* Positioning (position/top/left/width/z-index) is applied inline so it can be measured;\n       this class is a styling hook for consumers (e.g. a shadow once stuck). */\n  }\n\n  &-placeholder {\n    /* Reserves the exact space the content left behind, so nothing below jumps up. */\n    pointer-events: none;\n  }\n}\n","import React, { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';\nimport cx from 'classnames';\nimport type { StickyProps } from './sticky.types';\nimport { stickyStack } from './stickyStack';\nimport type { StickyStackResult } from './stickyStack';\nimport { getScrollParent } from '../utils/scrollAncestors';\n\nimport styles from './sticky.module.pcss';\n\ninterface Metrics {\n  width: number;\n  left: number;\n  height: number;\n}\n\nexport default function Sticky({\n  children,\n  offsetTop = 0,\n  offsetElement,\n  stickToScrollParent = false,\n  boundary,\n  boundaryInset = 0,\n  zIndex = 20,\n  disabled = false,\n  onStuckChange,\n  className,\n  style,\n  classNames,\n  styles: slotStyles,\n  ...rest\n}: StickyProps) {\n  const rootRef = useRef<HTMLDivElement>(null);\n  const contentRef = useRef<HTMLDivElement>(null);\n  const stackIdRef = useRef<symbol | null>(null);\n  const scrollParentRef = useRef<HTMLElement | null>(null);\n  const [metrics, setMetrics] = useState<Metrics>({ width: 0, left: 0, height: 0 });\n  const [measuredOffset, setMeasuredOffset] = useState<number | null>(null);\n  const [scrollParentTop, setScrollParentTop] = useState(0);\n  const effectiveOffsetTop = offsetElement?.current\n    ? (measuredOffset ?? offsetElement.current.offsetHeight)\n    : offsetTop;\n  const [stackResult, setStackResult] = useState<StickyStackResult>({ isStuck: false, top: effectiveOffsetTop });\n\n  const onStuckChangeRef = useRef(onStuckChange);\n  useEffect(() => {\n    onStuckChangeRef.current = onStuckChange;\n  }, [onStuckChange]);\n\n  // The stack entry is registered once, so the boundary reaches it through a ref\n  // rather than the closure: the one handed in may still be empty on first render.\n  const boundaryRef = useRef(boundary);\n  const boundaryInsetRef = useRef(boundaryInset);\n  useLayoutEffect(() => {\n    boundaryRef.current = boundary;\n    boundaryInsetRef.current = boundaryInset;\n  });\n\n  // Local geometry (width/left/height) doesn't depend on other Stickies — only the\n  // effective `top` and `isStuck` (from the stack coordinator) do.\n  const measure = useCallback(() => {\n    const root = rootRef.current;\n    const content = contentRef.current;\n    if (!root || !content) return;\n    const rootRect = root.getBoundingClientRect();\n    setMetrics({ width: root.clientWidth, left: rootRect.left, height: content.offsetHeight });\n    stickyStack.schedule();\n  }, []);\n\n  useEffect(() => {\n    const el = offsetElement?.current;\n    if (!el) return;\n    const updateFromElement = () => setMeasuredOffset(el.offsetHeight);\n    updateFromElement();\n    const ro = new ResizeObserver(updateFromElement);\n    ro.observe(el);\n    return () => ro.disconnect();\n  }, [offsetElement]);\n\n  useLayoutEffect(() => {\n    scrollParentRef.current = stickToScrollParent ? getScrollParent(rootRef.current) : null;\n  }, [stickToScrollParent]);\n\n  useLayoutEffect(() => {\n    const id = stickyStack.register(\n      {\n        node: rootRef.current as HTMLElement,\n        offsetTop: effectiveOffsetTop,\n        disabled,\n        getMetrics: () => {\n          const root = rootRef.current;\n          const content = contentRef.current;\n          if (!root || !content) return null;\n          const rect = root.getBoundingClientRect();\n          const parentRect = scrollParentRef.current?.getBoundingClientRect();\n          const newParentTop = parentRect?.top ?? 0;\n          setScrollParentTop((prev) => (prev === newParentTop ? prev : newParentTop));\n          // Report top/left relative to the scroll parent's box so the stack's stuck/stacking\n          // math stays in the same coordinate space regardless of what it's pinned relative to.\n          const top = parentRect ? rect.top - parentRect.top : rect.top;\n          const left = parentRect ? rect.left - parentRect.left : rect.left;\n          const boundaryEl = boundaryRef.current?.current ?? null;\n          const boundaryRect = boundaryEl?.getBoundingClientRect();\n          return {\n            top,\n            left,\n            width: root.clientWidth,\n            contentHeight: content.offsetHeight,\n            container: scrollParentRef.current,\n            boundary: boundaryEl,\n            boundaryBottom: boundaryRect ? boundaryRect.bottom - newParentTop - boundaryInsetRef.current : null,\n          };\n        },\n      },\n      setStackResult,\n    );\n    stackIdRef.current = id;\n    return () => {\n      stickyStack.unregister(id);\n      stackIdRef.current = null;\n    };\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, []);\n\n  useEffect(() => {\n    if (stackIdRef.current) {\n      stickyStack.updateEntry(stackIdRef.current, { offsetTop: effectiveOffsetTop, disabled });\n    }\n    stickyStack.schedule();\n  }, [effectiveOffsetTop, disabled]);\n\n  useLayoutEffect(() => {\n    measure();\n  }, [measure, children]);\n\n  useEffect(() => {\n    const ro = new ResizeObserver(measure);\n    if (rootRef.current) ro.observe(rootRef.current);\n    if (contentRef.current) ro.observe(contentRef.current);\n    if (scrollParentRef.current) ro.observe(scrollParentRef.current);\n    return () => ro.disconnect();\n  }, [measure, stickToScrollParent]);\n\n  const firstRun = useRef(true);\n  useEffect(() => {\n    if (firstRun.current) {\n      firstRun.current = false;\n      return;\n    }\n    onStuckChangeRef.current?.(stackResult.isStuck);\n  }, [stackResult.isStuck]);\n\n  // stackResult.top is relative to the scroll parent's box (or the viewport if there isn't\n  // one) — convert back to a viewport-absolute value for the fixed `top`.\n  const contentStyle: React.CSSProperties | undefined = stackResult.isStuck\n    ? {\n        position: 'fixed',\n        top: scrollParentTop + stackResult.top,\n        left: metrics.left,\n        width: metrics.width,\n        zIndex,\n        ...slotStyles?.content,\n      }\n    : slotStyles?.content;\n\n  return (\n    <div\n      ref={rootRef}\n      className={cx(styles.sticky, className, classNames?.root)}\n      style={{ ...slotStyles?.root, ...style }}\n      {...rest}\n    >\n      {stackResult.isStuck && (\n        <div\n          aria-hidden\n          className={cx(styles.stickyPlaceholder, classNames?.placeholder)}\n          style={{ height: metrics.height, ...slotStyles?.placeholder }}\n        />\n      )}\n      <div\n        ref={contentRef}\n        className={cx(styles.stickyContent, stackResult.isStuck && styles.stickyContentStuck, classNames?.content)}\n        style={contentStyle}\n      >\n        {children}\n      </div>\n    </div>\n  );\n}\n",".switch {\n  user-select: none;\n  display: flex;\n  align-items: center;\n  column-gap: 0.625em;\n  width: fit-content;\n  cursor: pointer;\n  -webkit-tap-highlight-color: transparent;\n  border: 1px solid transparent;\n  margin: -1px;\n\n  &:focus-visible {\n    outline: none;\n    border: 1px solid var(--focus-border-color, var(--primary-color));\n    border-radius: var(--default-border-radius);\n  }\n\n  &-disabled {\n    filter: brightness(0.8);\n    cursor: not-allowed;\n  }\n\n  &-track {\n    width: 2.25em;\n    height: 1.25em;\n    flex-shrink: 0;\n    border-radius: var(--default-border-radius);\n    background-color: var(--border-color);\n    position: relative;\n    transition: background-color 0.2s;\n    box-sizing: border-box;\n    padding: 0.125em;\n\n    &-checked {\n      background-color: var(--primary-color);\n      background-image: var(--primary-gradient, none);\n    }\n  }\n\n  &-thumb {\n    width: 1em;\n    height: 1em;\n    border-radius: var(--default-border-radius);\n    background-color: var(--background);\n    box-shadow: var(--card-shadow);\n    transform: translateX(0);\n    transition: transform 0.2s;\n\n    &-checked {\n      transform: translateX(1em);\n    }\n  }\n\n  &-label {\n    line-height: 100%;\n    color: var(--text-color);\n  }\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .switch-thumb {\n    transition: none;\n  }\n}\n","import { useEffect, useState, type ReactElement } from 'react';\nimport cx from 'classnames';\n\nimport { useComponentDefaults } from '../configProvider';\nimport type { SwitchProps } from './switch.types';\nimport styles from './switch.module.pcss';\n\nfunction Switch(props: SwitchProps): ReactElement {\n  const {\n    label = '',\n    selected = false,\n    onToggle = () => {},\n    className = '',\n    style,\n    disabled = false,\n    title = '',\n    tabIndex = 0,\n    formValidator = null,\n    children = null,\n    classNames,\n    styles: slotStyles,\n    ...rest\n  } = useComponentDefaults('Switch', props);\n  const [, forceUpdate] = useState(0);\n\n  useEffect(() => {\n    if (!formValidator) return;\n    const listener = () => forceUpdate((n) => n + 1);\n    formValidator.registerOnUpdateListener(listener);\n    return () => formValidator.removeOnUpdateListener(listener);\n  }, [formValidator]);\n\n  function handleToggle(event?: React.MouseEvent | React.KeyboardEvent) {\n    if (disabled) return;\n    if (formValidator) {\n      formValidator.set(!formValidator.value);\n    }\n    onToggle(!currSelected, event);\n  }\n\n  const currSelected = (formValidator ? formValidator.value : selected) as boolean;\n\n  return (\n    <div\n      {...rest}\n      title={title}\n      className={cx(\n        styles.switch,\n        {\n          [styles.switchDisabled]: disabled,\n        },\n        className,\n      )}\n      style={style}\n      onClick={handleToggle}\n      onKeyDown={(e) => {\n        if (e.key === 'Enter' || e.key === ' ') {\n          e.preventDefault();\n          handleToggle(e);\n        }\n      }}\n      role='switch'\n      aria-checked={currSelected}\n      tabIndex={disabled ? -1 : tabIndex}\n    >\n      <div\n        className={cx(styles.switchTrack, currSelected && styles.switchTrackChecked, classNames?.track)}\n        style={slotStyles?.track}\n      >\n        <div\n          className={cx(styles.switchThumb, currSelected && styles.switchThumbChecked, classNames?.thumb)}\n          style={slotStyles?.thumb}\n        />\n      </div>\n      {(children || label) && (\n        <div className={cx(styles.switchLabel, classNames?.label)} style={slotStyles?.label}>\n          {children || label}\n        </div>\n      )}\n    </div>\n  );\n}\n\nexport default Switch;\n",".tabHeader {\n  display: flex;\n  align-items: stretch;\n  gap: 4px;\n  border-bottom: 1px solid var(--border-color);\n  user-select: none;\n\n  /* The y value is not redundant: overflow-x: auto alone computes overflow-y to auto, so any\n     vertical overflow, such as a flex column squeezing the strip, let the tabs scroll up and down. */\n  overflow: auto hidden;\n\n  &-tab {\n    display: flex;\n    align-items: center;\n    gap: 10px;\n    padding: 12px 16px;\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n    color: var(--text-dark);\n    border-bottom: 2px solid transparent;\n    border-radius: var(--default-border-radius) var(--default-border-radius) 0 0;\n    transition:\n      color 0.2s,\n      background 0.2s,\n      border-color 0.2s;\n    white-space: nowrap;\n\n    &:hover {\n      /* Longhand, not the shorthand: hover beats -active on specificity, and the shorthand\n         would reset the active tab's gradient indicator away while the pointer is over it. */\n      background-color: var(--background-accent-light);\n      color: var(--text-color);\n    }\n\n    &-active {\n      color: var(--primary-color);\n      border-bottom-color: var(--primary-color);\n\n      /* Paints the gradient exactly over border-bottom, which a background layer only reaches\n         because its origin is the border box. The border stays as the no-gradient fallback. */\n      background-image: var(--primary-gradient, none);\n      background-repeat: no-repeat;\n      background-size: 100% 2px;\n      background-position: bottom;\n      background-origin: border-box;\n\n      &:hover {\n        color: var(--primary-color);\n      }\n    }\n\n    &-disabled {\n      opacity: 0.5;\n      pointer-events: none;\n      cursor: default;\n    }\n  }\n\n  &-icon {\n    font-size: 18px;\n    flex-shrink: 0;\n  }\n\n  &-labels {\n    display: flex;\n    flex-direction: column;\n    text-align: left;\n    line-height: 1.25;\n  }\n\n  &-title {\n    font-weight: var(--header-font-weight, 600);\n    font-size: var(--default-font-size);\n  }\n\n  &-subtitle {\n    font-size: var(--text-small-font-size);\n    color: var(--text-dark);\n  }\n}\n\n@media (width <= 640px) {\n  .tabHeader-tab:not(.tabHeader-tab-active) .tabHeader-labels {\n    display: none;\n  }\n}\n","import React from 'react';\nimport cx from 'classnames';\n\nimport InteractableDiv from '../interactableDiv';\nimport type { TabHeaderProps, TabHeaderTab } from './tabHeader.types';\n\nimport styles from './tabHeader.module.pcss';\nimport { renderIcon } from '../utils/renderIcon';\n\n// Not a truthiness test: a count of 0 is content.\nfunction hasContent(node: React.ReactNode) {\n  return node != null && node !== false && node !== '';\n}\n\nfunction TabHeader({\n  tabs = [],\n  currentTab,\n  onChange,\n  className,\n  style,\n  classNames,\n  styles: slotStyles,\n  ...rest\n}: TabHeaderProps) {\n  return (\n    <div\n      {...rest}\n      role='tablist'\n      className={cx(styles.tabHeader, classNames?.root, className)}\n      style={{ ...slotStyles?.root, ...style }}\n    >\n      {tabs.map((tab: TabHeaderTab) => {\n        const isActive = tab.id === currentTab;\n        const hasTitle = hasContent(tab.title);\n        const hasSubtitle = hasContent(tab.subtitle);\n        const hasLabels = hasTitle || hasSubtitle;\n\n        return (\n          <InteractableDiv\n            key={tab.id}\n            role='tab'\n            aria-selected={isActive}\n            aria-disabled={tab.disabled || undefined}\n            tabIndex={tab.disabled ? -1 : 0}\n            className={cx(\n              styles.tabHeaderTab,\n              { [styles.tabHeaderTabActive]: isActive },\n              { [styles.tabHeaderTabDisabled]: tab.disabled },\n              classNames?.tab,\n            )}\n            style={slotStyles?.tab}\n            onClick={() => {\n              if (tab.disabled || isActive) return;\n              onChange(tab.id);\n            }}\n          >\n            {renderIcon(tab.icon, {\n              className: cx(styles.tabHeaderIcon, classNames?.icon),\n              style: slotStyles?.icon,\n            })}\n            {hasLabels && (\n              <div className={cx(styles.tabHeaderLabels, classNames?.labels)} style={slotStyles?.labels}>\n                {hasTitle && (\n                  <div className={cx(styles.tabHeaderTitle, classNames?.title)} style={slotStyles?.title}>\n                    {tab.title}\n                  </div>\n                )}\n                {hasSubtitle && (\n                  <div className={cx(styles.tabHeaderSubtitle, classNames?.subtitle)} style={slotStyles?.subtitle}>\n                    {tab.subtitle}\n                  </div>\n                )}\n              </div>\n            )}\n            {/* Outside the labels, which a narrow screen hides on every inactive tab. */}\n            {hasContent(tab.endContent) && (\n              <span className={classNames?.endContent} style={slotStyles?.endContent}>\n                {tab.endContent}\n              </span>\n            )}\n          </InteractableDiv>\n        );\n      })}\n    </div>\n  );\n}\n\nexport default TabHeader;\n",".templateEditor {\n  /* Theme tokens resolved once, each with a built-in fallback. Set any of the\n     public `--template-editor-*` variables via ThemeProvider (theme-wide) or\n     `style` (per instance) to override; unset ones fall back to the values below. */\n  --custom-te-palette-width: var(--template-editor-palette-width, 200px);\n  --custom-te-gap: var(--template-editor-gap, 16px);\n\n  display: flex;\n  gap: var(--custom-te-gap);\n  width: 100%;\n  color: var(--text-color);\n\n  &-right {\n    flex-flow: row wrap;\n    align-items: flex-start;\n  }\n\n  &-bottom {\n    flex-direction: column;\n  }\n\n  &-field {\n    flex: 1 1 auto;\n    min-width: 0;\n  }\n\n  &-surface {\n    /* How far below the top of the field the first line starts. On a multiline field that is\n       Input's own 4px padding-top; a single-line one sets it below. */\n    --custom-te-line-top: 4px;\n\n    position: relative;\n    display: flex;\n    align-items: flex-start;\n    width: 100%;\n\n    &-singleLine {\n      --custom-te-line-top: calc(7px - var(--field-label-offset, 0px));\n    }\n  }\n\n  &-editable {\n    flex: 1 1 auto;\n    min-width: 0;\n\n    /* Literal `\\n` characters carry the line breaks, so the DOM inside the\n       field stays a flat list of text nodes and chips. */\n    white-space: pre-wrap;\n    overflow-wrap: anywhere;\n    outline: none;\n    max-height: 60vh;\n    overflow-y: auto;\n\n    &[data-dragover='true'] {\n      background: var(--background-accent-light);\n      border-radius: var(--default-border-radius);\n    }\n\n    &-disabled {\n      opacity: 0.6;\n    }\n  }\n\n  &-chip {\n    display: inline-block;\n    vertical-align: baseline;\n    border-radius: 99999px;\n    padding: 1px 8px;\n\n    /* No side margins: the gap beside a chip is then exactly the text beside it, so two chips with\n       no space between them touch, and read as the joined words they will be sent as. */\n    font-size: 0.9em;\n    line-height: 1.4;\n    white-space: nowrap;\n\n    /* Deliberately no `user-select: none`. WebKit refuses to drag an element\n       that has it, and on an inline chip it would also drop the chip out of a\n       text selection dragged across it, so copying a range spanning a chip\n       would lose the placeholder. */\n\n    /* Read here, not resolved on the root like the others: the --tone properties are set by the\n       tone class on each chip, so they do not exist on the root. */\n    background: var(--template-editor-chip-background, var(--tone-light));\n    color: var(--template-editor-chip-color, var(--tone-text));\n\n    &[data-dragging='true'] {\n      opacity: 0.4;\n    }\n\n    &[data-highlighted] {\n      outline: 2px solid var(--tone);\n      outline-offset: 1px;\n    }\n\n    /* Drawn whole, in the colour text is selected in: left to the engines, Chromium painted a\n       selected chip not at all and Firefox drew hard rectangles inside the pill. The attribute\n       outranks the one-class preview and unknown rules below. */\n    &[data-selected] {\n      background: Highlight;\n      color: HighlightText;\n    }\n\n    /* The keyframes are named apart from this class: both would camelCase to the same module key,\n       and the keyframes' export would replace the class. */\n    &-inserted {\n      animation: templateEditorChipPop 400ms ease-out;\n    }\n\n    /* The sample reads as the prose it will become: the field's own size and line, marked only by\n       the tone's underline. It wraps like that prose too: a nowrap inline-block ran a sample longer\n       than the field out of it. */\n    &-preview {\n      display: inline;\n      padding: 0;\n      background: none;\n      font-size: inherit;\n      white-space: inherit;\n      text-decoration: underline dotted;\n      text-underline-offset: 0.2em;\n    }\n\n    &-unknown {\n      background: var(--template-editor-unknown-chip-background, var(--tone-light));\n      color: var(--template-editor-unknown-chip-color, var(--tone-text));\n    }\n\n    &-remove {\n      display: inline-block;\n\n      /* Negative right margin keeps the chip's visual padding unchanged while\n         the padding here widens the tap target on touch. */\n      margin: 0 -3px 0 4px;\n      padding: 0 3px;\n      font-weight: 700;\n      line-height: 1;\n      opacity: 0.5;\n      cursor: pointer;\n      -webkit-tap-highlight-color: transparent;\n\n      &:hover {\n        opacity: 1;\n      }\n    }\n  }\n\n  /* Invisible element the autocomplete anchors to. It lives outside the\n     editable: inside, it would be serialized into the value, split by editing,\n     and typed into. One pixel wide, not zero: WebKit counts an anchor without\n     area as hidden, and `position-visibility: anchors-visible` hid the list too. */\n  &-caretAnchor {\n    position: absolute;\n    width: 1px;\n    pointer-events: none;\n  }\n\n  &-dropCaret {\n    position: absolute;\n    width: 2px;\n    background: var(--primary-color);\n    pointer-events: none;\n    opacity: 0;\n\n    &-visible {\n      opacity: 1;\n    }\n  }\n\n  /* One line tall and level with the first line, so the buttons sit on that line however many\n     follow, and never set the field's height themselves. 1.25em is Input's line-height. */\n  &-actions {\n    flex: none;\n    display: flex;\n    align-items: center;\n    gap: 2px;\n    height: 1.25em;\n    margin-top: var(--custom-te-line-top);\n  }\n\n  &-palette {\n    display: flex;\n    flex-direction: column;\n    gap: 8px;\n    min-width: 0;\n\n    &-label {\n      font-size: 0.8em;\n      color: var(--text-light);\n    }\n\n    &-list {\n      display: flex;\n      flex-direction: column;\n      gap: 12px;\n    }\n\n    &-group {\n      min-width: 0;\n    }\n\n    &-group-label {\n      font-size: 0.75em;\n      text-transform: uppercase;\n      letter-spacing: 0.04em;\n      color: var(--text-light);\n      margin-bottom: 4px;\n    }\n\n    &-items {\n      display: flex;\n      flex-wrap: wrap;\n      gap: 6px;\n    }\n\n    &-item {\n      display: inline-flex;\n      align-items: baseline;\n      gap: 6px;\n      cursor: grab;\n      -webkit-tap-highlight-color: transparent;\n\n      /* Explicit for WebKit, which otherwise drags the item's text rather than\n         the element itself. */\n      -webkit-user-drag: element;\n      max-width: 100%;\n\n      &:focus-visible {\n        outline: 2px solid var(--primary-color);\n        outline-offset: 2px;\n      }\n\n      &:not([aria-disabled]):active {\n        cursor: grabbing;\n      }\n\n      &-label {\n        overflow: hidden;\n        text-overflow: ellipsis;\n      }\n\n      &-description {\n        font-size: 0.85em;\n        opacity: 0.7;\n        white-space: nowrap;\n        overflow: hidden;\n        text-overflow: ellipsis;\n      }\n\n      /* Grey rather than faded: a disabled entry is there to be read, its description usually\n         saying why it cannot be used. Overrides the chip rule's colour, so it stays after it.\n         Unselectable, or a press-and-drag that cannot move it highlights its text instead. */\n      &-disabled {\n        color: var(--text-light);\n        cursor: not-allowed;\n        user-select: none;\n        -webkit-user-drag: none;\n      }\n    }\n  }\n\n  &-suggestions {\n    background: var(--background);\n    border: 1px solid var(--border-color);\n    border-radius: var(--default-border-radius);\n    box-shadow: var(--card-shadow);\n    max-height: 260px;\n    overflow-y: auto;\n    min-width: 220px;\n\n    /* Caps what a long sample can stretch the list to, so the sample is cut short instead. */\n    max-width: min(360px, 100vw - 32px);\n    padding: 4px 0;\n\n    &-anchor {\n      position: fixed;\n      top: 0;\n      left: 0;\n\n      /* Shared by every overlay, so the order they are portaled in, which is the order they opened in, decides. */\n      z-index: 1;\n\n      /* Hidden until usePopoverPosition writes the first coordinates, to avoid a top-left flash. */\n      visibility: hidden;\n    }\n  }\n\n  &-suggestion {\n    &-list {\n      display: flex;\n      flex-direction: column;\n    }\n\n    &-group {\n      min-width: 0;\n    }\n\n    &-group-label {\n      font-size: 0.7em;\n      text-transform: uppercase;\n      letter-spacing: 0.04em;\n      color: var(--text-light);\n      padding: 6px 12px 2px;\n    }\n\n    &-item {\n      display: grid;\n      grid-template-columns: auto minmax(0, 1fr);\n      column-gap: 16px;\n      align-items: baseline;\n      padding: 6px 12px;\n      cursor: pointer;\n      -webkit-tap-highlight-color: transparent;\n\n      &-active {\n        background: var(--background-accent-light);\n      }\n    }\n\n    &-label {\n      color: var(--text-color);\n    }\n\n    &-match {\n      background: none;\n      color: inherit;\n      font-weight: 600;\n    }\n\n    &-sample {\n      justify-self: end;\n      max-width: 100%;\n      overflow: hidden;\n      font-size: 0.85em;\n      color: var(--text-light);\n      text-overflow: ellipsis;\n      white-space: nowrap;\n    }\n\n    &-description {\n      grid-column: 1 / -1;\n      font-size: 0.85em;\n      color: var(--text-light);\n    }\n  }\n}\n\n/* The palette wraps under the field once the two no longer fit side by side, and takes the whole\n   row there. The 999 hands nearly all spare width on a wide row to the field, so beside it the\n   palette keeps its own width. Two classes, so it beats the single-class rules above. */\n.templateEditor-right .templateEditor-field {\n  flex: 999 1 320px;\n}\n\n.templateEditor-right .templateEditor-palette {\n  flex: 1 0 var(--custom-te-palette-width);\n}\n\n/* Half of the 14px that Input's min-height adds to a 1.25em line, less the label offset its\n   container pads with: one line then fills the field exactly, as an Input's centred text does. Left\n   to Input's centring instead, a second line had no room to centre in and the first jumped up 4px.\n   Two classes, to beat Input's own padding on the field. */\n.templateEditor-surface-singleLine .templateEditor-editable {\n  padding-block: var(--custom-te-line-top);\n}\n\n/* 1.2 of the chip's 0.9em is 1.08em, inside the field's 1.25em line: chips on consecutive lines\n   keep a gap instead of overlapping, and a line holding one is no taller than the rest. Not on\n   palette entries, which share the chip class and keep their larger tap target. */\n.templateEditor-editable .templateEditor-chip {\n  padding-block: 0;\n  line-height: 1.2;\n}\n\n/* The native highlight inside a chip, which `data-selected` replaces. */\n.templateEditor-editable .templateEditor-chip ::selection {\n  background: transparent;\n}\n\n/* The preview toggle's morph, see `morphChips`. Each image keeps its own size, pinned to the start\n   and clipped by the box resizing between chip and sample. By default both are scaled to the box's\n   width, which shrank a chip's label to a sliver and blew a short sample up to several lines tall. */\n::view-transition-group(*.templateEditor-chip-morph) {\n  animation-duration: 300ms;\n}\n\n::view-transition-old(*.templateEditor-chip-morph),\n::view-transition-new(*.templateEditor-chip-morph) {\n  height: 100%;\n  object-fit: none;\n  object-position: left center;\n  overflow: clip;\n}\n\n@keyframes templateEditorChipPop {\n  0% {\n    transform: scale(0.8);\n    box-shadow: 0 0 0 2px color-mix(in srgb, var(--tone) 45%, transparent);\n  }\n\n  35% {\n    transform: scale(1.05);\n  }\n\n  60% {\n    transform: scale(1);\n  }\n\n  100% {\n    box-shadow: 0 0 0 2px transparent;\n  }\n}\n\n@keyframes templateEditorChipGlow {\n  from {\n    box-shadow: 0 0 0 2px color-mix(in srgb, var(--tone) 45%, transparent);\n  }\n\n  to {\n    box-shadow: 0 0 0 2px transparent;\n  }\n}\n\n/* Only the scale is motion. The glow alone still shows where the chip landed. */\n@media (prefers-reduced-motion: reduce) {\n  .templateEditor-chip-inserted {\n    animation-name: templateEditorChipGlow;\n  }\n\n  /* The box no longer travels or resizes; the chip and its sample still cross-fade in place. */\n  ::view-transition-group(*.templateEditor-chip-morph) {\n    animation-name: none;\n  }\n}\n","import type {\n  TemplateEditorLabels,\n  TemplateEditorToken,\n  TemplateEditorTokenGroup,\n  TemplateSegment,\n} from './templateEditor.types';\n\nexport const DEFAULT_PREFIX = '{{';\nexport const DEFAULT_SUFFIX = '}}';\n\nexport const DEFAULT_LABELS: Required<TemplateEditorLabels> = {\n  palette: 'Placeholders',\n  insertToken: 'Insert placeholder',\n  preview: 'Preview with sample data',\n  removeToken: (label) => `Remove ${label}`,\n  qualifiedToken: (group, label) => `${group} · ${label}`,\n  unknownToken: (id) => id,\n  unknownTokenTitle: (id) => `${id} (unknown placeholder)`,\n};\n\n/** Escapes a string so it can be embedded in a RegExp as a literal. */\nexport function escapeRegExp(value: string): string {\n  return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/**\n * Builds the token matcher from the configured delimiters.\n *\n * The id is deliberate on all four counts: non-greedy so it stops at the\n * first closing delimiter rather than swallowing everything up to the last one;\n * newline-free so a stray opening delimiter typed in prose can't consume whole\n * paragraphs; free of the opening delimiter, so an unclosed `{{` earlier on the\n * line stays text instead of taking that text and the next token into one chip;\n * and `+` rather than `*` so an empty `{{}}` stays literal text instead of\n * becoming an invisible, unremovable chip.\n */\nexport function buildTokenRegex(prefix: string, suffix: string): RegExp {\n  const open = escapeRegExp(prefix);\n  return new RegExp(`${open}((?:(?!${open})[^\\\\n])+?)${escapeRegExp(suffix)}`, 'g');\n}\n\n/**\n * Splits a template string into literal text and tokens, exactly as the editor reads it. Joining the\n * segments back, each token as `prefix + id + suffix`, gives the input unchanged.\n */\nexport function parseTemplate(value: string, prefix = DEFAULT_PREFIX, suffix = DEFAULT_SUFFIX): TemplateSegment[] {\n  if (!value) return [];\n  const segments: TemplateSegment[] = [];\n  const regex = buildTokenRegex(prefix, suffix);\n  let lastIndex = 0;\n  let match: RegExpExecArray | null;\n\n  while ((match = regex.exec(value)) !== null) {\n    if (match.index > lastIndex) {\n      segments.push({ type: 'text', text: value.slice(lastIndex, match.index) });\n    }\n    segments.push({ type: 'token', id: match[1] });\n    lastIndex = match.index + match[0].length;\n  }\n\n  if (lastIndex < value.length) {\n    segments.push({ type: 'text', text: value.slice(lastIndex) });\n  }\n  return segments;\n}\n\n/** Line endings as `\\n`, or, where the field is single-line, each run of line breaks as one space. */\nexport function normalizeLineBreaks(text: string, multiline: boolean): string {\n  return multiline ? text.replace(/\\r\\n?/g, '\\n') : text.replace(/(\\r\\n?|\\n)+/g, ' ');\n}\n\n/** Number of characters a token occupies in the serialized string, which is the unit caret offsets are counted in. */\nexport function tokenLength(id: string, prefix: string, suffix: string): number {\n  return prefix.length + id.length + suffix.length;\n}\n\n/** The span a token occupies in the serialized string. */\nexport interface TokenSpan {\n  start: number;\n  end: number;\n  id: string;\n}\n\n/** Every token in the string with the offsets it occupies. */\nexport function tokenSpans(value: string, prefix: string, suffix: string): TokenSpan[] {\n  const spans: TokenSpan[] = [];\n  let position = 0;\n  parseTemplate(value, prefix, suffix).forEach((segment) => {\n    const length = segment.type === 'text' ? segment.text.length : tokenLength(segment.id, prefix, suffix);\n    if (segment.type === 'token') spans.push({ start: position, end: position + length, id: segment.id });\n    position += length;\n  });\n  return spans;\n}\n\n/** The token immediately before `offset`, i.e. the one a backspace should remove whole. */\nexport function tokenSpanEndingAt(value: string, offset: number, prefix: string, suffix: string): TokenSpan | null {\n  return tokenSpans(value, prefix, suffix).find((span) => span.end === offset) ?? null;\n}\n\n/** The token immediately after `offset`, i.e. the one a forward delete should remove whole. */\nexport function tokenSpanStartingAt(value: string, offset: number, prefix: string, suffix: string): TokenSpan | null {\n  return tokenSpans(value, prefix, suffix).find((span) => span.start === offset) ?? null;\n}\n\n/** Whether `offset` starts a line with a token, which is where the editor keeps a spacer (see `isSpacer`). */\nexport function isLineStartBeforeToken(value: string, offset: number, prefix: string, suffix: string): boolean {\n  return offset > 0 && value[offset - 1] === '\\n' && !!tokenSpanStartingAt(value, offset, prefix, suffix);\n}\n\nconst WORD_BEFORE = /[\\p{L}\\p{M}\\p{N}]$/u;\nconst WORD_AFTER = /^[\\p{L}\\p{N}]/u;\n\n/**\n * The spaces smart spacing puts around a chip placed over `start…end`: one on each side where it\n * would otherwise touch a letter, a digit or another chip. Punctuation, whitespace and line edges\n * get none, so `({{name}})`, `{{name}}'s` and `{{first}}.{{last}}@acme.com` keep their shape: a\n * missing space is one keystroke to add, a wrong one inside an address breaks it unseen.\n */\nexport function spacesAround(\n  value: string,\n  start: number,\n  end: number,\n  prefix: string,\n  suffix: string,\n): [before: string, after: string] {\n  const before = WORD_BEFORE.test(value.slice(0, start)) || !!tokenSpanEndingAt(value, start, prefix, suffix);\n  const after = WORD_AFTER.test(value.slice(end)) || !!tokenSpanStartingAt(value, end, prefix, suffix);\n  return [before ? ' ' : '', after ? ' ' : ''];\n}\n\n/**\n * `value` with a chip's `start…end` cut out, and with smart spacing also the space that left\n * redundant: one of two spaces meeting in the gap, or one left at the start or end of a line.\n * `at` is where the cut closed, which is where the caret belongs.\n */\nexport function cutChip(value: string, start: number, end: number, tidy: boolean): { value: string; at: number } {\n  const without = value.slice(0, start) + value.slice(end);\n  if (!tidy) return { value: without, at: start };\n  const before = without[start - 1];\n  const after = without[start];\n  const lineStart = start === 0 || before === '\\n';\n  const lineEnd = start === without.length || after === '\\n';\n  const gap = before === ' ' && (after === ' ' || lineEnd) ? start - 1 : after === ' ' && lineStart ? start : null;\n  if (gap === null) return { value: without, at: start };\n  return { value: without.slice(0, gap) + without.slice(gap + 1), at: gap };\n}\n\n/** Longest query the autocomplete will track before giving up and assuming the delimiter was prose. */\nconst MAX_QUERY_LENGTH = 60;\n\n/**\n * The unterminated `prefix…` run the caret currently sits in, which is what\n * opens the autocomplete. Returns `null` when the caret is not inside one, so\n * a delimiter typed as ordinary prose stops matching as soon as it is followed\n * by a newline or a closing delimiter.\n */\nexport function openTokenRun(\n  value: string,\n  offset: number,\n  prefix: string,\n  suffix: string,\n): { start: number; query: string } | null {\n  const head = value.slice(0, offset);\n  const start = head.lastIndexOf(prefix);\n  // A delimiter of a complete token opens nothing. Where the suffix is the prefix, `%name%`, the\n  // token's closing `%` read as an open run, so every insertion reopened the list.\n  const closedUpTo = Math.max(\n    0,\n    ...tokenSpans(value, prefix, suffix).map((span) => (span.end <= offset ? span.end : 0)),\n  );\n  if (start < closedUpTo) return null;\n  const query = head.slice(start + prefix.length);\n  if (query.length > MAX_QUERY_LENGTH) return null;\n  if (query.includes('\\n') || query.includes(suffix)) return null;\n  return { start, query };\n}\n\n/** Flattens the groups into an id lookup. The first occurrence of an id wins. */\nexport function buildTokenIndex(groups: TemplateEditorTokenGroup[] = []): Map<string, TemplateEditorToken> {\n  const index = new Map<string, TemplateEditorToken>();\n  groups.forEach((group) => {\n    group.tokens?.forEach((token) => {\n      if (!index.has(token.id)) index.set(token.id, token);\n    });\n  });\n  return index;\n}\n\n/** The text shown on a chip and in the palette for a token. */\nexport function tokenLabel(token: TemplateEditorToken): string {\n  return token.label ?? token.id;\n}\n\n/**\n * Chip text per token id. A palette entry sits under its group's heading and a chip does not, so a\n * label two tokens share gets its group in front: two \"First name\" chips would read the same\n * whichever person they resolve to.\n */\nexport function buildChipLabels(\n  groups: TemplateEditorTokenGroup[] = [],\n  qualify: (group: string, label: string) => string,\n): Map<string, string> {\n  const index = buildTokenIndex(groups);\n  const counts = new Map<string, number>();\n  index.forEach((token) => counts.set(tokenLabel(token), (counts.get(tokenLabel(token)) ?? 0) + 1));\n  const labels = new Map<string, string>();\n  groups.forEach((group) => {\n    group.tokens?.forEach((token) => {\n      if (labels.has(token.id)) return;\n      const label = tokenLabel(token);\n      labels.set(token.id, group.label && (counts.get(label) ?? 0) > 1 ? qualify(group.label, label) : label);\n    });\n  });\n  return labels;\n}\n\n/** Case-insensitive substring filter over id, label, and description. Groups left empty are dropped. */\nexport function filterGroups(groups: TemplateEditorTokenGroup[] = [], query: string): TemplateEditorTokenGroup[] {\n  const needle = query.trim().toLowerCase();\n  if (!needle) return groups;\n  return groups\n    .map((group) => ({\n      ...group,\n      tokens: (group.tokens ?? []).filter((token) =>\n        [token.id, token.label, token.description].some((field) => field?.toLowerCase().includes(needle)),\n      ),\n    }))\n    .filter((group) => group.tokens.length > 0);\n}\n\n/**\n * `text` split around the first case-insensitive occurrence of `query`, for the autocomplete to mark\n * what matched; `null` when it does not occur. Matched with a regex rather than by `indexOf` on a\n * lowercased copy, whose offsets drift from the original where lowercasing changes a length (`İ`).\n */\nexport function splitAtMatch(text: string, query: string): [string, string, string] | null {\n  const needle = query.trim();\n  const match = needle ? new RegExp(escapeRegExp(needle), 'i').exec(text) : null;\n  if (!match) return null;\n  const end = match.index + match[0].length;\n  return [text.slice(0, match.index), match[0], text.slice(end)];\n}\n\n/** Every token across all groups, in display order. Used for autocomplete highlight navigation. */\nexport function flattenTokens(groups: TemplateEditorTokenGroup[] = []): TemplateEditorToken[] {\n  return groups.flatMap((group) => group.tokens ?? []);\n}\n","import cx from 'classnames';\n\nimport styles from './templateEditor.module.pcss';\nimport { splitAtMatch, tokenLabel } from './templateEditor.utils';\nimport type { TemplateEditorSuggestionsProps } from './templateEditor.types';\n\nfunction marked(text: string, query: string) {\n  const parts = splitAtMatch(text, query);\n  if (!parts) return text;\n  return (\n    <>\n      {parts[0]}\n      <mark className={styles.templateEditorSuggestionMatch}>{parts[1]}</mark>\n      {parts[2]}\n    </>\n  );\n}\n\n/**\n * The list inside the caret-anchored autocomplete popup. Purely presentational:\n * the highlighted index and every key binding live in the editable, because the\n * keyboard events arrive on the contenteditable, not here.\n */\nfunction TemplateEditorSuggestions({\n  groups,\n  query,\n  highlightedId,\n  listId,\n  optionId,\n  onSelect,\n  classNames,\n  styles: slotStyles,\n}: TemplateEditorSuggestionsProps) {\n  return (\n    <div id={listId} role='listbox' className={styles.templateEditorSuggestionList}>\n      {groups.map((group, groupIndex) => (\n        <div key={group.id ?? group.label ?? groupIndex} className={styles.templateEditorSuggestionGroup}>\n          {group.label && (\n            <div\n              className={cx(styles.templateEditorSuggestionGroupLabel, classNames?.suggestionGroupLabel)}\n              style={slotStyles?.suggestionGroupLabel}\n              role='presentation'\n            >\n              {group.label}\n            </div>\n          )}\n          {group.tokens.map((token) => {\n            const selected = token.id === highlightedId;\n            return (\n              <div\n                key={token.id}\n                id={optionId(token.id)}\n                role='option'\n                aria-selected={selected}\n                className={cx(\n                  styles.templateEditorSuggestionItem,\n                  selected && styles.templateEditorSuggestionItemActive,\n                  classNames?.suggestionItem,\n                )}\n                style={slotStyles?.suggestionItem}\n                // Keep focus and the selection in the editable: a mousedown here\n                // would blur it and lose the run the popup is about to replace.\n                onMouseDown={(event) => event.preventDefault()}\n                onClick={() => onSelect(token.id)}\n              >\n                <span className={styles.templateEditorSuggestionLabel}>{marked(tokenLabel(token), query)}</span>\n                {token.sample && <span className={styles.templateEditorSuggestionSample}>{token.sample}</span>}\n                {token.description && (\n                  <span className={styles.templateEditorSuggestionDescription}>{marked(token.description, query)}</span>\n                )}\n              </div>\n            );\n          })}\n        </div>\n      ))}\n    </div>\n  );\n}\n\nexport default TemplateEditorSuggestions;\n","import type React from 'react';\nimport type { TemplateEditorLabels, TemplateEditorToken } from './templateEditor.types';\nimport { parseTemplate, tokenLabel, tokenLength, tokenSpans } from './templateEditor.utils';\n\n/** Everything the DOM helpers need to turn a token id into a chip element. */\nexport interface TemplateChipContext {\n  prefix: string;\n  suffix: string;\n  tokenIndex: Map<string, TemplateEditorToken>;\n  /** From `buildChipLabels`. A token missing here is labelled with `tokenLabel`. */\n  chipLabels?: Map<string, string>;\n  labels: Pick<Required<TemplateEditorLabels>, 'removeToken' | 'unknownToken' | 'unknownTokenTitle'>;\n  /** Show each token's `sample`, where it has one, in place of its label. */\n  preview?: boolean;\n  chipClassName?: string;\n  /** Added to a chip whose token is in `tokenIndex`; `unknownChipClassName` replaces it otherwise. */\n  knownChipClassName?: string;\n  unknownChipClassName?: string;\n  /** Added to a chip showing its sample. */\n  previewChipClassName?: string;\n  chipStyle?: React.CSSProperties;\n  /** Render a remove button inside each chip. Off while the field is read-only or disabled. */\n  showRemove?: boolean;\n  removeClassName?: string;\n  removeStyle?: React.CSSProperties;\n}\n\n/** Where a boundary in the serialized string lands in the DOM. */\ninterface BoundaryPoint {\n  node: Node;\n  offset: number;\n}\n\n/** Which side of a chip a snapped selection endpoint should end up on. */\ntype SnapPreference = 'before' | 'after';\n\n// Deliberately not a type predicate: narrowing an `HTMLElement` to `HTMLElement`\n// leaves every `else` branch typed `never`, which every caller here has one of.\nexport function isChip(node: Node | null | undefined): boolean {\n  return !!node && node.nodeType === Node.ELEMENT_NODE && (node as HTMLElement).dataset?.chip !== undefined;\n}\n\nconst SPACER_TEXT = '\\uFEFF';\n\n/**\n * A zero-width text holder in front of a chip that starts a line. Without one,\n * the only position before such a chip is the end of the text before it, after\n * its newline, and no engine handles that: Chrome draws no caret there and types\n * after the chip, WebKit types before the newline, Firefox draws the caret on the\n * line above. Slate and ProseMirror carry a spacer for the same reason. It never\n * serializes, and text typed into it is moved out by `normalizeEditable`.\n */\nexport function isSpacer(node: Node | null | undefined): boolean {\n  return !!node && node.nodeType === Node.ELEMENT_NODE && (node as HTMLElement).dataset?.spacer !== undefined;\n}\n\nfunction endsLine(node: Node | null): boolean {\n  return !!node && node.nodeType === Node.TEXT_NODE && (node as Text).data.endsWith('\\n');\n}\n\n/** Puts a spacer in front of every chip that starts a line, and removes any that no longer does. Returns whether anything changed. */\nexport function syncSpacers(root: HTMLElement): boolean {\n  let changed = false;\n  Array.from(root.childNodes).forEach((node) => {\n    if (isSpacer(node) && !(isChip(node.nextSibling) && endsLine(node.previousSibling))) {\n      node.parentNode?.removeChild(node);\n      changed = true;\n    }\n  });\n  Array.from(root.childNodes).forEach((node) => {\n    if (!isChip(node) || !endsLine(node.previousSibling)) return;\n    const spacer = root.ownerDocument.createElement('span');\n    spacer.dataset.spacer = '';\n    spacer.textContent = SPACER_TEXT;\n    node.parentNode?.insertBefore(spacer, node);\n    changed = true;\n  });\n  return changed;\n}\n\n/**\n * A `<br>` that exists only so the caret has somewhere to go, and that must not\n * serialize to a newline. Ours are tagged; a browser-inserted trailing `<br>` is\n * treated the same way, since the canonical DOM never contains a real `<br>`\n * (line breaks are literal `\\n` characters under `white-space: pre-wrap`).\n */\nexport function isBogusBr(node: Node | null | undefined): boolean {\n  if (!node || node.nodeName !== 'BR') return false;\n  return (node as HTMLElement).dataset?.bogus !== undefined || node.parentNode?.lastChild === node;\n}\n\n/** The nearest enclosing chip of a node, or `null` when it isn't inside one. */\nexport function closestChip(root: HTMLElement, node: Node | null): HTMLElement | null {\n  let current: Node | null = node;\n  while (current && current !== root) {\n    if (isChip(current)) return current as HTMLElement;\n    current = current.parentNode;\n  }\n  return null;\n}\n\nfunction nameOf(id: string, token: TemplateEditorToken, ctx: TemplateChipContext): string {\n  return ctx.chipLabels?.get(id) ?? tokenLabel(token);\n}\n\nexport function chipLabelFor(id: string, ctx: TemplateChipContext): string {\n  const token = ctx.tokenIndex.get(id);\n  if (!token) return ctx.labels.unknownToken(id);\n  return ctx.preview && token.sample ? token.sample : nameOf(id, token, ctx);\n}\n\n/** The chip's own label node, which is what the repair sweep compares against. */\nfunction labelElementOf(chip: HTMLElement): HTMLElement | null {\n  return chip.querySelector<HTMLElement>('[data-chip-label]');\n}\n\nfunction createRemoveElement(label: string, ctx: TemplateChipContext, doc: Document): HTMLElement {\n  const el = doc.createElement('span');\n  el.dataset.chipRemove = '';\n  // Exposed as a button but never in the tab order: Tab belongs to the text\n  // caret here, and keyboard users delete a chip by arrowing onto it and\n  // pressing Backspace. This is the pointer and touch affordance for that.\n  el.setAttribute('role', 'button');\n  el.setAttribute('aria-label', ctx.labels.removeToken(label));\n  el.setAttribute('contenteditable', 'false');\n  el.draggable = false;\n  el.textContent = '×';\n  if (ctx.removeClassName) el.className = ctx.removeClassName;\n  if (ctx.removeStyle) Object.assign(el.style, ctx.removeStyle);\n  return el;\n}\n\n/**\n * Builds one inline chip. `data-label` records the text that was rendered, so\n * `normalizeEditable` can tell a chip the user broke into apart from a chip\n * whose label merely went stale after `tokenGroups` changed.\n */\nexport function createChipElement(id: string, ctx: TemplateChipContext, doc: Document = document): HTMLElement {\n  const el = doc.createElement('span');\n  const token = ctx.tokenIndex.get(id);\n  const label = chipLabelFor(id, ctx);\n  const showsSample = !!(ctx.preview && token?.sample);\n\n  el.dataset.chip = '';\n  el.dataset.token = id;\n  el.dataset.label = label;\n  if (!token) el.dataset.unknown = 'true';\n  // Set as an attribute, not via the IDL property: the property is unimplemented\n  // in jsdom, so the attribute form is the only one that survives into tests.\n  el.setAttribute('contenteditable', 'false');\n  el.draggable = true;\n  el.spellcheck = false;\n  el.className = [\n    ctx.chipClassName,\n    token ? ctx.knownChipClassName : ctx.unknownChipClassName,\n    showsSample && ctx.previewChipClassName,\n  ]\n    .filter(Boolean)\n    .join(' ');\n  if (ctx.chipStyle) Object.assign(el.style, ctx.chipStyle);\n\n  // The label lives in its own span rather than being the chip's whole text\n  // content, so the remove button can sit beside it without the repair sweep in\n  // `normalizeEditable` mistaking the button for the user having edited the label.\n  const labelEl = doc.createElement('span');\n  labelEl.dataset.chipLabel = '';\n  labelEl.textContent = label;\n  el.appendChild(labelEl);\n\n  if (ctx.showRemove) el.appendChild(createRemoveElement(label, ctx, doc));\n\n  if (!token) el.title = ctx.labels.unknownTokenTitle(id);\n  else if (showsSample) el.title = nameOf(id, token, ctx);\n  return el;\n}\n\n/**\n * Flags the chips a selection covers, for the stylesheet to draw: no engine paints a selection\n * across an atomic chip the same way, and Chromium does not paint it at all. `null` clears them.\n */\nexport function markSelectedChips(\n  root: HTMLElement,\n  selection: { start: number; end: number } | null,\n  prefix: string,\n  suffix: string,\n): void {\n  const selected = new Set(\n    selection && selection.start !== selection.end\n      ? chipsWithin(root, selection.start, selection.end, prefix, suffix)\n      : [],\n  );\n  root\n    .querySelectorAll<HTMLElement>('[data-chip]')\n    .forEach((chip) => chip.toggleAttribute('data-selected', selected.has(chip)));\n}\n\n/** The chips whose tokens lie within serialized-string offsets, e.g. the ones an insertion just placed. */\nexport function chipsWithin(\n  root: HTMLElement,\n  start: number,\n  end: number,\n  prefix: string,\n  suffix: string,\n): HTMLElement[] {\n  // Chips are in the DOM in the order their tokens are in the value.\n  const chips = root.querySelectorAll<HTMLElement>('[data-chip]');\n  return tokenSpans(domToValue(root, prefix, suffix), prefix, suffix).flatMap((span, index) =>\n    span.start >= start && span.end <= end && chips[index] ? [chips[index]] : [],\n  );\n}\n\n/**\n * Serializes a DOM subtree back to the template string. Recursive, so a\n * transient element the browser injected between a mutation and the next\n * `normalizeEditable` still serializes to the right text.\n */\nexport function serializeFragment(node: Node, prefix: string, suffix: string): string {\n  let out = '';\n  node.childNodes.forEach((child) => {\n    if (child.nodeType === Node.TEXT_NODE) {\n      out += (child as Text).data;\n      return;\n    }\n    if (child.nodeType !== Node.ELEMENT_NODE) return;\n    const el = child as HTMLElement;\n    if (isChip(el)) {\n      out += prefix + (el.dataset.token ?? '') + suffix;\n      return;\n    }\n    if (el.nodeName === 'BR') {\n      if (!isBogusBr(el)) out += '\\n';\n      return;\n    }\n    if (isSpacer(el)) {\n      out += (el.textContent ?? '').split(SPACER_TEXT).join('');\n      return;\n    }\n    out += serializeFragment(el, prefix, suffix);\n  });\n  return out;\n}\n\nexport function domToValue(root: HTMLElement, prefix: string, suffix: string): string {\n  return serializeFragment(root, prefix, suffix);\n}\n\n/** Guarantees the last child is a caret parking spot, so a trailing newline, a trailing chip, and an empty field are all caretable. */\nexport function ensureTrailingBr(root: HTMLElement): void {\n  const doc = root.ownerDocument;\n  const last = root.lastChild;\n  if (last && last.nodeName === 'BR') {\n    (last as HTMLElement).dataset.bogus = 'true';\n    return;\n  }\n  const br = doc.createElement('br');\n  br.dataset.bogus = 'true';\n  root.appendChild(br);\n}\n\n/** Replaces the editable's entire content with the rendering of `value`. */\nexport function renderValue(root: HTMLElement, value: string, ctx: TemplateChipContext): void {\n  const doc = root.ownerDocument;\n  const nodes = parseTemplate(value, ctx.prefix, ctx.suffix).map((segment) =>\n    segment.type === 'text'\n      ? (doc.createTextNode(segment.text) as Node)\n      : (createChipElement(segment.id, ctx, doc) as Node),\n  );\n  root.replaceChildren(...nodes);\n  syncSpacers(root);\n  ensureTrailingBr(root);\n}\n\n/**\n * Brings every placed chip in line with a changed context, e.g. a label once\n * `tokenGroups` load or the remove button once `readOnly` toggles, without\n * touching text nodes. The chip node itself is kept and only its attributes and\n * children are swapped: replacing the node would move a caret sitting right\n * after the chip to right before it.\n */\nexport function refreshChips(root: HTMLElement, ctx: TemplateChipContext): void {\n  root.querySelectorAll<HTMLElement>('[data-chip]').forEach((chip) => {\n    const fresh = createChipElement(chip.dataset.token ?? '', ctx, root.ownerDocument);\n    // State the editable keeps on a placed chip, which a freshly built one does not have.\n    ['data-dragging', 'data-selected'].forEach((name) => {\n      const state = chip.getAttribute(name);\n      if (state !== null) fresh.setAttribute(name, state);\n    });\n    if (fresh.isEqualNode(chip)) return;\n    Array.from(chip.attributes).forEach((attribute) => chip.removeAttribute(attribute.name));\n    Array.from(fresh.attributes).forEach((attribute) => chip.setAttribute(attribute.name, attribute.value));\n    chip.replaceChildren(...Array.from(fresh.childNodes));\n  });\n}\n\nlet morphCount = 0;\n\nfunction textHeight(root: HTMLElement): number {\n  const range = root.ownerDocument.createRange();\n  range.selectNodeContents(root);\n  return typeof range.getBoundingClientRect === 'function' ? range.getBoundingClientRect().height : 0;\n}\n\n/**\n * Runs `update`, which changes what the chips show, as a view transition in which each chip\n * morphs from its old box into its new one. Where the engine has no view transitions, or none\n * that take a class to style them by, or the swap changes how many lines the text takes, `update`\n * simply takes effect.\n */\nexport function morphChips(root: HTMLElement, transitionClass: string, update: () => void): void {\n  const doc = root.ownerDocument;\n  if (!doc.startViewTransition || !doc.defaultView?.CSS?.supports('view-transition-class', 'a')) {\n    update();\n    return;\n  }\n  // A named element is drawn above the whole page for the transition, so a chip scrolled out of\n  // the field would show outside it. Those cross-fade with the rest of the page instead.\n  const box = root.getBoundingClientRect();\n  const chips = Array.from(root.querySelectorAll<HTMLElement>('[data-chip]')).filter((chip) => {\n    const rect = chip.getBoundingClientRect();\n    return rect.top >= box.top && rect.bottom <= box.bottom && rect.left >= box.left && rect.right <= box.right;\n  });\n  morphCount += 1;\n  const run = morphCount;\n  const nameOfChip = (index: number) => `template-editor-chip-${run}-${index}`;\n  const name = () =>\n    chips.forEach((chip, index) => {\n      chip.style.setProperty('view-transition-name', nameOfChip(index));\n      chip.style.setProperty('view-transition-class', transitionClass);\n    });\n  const unname = () =>\n    chips.forEach((chip, index) => {\n      // A toggle pressed again mid-transition has named the chip for its own.\n      if (chip.style.getPropertyValue('view-transition-name') !== nameOfChip(index)) return;\n      chip.style.removeProperty('view-transition-name');\n      chip.style.removeProperty('view-transition-class');\n      if (!chip.getAttribute('style')) chip.removeAttribute('style');\n    });\n\n  name();\n  const height = textHeight(root);\n  const halfLine = parseFloat(doc.defaultView.getComputedStyle(root).fontSize) / 2;\n  const transition = doc.startViewTransition(() => {\n    update();\n    // Only chips morph; text cross-fades, so a swap that changes how many lines the text takes\n    // showed every line after the change twice. That one happens at once instead.\n    if (Math.abs(textHeight(root) - height) > halfLine) transition.skipTransition();\n    // `update` rebuilds the chips' attributes, and the names with them.\n    name();\n  });\n  // Rejects when the transition is skipped, which is a swap without the morph rather than an error.\n  transition.ready.catch(() => {});\n  transition.finished.then(unname, unname);\n}\n\n/**\n * Converts any complete `prefix…suffix` run sitting in a text node into a chip.\n * This is what makes typing a placeholder out by hand work, and it is the\n * graceful degradation for the autocomplete popup.\n */\nexport function autoTokenize(root: HTMLElement, ctx: TemplateChipContext): boolean {\n  const doc = root.ownerDocument;\n  let changed = false;\n  Array.from(root.childNodes).forEach((node) => {\n    if (node.nodeType !== Node.TEXT_NODE) return;\n    const segments = parseTemplate((node as Text).data, ctx.prefix, ctx.suffix);\n    if (!segments.some((segment) => segment.type === 'token')) return;\n    const fragment = doc.createDocumentFragment();\n    segments.forEach((segment) => {\n      fragment.appendChild(\n        segment.type === 'text' ? doc.createTextNode(segment.text) : createChipElement(segment.id, ctx, doc),\n      );\n    });\n    node.parentNode?.replaceChild(fragment, node);\n    changed = true;\n  });\n  return changed;\n}\n\nfunction isBlockElement(el: HTMLElement): boolean {\n  return ['DIV', 'P', 'LI', 'TR', 'SECTION', 'ARTICLE', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6'].includes(el.nodeName);\n}\n\n/** Whether only empty text nodes follow, i.e. this node is last once the sweep has cleaned up. */\nfunction isLastMeaningful(node: Node): boolean {\n  let next = node.nextSibling;\n  while (next) {\n    if (!(next.nodeType === Node.TEXT_NODE && (next as Text).data === '')) return false;\n    next = next.nextSibling;\n  }\n  return true;\n}\n\n/**\n * Repairs the editable back to its invariants: a flat list of text nodes and\n * chips, ending in a bogus `<br>`. Browsers inject wrappers on paste, drop, and\n * on any editing command we failed to intercept, so this runs before every\n * serialization. Returns whether anything changed, so the caller knows to\n * restore the caret.\n */\nexport function normalizeEditable(root: HTMLElement, ctx: TemplateChipContext): boolean {\n  const doc = root.ownerDocument;\n  let changed = false;\n\n  const visit = (parent: Node): void => {\n    Array.from(parent.childNodes).forEach((node) => {\n      if (node.nodeType === Node.TEXT_NODE) {\n        if ((node as Text).data === '') {\n          node.parentNode?.removeChild(node);\n          changed = true;\n        }\n        return;\n      }\n      if (node.nodeType !== Node.ELEMENT_NODE) {\n        node.parentNode?.removeChild(node);\n        changed = true;\n        return;\n      }\n      const el = node as HTMLElement;\n\n      if (isChip(el)) {\n        // A chip whose label no longer matches what we rendered was edited into\n        // by the user; there is no sane partial state, so it goes. Compared\n        // against the label node specifically, so the remove button's own text\n        // never reads as an edit.\n        const labelEl = labelElementOf(el);\n        if (!labelEl || labelEl.textContent !== (el.dataset.label ?? '')) {\n          el.remove();\n          changed = true;\n        }\n        return;\n      }\n\n      if (isSpacer(el)) {\n        if (el.textContent === SPACER_TEXT) return;\n        // Typed into: what was typed is ordinary text. `syncSpacers` puts a\n        // fresh spacer back if the chip still starts its line.\n        el.replaceWith(doc.createTextNode((el.textContent ?? '').split(SPACER_TEXT).join('')));\n        changed = true;\n        return;\n      }\n\n      if (el.nodeName === 'BR') {\n        // Only the very last `<br>` is the caret parking spot. One left mid-document\n        // would render a line break while serializing to nothing, so it becomes a\n        // real newline instead.\n        if (parent === root && isLastMeaningful(el)) return;\n        el.replaceWith(doc.createTextNode('\\n'));\n        changed = true;\n        return;\n      }\n\n      visit(el);\n      const fragment = doc.createDocumentFragment();\n      if (isBlockElement(el) && el.previousSibling) fragment.appendChild(doc.createTextNode('\\n'));\n      while (el.firstChild) fragment.appendChild(el.firstChild);\n      el.replaceWith(fragment);\n      changed = true;\n    });\n  };\n\n  visit(root);\n  if (autoTokenize(root, ctx)) changed = true;\n\n  const before = root.childNodes.length;\n  root.normalize();\n  if (root.childNodes.length !== before) changed = true;\n\n  if (syncSpacers(root)) changed = true;\n  if (!root.lastChild || root.lastChild.nodeName !== 'BR') changed = true;\n  ensureTrailingBr(root);\n  return changed;\n}\n\n/**\n * Moves a selection endpoint that a browser placed inside a chip out to one of\n * the chip's edges, so all offset arithmetic happens between atomic units.\n */\nexport function snapOutOfChip(\n  root: HTMLElement,\n  node: Node,\n  offset: number,\n  prefer: SnapPreference = 'after',\n): BoundaryPoint {\n  const chip = closestChip(root, node);\n  if (!chip || !chip.parentNode) return { node, offset };\n  const parent = chip.parentNode;\n  const index = Array.prototype.indexOf.call(parent.childNodes, chip);\n  const after = node === chip ? offset > 0 : prefer === 'after';\n  return { node: parent, offset: after ? index + 1 : index };\n}\n\n/** The serialized-string offset of a DOM boundary point. */\nexport function offsetOfPoint(root: HTMLElement, node: Node, offset: number, prefix: string, suffix: string): number {\n  const probe = root.ownerDocument.createRange();\n  probe.setStart(root, 0);\n  probe.setEnd(node, offset);\n  // Serializing a clone of the span reuses the exact walker `domToValue` uses,\n  // so caret offsets and value offsets cannot drift apart.\n  return serializeFragment(probe.cloneContents(), prefix, suffix).length;\n}\n\nfunction selectionOf(root: HTMLElement): Selection | null {\n  return root.ownerDocument.getSelection();\n}\n\n/** The current selection as `[start, end]` offsets into the serialized string, with any chip endpoints expanded to whole chips. */\nexport function getSelectionOffsets(\n  root: HTMLElement,\n  prefix: string,\n  suffix: string,\n): { start: number; end: number } | null {\n  const selection = selectionOf(root);\n  if (!selection || selection.rangeCount === 0) return null;\n  const range = selection.getRangeAt(0);\n  if (!root.contains(range.startContainer) || !root.contains(range.endContainer)) return null;\n\n  const startPoint = snapOutOfChip(root, range.startContainer, range.startOffset, range.collapsed ? 'after' : 'before');\n  const endPoint = snapOutOfChip(root, range.endContainer, range.endOffset, 'after');\n  const start = offsetOfPoint(root, startPoint.node, startPoint.offset, prefix, suffix);\n  const end = offsetOfPoint(root, endPoint.node, endPoint.offset, prefix, suffix);\n  return start <= end ? { start, end } : { start: end, end: start };\n}\n\n/** The caret position as a serialized-string offset, or `null` when the selection isn't in this editable. */\nexport function getCaretOffset(root: HTMLElement, prefix: string, suffix: string): number | null {\n  return getSelectionOffsets(root, prefix, suffix)?.start ?? null;\n}\n\n/** Resolves a serialized-string offset back to a DOM boundary point. */\nfunction locate(root: HTMLElement, target: number, prefix: string, suffix: string): BoundaryPoint {\n  const children = Array.from(root.childNodes);\n  let remaining = Math.max(0, target);\n\n  for (let index = 0; index < children.length; index += 1) {\n    const node = children[index];\n    if (node.nodeType === Node.TEXT_NODE) {\n      const length = (node as Text).data.length;\n      // The end of a line's text is also the start of the next line, where a\n      // spacer, when there is one, is the position that works.\n      if (remaining < length || (remaining === length && !isSpacer(node.nextSibling)))\n        return { node, offset: remaining };\n      remaining -= length;\n      continue;\n    }\n    if (node.nodeType !== Node.ELEMENT_NODE) continue;\n    const el = node as HTMLElement;\n    if (isBogusBr(el)) continue;\n    if (isSpacer(el)) {\n      if (remaining === 0 && el.firstChild) return { node: el.firstChild, offset: (el.firstChild as Text).length };\n      continue;\n    }\n\n    const length = isChip(el)\n      ? tokenLength(el.dataset.token ?? '', prefix, suffix)\n      : el.nodeName === 'BR'\n        ? 1\n        : (el.textContent?.length ?? 0);\n\n    // Never land inside an atomic node: snap to whichever edge is nearer.\n    if (remaining < length) return { node: root, offset: remaining === 0 ? index : index + 1 };\n    remaining -= length;\n  }\n\n  const last = root.lastChild;\n  if (last && isBogusBr(last)) return { node: root, offset: children.length - 1 };\n  return { node: root, offset: children.length };\n}\n\n/** Places the caret (or a range) at serialized-string offsets. */\nexport function setSelectionOffsets(\n  root: HTMLElement,\n  start: number,\n  end: number,\n  prefix: string,\n  suffix: string,\n): void {\n  const doc = root.ownerDocument;\n  const range = doc.createRange();\n  const from = locate(root, start, prefix, suffix);\n  const to = start === end ? from : locate(root, end, prefix, suffix);\n  range.setStart(from.node, from.offset);\n  range.setEnd(to.node, to.offset);\n  const selection = selectionOf(root);\n  selection?.removeAllRanges();\n  selection?.addRange(range);\n}\n\n/**\n * Resolves viewport coordinates to a caret range, across both the standard API\n * and the WebKit-prefixed one. Returns `null` where neither exists, which is\n * both older Safari and jsdom, so every caller needs a fallback.\n */\nexport function caretFromPoint(doc: Document, x: number, y: number): Range | null {\n  const api = doc as Document & {\n    caretPositionFromPoint?: (x: number, y: number) => { offsetNode: Node; offset: number } | null;\n    caretRangeFromPoint?: (x: number, y: number) => Range | null;\n  };\n  if (typeof api.caretPositionFromPoint === 'function') {\n    const position = api.caretPositionFromPoint(x, y);\n    if (!position) return null;\n    const range = doc.createRange();\n    range.setStart(position.offsetNode, position.offset);\n    range.collapse(true);\n    return range;\n  }\n  if (typeof api.caretRangeFromPoint === 'function') return api.caretRangeFromPoint(x, y);\n  return null;\n}\n\nfunction isRtl(root: HTMLElement): boolean {\n  const view = root.ownerDocument.defaultView;\n  return !!view && view.getComputedStyle(root).direction === 'rtl';\n}\n\n/**\n * The serialized-string offset under a pointer, used for drops. A point inside\n * a chip snaps to the nearer edge, which is the far edge in RTL.\n */\nexport function offsetFromPoint(\n  root: HTMLElement,\n  x: number,\n  y: number,\n  prefix: string,\n  suffix: string,\n): number | null {\n  const range = caretFromPoint(root.ownerDocument, x, y);\n  if (!range || !root.contains(range.startContainer)) return null;\n\n  let node: Node = range.startContainer;\n  let offset = range.startOffset;\n  const chip = closestChip(root, node);\n  if (chip?.parentNode) {\n    const index = Array.prototype.indexOf.call(chip.parentNode.childNodes, chip);\n    node = chip.parentNode;\n    offset = isAfterChip(root, chip, x, y) ? index + 1 : index;\n  }\n  return offsetOfPoint(root, node, offset, prefix, suffix);\n}\n\n/**\n * Whether a pointer over a chip is nearer its far edge, which is its left edge in RTL. A sample in\n * the preview wraps, so this is measured along its lines: across its box, a click on the start of\n * its last line counted as before it.\n */\nexport function isAfterChip(root: HTMLElement, chip: HTMLElement, x: number, y: number): boolean {\n  const fragments = Array.from(chip.getClientRects()).filter((rect) => rect.width > 0);\n  const lines = fragments.length > 1 ? fragments : [chip.getBoundingClientRect()];\n  const distance = (line: DOMRect) => Math.max(line.top - y, y - line.bottom, 0);\n  const hit = lines.reduce((nearest, line, index) => (distance(line) < distance(lines[nearest]) ? index : nearest), 0);\n  const line = lines[hit];\n  const into = Math.min(Math.max(isRtl(root) ? line.right - x : x - line.left, 0), line.width);\n  const before = lines.slice(0, hit).reduce((sum, { width }) => sum + width, 0);\n  const total = lines.reduce((sum, { width }) => sum + width, 0);\n  return before + into > total / 2;\n}\n\n/** A zero-width caret at a box's near or far edge, which in RTL are its right and left. */\nfunction caretAtEdge(root: HTMLElement, rect: DOMRect | undefined, far: boolean): DOMRect | null {\n  return rect ? new DOMRect(far !== isRtl(root) ? rect.right : rect.left, rect.top, 0, rect.height) : null;\n}\n\n/**\n * Where a caret is drawn when the engines' own rect for it is missing or wrong, taken from what it\n * sits against. After a chip ending a line and at the start of a line, Chromium reported no rect,\n * and Firefox put a line start at the end of the line before; in front of the trailing `<br>`, so in\n * an empty field or on a last empty line, all three reported none.\n */\nfunction caretRectBeside(root: HTMLElement, { node, offset }: BoundaryPoint): DOMRect | null {\n  const text = node.nodeType === Node.TEXT_NODE ? (node as Text).data : null;\n\n  const before = text === null ? node.childNodes[offset - 1] : offset === 0 ? node.previousSibling : null;\n  if (isChip(before)) {\n    const rects = (before as HTMLElement).getClientRects();\n    return caretAtEdge(root, rects[rects.length - 1], true);\n  }\n\n  const range = root.ownerDocument.createRange();\n  const measure = () =>\n    typeof range.getClientRects === 'function' ? caretAtEdge(root, range.getClientRects()[0], false) : null;\n  if (text !== null) {\n    if (offset === 0 || text[offset - 1] !== '\\n') return null;\n    if (offset < text.length) {\n      range.setStart(node, offset);\n      range.setEnd(node, offset + 1);\n      return measure();\n    }\n  }\n  const next = text === null ? node.childNodes[offset] : node.nextSibling;\n  if (!next || !isBogusBr(next)) return null;\n  range.selectNode(next);\n  return measure();\n}\n\n/**\n * A caret after a character, at that character's far edge. Only a fallback, for where Chromium\n * reports no rect of its own: after a space that ends a line.\n */\nfunction caretRectAfterCharacter(root: HTMLElement, { node, offset }: BoundaryPoint): DOMRect | null {\n  if (node.nodeType !== Node.TEXT_NODE || offset === 0) return null;\n  const range = root.ownerDocument.createRange();\n  range.setStart(node, offset - 1);\n  range.setEnd(node, offset);\n  const rects = typeof range.getClientRects === 'function' ? range.getClientRects() : null;\n  return caretAtEdge(root, rects?.[rects.length - 1], true);\n}\n\n/** The client rect of a serialized-string range, used to place the autocomplete anchor and the drop caret. */\nexport function rectOfOffsets(\n  root: HTMLElement,\n  start: number,\n  end: number,\n  prefix: string,\n  suffix: string,\n): DOMRect | null {\n  const doc = root.ownerDocument;\n  const range = doc.createRange();\n  const from = locate(root, start, prefix, suffix);\n  const to = start === end ? from : locate(root, end, prefix, suffix);\n  const beside = start === end ? caretRectBeside(root, from) : null;\n  if (beside) return beside;\n  range.setStart(from.node, from.offset);\n  range.setEnd(to.node, to.offset);\n  // Range geometry is layout, which jsdom has none of: both methods can be\n  // missing entirely. Every caller treats `null` as \"no position known\".\n  const rects = typeof range.getClientRects === 'function' ? range.getClientRects() : null;\n  // The last rect of a wrapped range is the one nearest the caret; a collapsed\n  // range often reports no rects at all, in which case the bounding box (which\n  // browsers still fill in from the containing text) is the better guess.\n  if (rects && rects.length > 0) return rects[rects.length - 1];\n  if (typeof range.getBoundingClientRect !== 'function') return null;\n  const bounding = range.getBoundingClientRect();\n  if (bounding.width || bounding.height || bounding.top || bounding.left) return bounding;\n  return start === end ? caretRectAfterCharacter(root, from) : null;\n}\n\n/**\n * Scrolls the editable so the offset is visible, if it isn't already. Needed\n * because none of the ways this component moves the caret scroll on their own:\n * assigning a selection never scrolls, and `focus()` scrolls to wherever the\n * caret was *before* the content was rebuilt.\n */\nexport function scrollOffsetIntoView(\n  root: HTMLElement,\n  offset: number,\n  prefix: string,\n  suffix: string,\n  margin = 8,\n): void {\n  // A collapsed range reports no rect in some engines, so fall back to the one\n  // character before the caret, which occupies the same line.\n  const rect =\n    rectOfOffsets(root, offset, offset, prefix, suffix) ??\n    rectOfOffsets(root, Math.max(0, offset - 1), offset, prefix, suffix);\n  if (!rect) return;\n  const host = root.getBoundingClientRect();\n  if (rect.top < host.top + margin) {\n    root.scrollTop -= host.top + margin - rect.top;\n  } else if (rect.bottom > host.bottom - margin) {\n    root.scrollTop += rect.bottom - (host.bottom - margin);\n  }\n}\n\n/** True when the editable, or anything inside it, currently holds focus. */\nexport function hasFocusWithin(root: HTMLElement): boolean {\n  const active = root.ownerDocument.activeElement;\n  return active === root || (!!active && root.contains(active));\n}\n","import { useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from 'react';\nimport { flushSync } from 'react-dom';\nimport cx from 'classnames';\nimport { faEye } from '@fortawesome/free-solid-svg-icons';\nimport ActionIcon from 'components/common/actionIcon';\nimport BodyEnd from 'components/common/bodyEnd';\n\nimport styles from './templateEditor.module.pcss';\nimport { useOverlay } from '../hooks/useOverlay';\nimport { usePopoverPosition } from '../popover/usePopoverPosition';\nimport { PopoverAlign, PopoverPlacement } from '../popover/popover.types';\nimport { ButtonStyleType } from '../button/button.types';\nimport { ThemeColor } from '../themeProvider/color.types';\nimport TemplateEditorSuggestions from './templateEditorSuggestions';\nimport {\n  chipsWithin,\n  closestChip,\n  domToValue,\n  getCaretOffset,\n  getSelectionOffsets,\n  hasFocusWithin,\n  isAfterChip,\n  markSelectedChips,\n  morphChips,\n  normalizeEditable,\n  offsetFromPoint,\n  offsetOfPoint,\n  rectOfOffsets,\n  scrollOffsetIntoView,\n  refreshChips,\n  renderValue,\n  setSelectionOffsets,\n} from './templateEditor.dom';\nimport {\n  cutChip,\n  filterGroups,\n  flattenTokens,\n  isLineStartBeforeToken,\n  normalizeLineBreaks,\n  openTokenRun,\n  parseTemplate,\n  spacesAround,\n  tokenLength,\n  tokenSpanEndingAt,\n  tokenSpanStartingAt,\n} from './templateEditor.utils';\nimport type { TemplateEditableProps } from './templateEditor.types';\n\n/** Consecutive plain-text edits closer together than this collapse into one undo step. */\nconst HISTORY_COALESCE_MS = 500;\nconst HISTORY_LIMIT = 100;\n\n/**\n * Editing is intercepted on `beforeinput` rather than `keydown` throughout:\n * Android soft keyboards report `keyCode 229` for every key, so Enter and\n * Backspace are simply not identifiable from a key event there. `keydown` is\n * used only for navigation, Escape, and the undo shortcuts.\n */\nconst EVENT_TYPES = [\n  'beforeinput',\n  'input',\n  'keydown',\n  'mousedown',\n  'click',\n  'compositionstart',\n  'compositionend',\n  'paste',\n  'copy',\n  'cut',\n  'dragstart',\n  'dragenter',\n  'dragover',\n  'dragleave',\n  'drop',\n  'dragend',\n] as const;\n\ntype EventType = (typeof EVENT_TYPES)[number];\ntype Handlers = Partial<Record<EventType, (event: never) => void>>;\n\ninterface HistoryEntry {\n  value: string;\n  caret: number;\n}\n\ninterface HistoryState {\n  entries: HistoryEntry[];\n  index: number;\n  lastPushAt: number;\n}\n\nfunction freshHistory(value: string, caret: number): HistoryState {\n  return { entries: [{ value, caret }], index: 0, lastPushAt: 0 };\n}\n\ninterface SuggestState {\n  /** What a pick replaces: the typed run from its opening delimiter, or the selection the menu was opened on. */\n  start: number;\n  end: number;\n  query: string;\n  fromMenu?: boolean;\n}\n\ninterface Offsets {\n  start: number;\n  end: number;\n}\n\nfunction isOneToken(text: string, prefix: string, suffix: string): boolean {\n  const segments = parseTemplate(text, prefix, suffix);\n  return segments.length === 1 && segments[0].type === 'token';\n}\n\nfunction TemplateEditable({\n  value = '',\n  onChange,\n  onFocus,\n  onBlur,\n  className,\n  style,\n  id,\n  tabIndex = 0,\n  readOnly = false,\n  disabled = false,\n  apiRef,\n  dragRef,\n  chipContext,\n  tokenGroups,\n  ariaLabel,\n  multiline = true,\n  rows = 4,\n  autoFocus = false,\n  smartSpacing = true,\n  menuLabel,\n  previewLabel,\n  onTogglePreview,\n  classNames,\n  styles: slotStyles,\n}: TemplateEditableProps) {\n  const wrapperRef = useRef<HTMLDivElement>(null);\n  const editableRef = useRef<HTMLDivElement>(null);\n  const caretAnchorRef = useRef<HTMLSpanElement>(null);\n  const dropCaretRef = useRef<HTMLSpanElement>(null);\n  const popupAnchorRef = useRef<HTMLDivElement>(null);\n  const popupPanelRef = useRef<HTMLDivElement>(null);\n  const noArrowRef = useRef<HTMLElement>(null);\n  const menuButtonRef = useRef<HTMLButtonElement>(null);\n\n  // `null` rather than the initial value, so the first layout effect always\n  // renders the incoming value into the (still empty) editable.\n  const lastEmittedRef = useRef<string | null>(null);\n  const lastSelectionRef = useRef<Offsets | null>(null);\n  const isComposingRef = useRef(false);\n  const pendingResyncRef = useRef<string | null>(null);\n  const dismissedRunRef = useRef<number | null>(null);\n  const historyRef = useRef<HistoryState>({ entries: [], index: -1, lastPushAt: 0 });\n  const chipContextRef = useRef(chipContext);\n  const handlersRef = useRef<Handlers>({});\n\n  const [suggest, setSuggest] = useState<SuggestState | null>(null);\n  const [highlightRequest, setHighlightRequest] = useState<string | null>(null);\n\n  const generatedId = useId();\n  const listId = `${id ?? generatedId}-suggestions`;\n  const optionId = useCallback((tokenId: string) => `${listId}-${tokenId.replace(/\\W/g, '-')}`, [listId]);\n\n  const { prefix, suffix } = chipContext;\n  const editingBlocked = readOnly || disabled;\n\n  const suggestionGroups = useMemo(() => {\n    if (!suggest) return [];\n    return filterGroups(tokenGroups, suggest.query)\n      .map((group) => ({ ...group, tokens: group.tokens.filter((token) => !token.disabled) }))\n      .filter((group) => group.tokens.length > 0);\n  }, [tokenGroups, suggest]);\n  const suggestionTokens = useMemo(() => flattenTokens(suggestionGroups), [suggestionGroups]);\n  const isSuggestOpen = !!suggest && suggestionTokens.length > 0;\n  const isMenuOpen = isSuggestOpen && !!suggest?.fromMenu;\n  const canPick = useMemo(() => flattenTokens(tokenGroups).some((token) => !token.disabled), [tokenGroups]);\n  // Derived rather than stored: a request that no longer matches the filtered\n  // list simply falls back to the first entry, so no effect has to reset it.\n  const highlightedId =\n    highlightRequest && suggestionTokens.some((token) => token.id === highlightRequest)\n      ? highlightRequest\n      : (suggestionTokens[0]?.id ?? null);\n\n  const currentValue = useCallback(\n    () => (editableRef.current ? domToValue(editableRef.current, prefix, suffix) : ''),\n    [prefix, suffix],\n  );\n\n  /**\n   * Where an insertion should land. The live selection wins whenever it is\n   * still inside this field. `lastSelectionRef` covers the rest, which is the\n   * normal case for a palette click: the palette cannot suppress its own\n   * mousedown without also killing drag initiation, so the field really does\n   * blur, and the caret has to be recovered from the last recorded selection.\n   */\n  const resolveOffsets = useCallback((): Offsets => {\n    const el = editableRef.current;\n    const end = el ? domToValue(el, prefix, suffix).length : 0;\n    const live = el ? getSelectionOffsets(el, prefix, suffix) : null;\n    return live ?? lastSelectionRef.current ?? { start: end, end };\n  }, [prefix, suffix]);\n\n  const pushHistory = useCallback((next: string, caret: number, discrete: boolean) => {\n    const history = historyRef.current;\n    history.entries = history.entries.slice(0, history.index + 1);\n    const now = Date.now();\n    const canCoalesce = !discrete && history.index >= 0 && now - history.lastPushAt < HISTORY_COALESCE_MS;\n    if (canCoalesce) {\n      history.entries[history.index] = { value: next, caret };\n    } else {\n      history.entries.push({ value: next, caret });\n      if (history.entries.length > HISTORY_LIMIT) history.entries.shift();\n      history.index = history.entries.length - 1;\n    }\n    history.lastPushAt = now;\n  }, []);\n\n  const commit = useCallback(\n    (next: string, caret: number, options: { history?: 'push' | 'skip'; discrete?: boolean } = {}) => {\n      if (next === lastEmittedRef.current) return;\n      lastEmittedRef.current = next;\n      if (options.history !== 'skip') pushHistory(next, caret, options.discrete ?? false);\n      onChange?.(next);\n    },\n    [onChange, pushHistory],\n  );\n\n  /** Re-renders the editable from a template string and puts the caret at an exact offset. */\n  const applyValue = useCallback(\n    (next: string, caret: number, options: { history?: 'push' | 'skip'; discrete?: boolean } = {}) => {\n      const el = editableRef.current;\n      if (!el) return;\n      // Rebuilding every child can clamp scrollTop, so it is restored before the\n      // caret is revealed; otherwise the field lurches to the top mid-edit.\n      const { scrollTop } = el;\n      renderValue(el, next, chipContextRef.current);\n      el.scrollTop = scrollTop;\n      setSelectionOffsets(el, caret, caret, prefix, suffix);\n      lastSelectionRef.current = { start: caret, end: caret };\n      if (hasFocusWithin(el)) scrollOffsetIntoView(el, caret, prefix, suffix);\n      commit(next, caret, options);\n    },\n    [commit, prefix, suffix],\n  );\n\n  const spliceValue = useCallback(\n    (start: number, end: number, insert: string, discrete = true) => {\n      const value = currentValue();\n      applyValue(value.slice(0, start) + insert + value.slice(end), start + insert.length, { discrete });\n    },\n    [applyValue, currentValue],\n  );\n\n  /** Reads the DOM back after a native edit, repairing it first. */\n  const emit = useCallback(() => {\n    const el = editableRef.current;\n    if (!el) return;\n    const before = getSelectionOffsets(el, prefix, suffix);\n    const changed = normalizeEditable(el, chipContextRef.current);\n    if (changed && before && hasFocusWithin(el)) {\n      setSelectionOffsets(el, before.start, before.end, prefix, suffix);\n    }\n    const next = domToValue(el, prefix, suffix);\n    const selection = getSelectionOffsets(el, prefix, suffix);\n    if (selection) lastSelectionRef.current = selection;\n    commit(next, selection?.start ?? next.length);\n  }, [commit, prefix, suffix]);\n\n  /**\n   * Pops in the chips an insertion or a drop placed between two offsets. Only those: the splice\n   * rebuilt every chip, and a class on all of them would animate the whole template each time.\n   */\n  const markPlaced = useCallback(\n    (start: number, end: number) => {\n      const el = editableRef.current;\n      if (!el) return;\n      chipsWithin(el, start, end, prefix, suffix).forEach((chip) => {\n        chip.classList.add(styles.templateEditorChipInserted);\n        chip.addEventListener('animationend', () => chip.classList.remove(styles.templateEditorChipInserted), {\n          once: true,\n        });\n      });\n    },\n    [prefix, suffix],\n  );\n\n  /**\n   * Puts `text` over `start…end` of `value`, with smart spacing's spaces around it when `spaced`.\n   * The caret goes right after the text, before a space added after it, so punctuation typed next\n   * lands against the chip: `{{name}}, one`, not `{{name}} ,one`.\n   */\n  const place = useCallback(\n    (text: string, start: number, end: number, value: string, spaced: boolean) => {\n      const [before, after] = spaced && smartSpacing ? spacesAround(value, start, end, prefix, suffix) : ['', ''];\n      const from = start + before.length;\n      applyValue(value.slice(0, start) + before + text + after + value.slice(end), from + text.length, {\n        discrete: true,\n      });\n      markPlaced(from, from + text.length);\n    },\n    [applyValue, markPlaced, prefix, smartSpacing, suffix],\n  );\n\n  const insertTokenAt = useCallback(\n    (tokenId: string, start: number, end: number, spaced = true) => {\n      const el = editableRef.current;\n      if (!el || editingBlocked) return;\n      el.focus({ preventScroll: true });\n      place(prefix + tokenId + suffix, start, end, currentValue(), spaced);\n      setSuggest(null);\n      dismissedRunRef.current = null;\n    },\n    [currentValue, editingBlocked, place, prefix, suffix],\n  );\n\n  /**\n   * Opens the popup on the unterminated run the caret sits in, and closes it once\n   * the caret leaves. The menu stays open for exactly as long as the selection it\n   * was opened on, so typing or moving the caret closes it.\n   */\n  const detectSuggest = useCallback(() => {\n    const el = editableRef.current;\n    const selection = el && !editingBlocked && !isComposingRef.current ? getSelectionOffsets(el, prefix, suffix) : null;\n    const caret = selection && selection.start === selection.end ? selection.start : null;\n    const run = el && caret !== null ? openTokenRun(domToValue(el, prefix, suffix), caret, prefix, suffix) : null;\n    if (caret !== null && !run) dismissedRunRef.current = null;\n    const typed =\n      run && caret !== null && dismissedRunRef.current !== run.start\n        ? { start: run.start, end: caret, query: run.query }\n        : null;\n    setSuggest((current) => {\n      if (current?.fromMenu && current.start === selection?.start && current.end === selection?.end) return current;\n      if (!typed) return null;\n      return current && !current.fromMenu && current.start === typed.start && current.query === typed.query\n        ? current\n        : typed;\n    });\n  }, [editingBlocked, prefix, suffix]);\n\n  const applyHistory = useCallback(\n    (direction: -1 | 1) => {\n      const history = historyRef.current;\n      const nextIndex = history.index + direction;\n      if (nextIndex < 0 || nextIndex >= history.entries.length) return;\n      const el = editableRef.current;\n      if (!el) return;\n      history.index = nextIndex;\n      history.lastPushAt = 0;\n      const entry = history.entries[nextIndex];\n      const { scrollTop } = el;\n      renderValue(el, entry.value, chipContextRef.current);\n      el.scrollTop = scrollTop;\n      setSelectionOffsets(el, entry.caret, entry.caret, prefix, suffix);\n      lastSelectionRef.current = { start: entry.caret, end: entry.caret };\n      if (hasFocusWithin(el)) scrollOffsetIntoView(el, entry.caret, prefix, suffix);\n      commit(entry.value, entry.caret, { history: 'skip' });\n      setSuggest(null);\n    },\n    [commit, prefix, suffix],\n  );\n\n  /** The offsets a placed chip occupies, used as the source span of a chip drag. */\n  const spanOfChip = useCallback(\n    (chip: HTMLElement): Offsets | null => {\n      const el = editableRef.current;\n      if (!el || !chip.parentNode) return null;\n      const index = Array.prototype.indexOf.call(chip.parentNode.childNodes, chip);\n      const start = offsetOfPoint(el, chip.parentNode, index, prefix, suffix);\n      return { start, end: start + tokenLength(chip.dataset.token ?? '', prefix, suffix) };\n    },\n    [prefix, suffix],\n  );\n\n  const showDropCaret = useCallback(\n    (offset: number | null) => {\n      const el = editableRef.current;\n      const caret = dropCaretRef.current;\n      const wrapper = wrapperRef.current;\n      if (!el || !caret || !wrapper) return;\n      const rect = offset == null ? null : rectOfOffsets(el, offset, offset, prefix, suffix);\n      if (!rect) {\n        caret.classList.remove(styles.templateEditorDropCaretVisible);\n        return;\n      }\n      const host = wrapper.getBoundingClientRect();\n      caret.style.left = `${rect.left - host.left}px`;\n      caret.style.top = `${rect.top - host.top}px`;\n      caret.style.height = `${rect.height || 16}px`;\n      caret.classList.add(styles.templateEditorDropCaretVisible);\n    },\n    [prefix, suffix],\n  );\n\n  const commitSuggestion = useCallback(\n    (tokenId: string) => {\n      // A run typed out is literal, as all typing is: only a pick from the menu is spaced.\n      if (suggest) insertTokenAt(tokenId, suggest.start, suggest.end, !!suggest.fromMenu);\n    },\n    [insertTokenAt, suggest],\n  );\n\n  const toggleMenu = useCallback(() => {\n    const el = editableRef.current;\n    if (!el || editingBlocked) return;\n    if (suggest?.fromMenu) {\n      setSuggest(null);\n      return;\n    }\n    const { start, end } = resolveOffsets();\n    el.focus({ preventScroll: true });\n    setSelectionOffsets(el, start, end, prefix, suffix);\n    lastSelectionRef.current = { start, end };\n    setHighlightRequest(null);\n    setSuggest({ start, end, query: '', fromMenu: true });\n  }, [editingBlocked, prefix, resolveOffsets, suffix, suggest?.fromMenu]);\n\n  // ---------------------------------------------------------------- handlers\n\n  const handlers: Handlers = {\n    beforeinput: (event: InputEvent) => {\n      const el = editableRef.current;\n      if (!el) return;\n      if (editingBlocked) {\n        event.preventDefault();\n        return;\n      }\n      const { inputType } = event;\n\n      if (inputType === 'historyUndo' || inputType === 'historyRedo') {\n        event.preventDefault();\n        applyHistory(inputType === 'historyUndo' ? -1 : 1);\n        return;\n      }\n      // Paste and drop are handled by their own events, which carry the data. `deleteByDrag` too:\n      // `dragend` removes what was moved out, and the browser's own removal on top of it took\n      // the same span out a second time.\n      if (inputType === 'insertFromPaste' || inputType === 'insertFromDrop' || inputType === 'deleteByDrag') {\n        event.preventDefault();\n        return;\n      }\n      if (inputType === 'insertParagraph' || inputType === 'insertLineBreak') {\n        event.preventDefault();\n        if (!multiline) return;\n        const selection = getSelectionOffsets(el, prefix, suffix);\n        const start = selection?.start ?? domToValue(el, prefix, suffix).length;\n        spliceValue(start, selection?.end ?? start, '\\n');\n        return;\n      }\n      if (inputType === 'deleteContentBackward' || inputType === 'deleteContentForward') {\n        const selection = getSelectionOffsets(el, prefix, suffix);\n        if (!selection) return;\n        if (selection.start !== selection.end) {\n          event.preventDefault();\n          spliceValue(selection.start, selection.end, '');\n          return;\n        }\n        // A chip is one unit: never let the browser nibble at its label.\n        const current = domToValue(el, prefix, suffix);\n        const backward = inputType === 'deleteContentBackward';\n        const span = backward\n          ? tokenSpanEndingAt(current, selection.start, prefix, suffix)\n          : tokenSpanStartingAt(current, selection.start, prefix, suffix);\n        if (span) {\n          event.preventDefault();\n          spliceValue(span.start, span.end, '');\n        } else if (backward && isLineStartBeforeToken(current, selection.start, prefix, suffix)) {\n          // Left to the browser, this deletes the spacer's invisible character and nothing else.\n          event.preventDefault();\n          spliceValue(selection.start - 1, selection.start, '');\n        } else {\n          // A character whose removal leaves a chip starting its line, where it gets a spacer: left to\n          // the browser, the caret ended up after the chip in Chromium and on the line above in WebKit.\n          // Half a surrogate pair is left to it, since it removes the whole character.\n          const at = backward ? selection.start - 1 : selection.start;\n          const unit = current.charCodeAt(at);\n          const without = current.slice(0, at) + current.slice(at + 1);\n          if (unit && (unit < 0xd800 || unit > 0xdfff) && isLineStartBeforeToken(without, at, prefix, suffix)) {\n            event.preventDefault();\n            spliceValue(at, at + 1, '');\n          }\n        }\n      }\n    },\n\n    input: () => {\n      if (isComposingRef.current) return;\n      emit();\n      detectSuggest();\n    },\n\n    keydown: (event: KeyboardEvent) => {\n      const el = editableRef.current;\n      if (!el) return;\n\n      if (isSuggestOpen) {\n        if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n          event.preventDefault();\n          const index = suggestionTokens.findIndex((token) => token.id === highlightedId);\n          const delta = event.key === 'ArrowDown' ? 1 : -1;\n          const next = (index + delta + suggestionTokens.length) % suggestionTokens.length;\n          setHighlightRequest(suggestionTokens[next]?.id ?? null);\n          return;\n        }\n        if (event.key === 'Enter' || event.key === 'Tab') {\n          const tokenId = highlightedId ?? suggestionTokens[0]?.id;\n          if (tokenId) {\n            event.preventDefault();\n            commitSuggestion(tokenId);\n          }\n          return;\n        }\n      }\n\n      // Undo and redo: Safari does not reliably emit the beforeinput history types.\n      const mod = event.metaKey || event.ctrlKey;\n      if (mod && (event.key === 'z' || event.key === 'Z')) {\n        event.preventDefault();\n        if (!editingBlocked) applyHistory(event.shiftKey ? 1 : -1);\n        return;\n      }\n      if (mod && (event.key === 'y' || event.key === 'Y')) {\n        event.preventDefault();\n        if (!editingBlocked) applyHistory(1);\n        return;\n      }\n\n      if (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight') return;\n      if (event.shiftKey || event.altKey || mod) return;\n      const selection = getSelectionOffsets(el, prefix, suffix);\n      if (!selection || selection.start !== selection.end) return;\n      const view = el.ownerDocument.defaultView;\n      const rtl = !!view && view.getComputedStyle(el).direction === 'rtl';\n      const backwards = rtl ? event.key === 'ArrowRight' : event.key === 'ArrowLeft';\n      const current = domToValue(el, prefix, suffix);\n      const span = backwards\n        ? tokenSpanEndingAt(current, selection.start, prefix, suffix)\n        : tokenSpanStartingAt(current, selection.start, prefix, suffix);\n      const step = backwards ? selection.start - 1 : selection.start + 1;\n      // Onto or off a line's spacer: natively its zero-width character is a stop\n      // of its own, a press that moves nothing on screen.\n      const viaSpacer =\n        isLineStartBeforeToken(current, selection.start, prefix, suffix) ||\n        isLineStartBeforeToken(current, step, prefix, suffix);\n      if (!span && !viaSpacer) return;\n      // Step over the chip in a single press, landing on its far edge. Left to\n      // the browser, an atomic `contenteditable=false` element yields an extra,\n      // invisible stop that reads as the caret disappearing into the chip.\n      // Deleting a whole chip does not depend on this: `beforeinput` removes the\n      // entire placeholder from either edge on one Backspace or Delete. Shift is\n      // excluded above, so extending a selection over a chip stays native.\n      event.preventDefault();\n      const target = span ? (backwards ? span.start : span.end) : step;\n      setSelectionOffsets(el, target, target, prefix, suffix);\n      lastSelectionRef.current = { start: target, end: target };\n    },\n\n    // Chips are imperative DOM, so presses on them and on their remove buttons\n    // are delegated from the editable rather than bound per chip.\n    mousedown: (event: MouseEvent) => {\n      // Stops the press from moving the caret into the chip or starting a drag.\n      if ((event.target as HTMLElement | null)?.closest?.('[data-chip-remove]')) event.preventDefault();\n    },\n\n    click: (event: MouseEvent) => {\n      const el = editableRef.current;\n      if (!el || editingBlocked) return;\n      const target = event.target as HTMLElement | null;\n      const chip = closestChip(el, target);\n      const span = chip ? spanOfChip(chip) : null;\n      if (!chip || !span) return;\n      el.focus({ preventScroll: true });\n      if (target?.closest?.('[data-chip-remove]')) {\n        event.preventDefault();\n        event.stopPropagation();\n        const { value: next, at } = cutChip(currentValue(), span.start, span.end, smartSpacing);\n        applyValue(next, at, { discrete: true });\n        return;\n      }\n      // A chip has no caret positions of its own, and with no margin beside it the\n      // text between two chips is zero wide. So the chip is the click target for\n      // both of its sides, and the half nearer the pointer decides which.\n      const offset = isAfterChip(el, chip, event.clientX, event.clientY) ? span.end : span.start;\n      setSelectionOffsets(el, offset, offset, prefix, suffix);\n      lastSelectionRef.current = { start: offset, end: offset };\n    },\n\n    compositionstart: () => {\n      isComposingRef.current = true;\n      setSuggest(null);\n    },\n\n    compositionend: () => {\n      isComposingRef.current = false;\n      const pending = pendingResyncRef.current;\n      pendingResyncRef.current = null;\n      const el = editableRef.current;\n      if (pending !== null && el) {\n        renderValue(el, pending, chipContextRef.current);\n        lastEmittedRef.current = pending;\n        historyRef.current = freshHistory(pending, pending.length);\n        return;\n      }\n      emit();\n      detectSuggest();\n    },\n\n    paste: (event: ClipboardEvent) => {\n      event.preventDefault();\n      const el = editableRef.current;\n      if (!el || editingBlocked) return;\n      const text = event.clipboardData?.getData('text/plain') ?? '';\n      const selection = getSelectionOffsets(el, prefix, suffix);\n      const start = selection?.start ?? domToValue(el, prefix, suffix).length;\n      spliceValue(start, selection?.end ?? start, normalizeLineBreaks(text, multiline));\n    },\n\n    copy: (event: ClipboardEvent) => {\n      const el = editableRef.current;\n      if (!el) return;\n      const selection = getSelectionOffsets(el, prefix, suffix);\n      if (!selection || selection.start === selection.end) return;\n      // Without this the clipboard gets a chip's *label*, so a copy and paste\n      // would silently turn a placeholder into ordinary prose.\n      event.preventDefault();\n      event.clipboardData?.setData('text/plain', domToValue(el, prefix, suffix).slice(selection.start, selection.end));\n    },\n\n    cut: (event: ClipboardEvent) => {\n      const el = editableRef.current;\n      if (!el) return;\n      const selection = getSelectionOffsets(el, prefix, suffix);\n      if (!selection || selection.start === selection.end) return;\n      event.preventDefault();\n      event.clipboardData?.setData('text/plain', domToValue(el, prefix, suffix).slice(selection.start, selection.end));\n      if (editingBlocked) return;\n      spliceValue(selection.start, selection.end, '');\n    },\n\n    dragstart: (event: DragEvent) => {\n      const el = editableRef.current;\n      if (!el || editingBlocked) {\n        event.preventDefault();\n        return;\n      }\n      const chip = closestChip(el, event.target as Node);\n      const span = chip ? spanOfChip(chip) : getSelectionOffsets(el, prefix, suffix);\n      if (!span || span.start === span.end) {\n        event.preventDefault();\n        return;\n      }\n      const text = domToValue(el, prefix, suffix).slice(span.start, span.end);\n      dragRef.current = { kind: 'move', text, start: span.start, end: span.end, source: el };\n      event.dataTransfer?.setData('text/plain', text);\n      if (event.dataTransfer) event.dataTransfer.effectAllowed = 'move';\n      chip?.setAttribute('data-dragging', 'true');\n    },\n\n    // Chrome only needs `dragover` prevented to accept a drop, but Firefox wants\n    // `dragenter` prevented as well before it treats this as a drop target.\n    dragenter: (event: DragEvent) => {\n      const el = editableRef.current;\n      if (!el || editingBlocked) return;\n      if (!dragRef.current && event.dataTransfer && Array.from(event.dataTransfer.types).includes('Files')) return;\n      event.preventDefault();\n      el.setAttribute('data-dragover', 'true');\n    },\n\n    dragover: (event: DragEvent) => {\n      const el = editableRef.current;\n      if (!el || editingBlocked) return;\n      const payload = dragRef.current;\n      // Files belong to DropZone, not here.\n      if (!payload && event.dataTransfer && Array.from(event.dataTransfer.types).includes('Files')) return;\n      event.preventDefault();\n      // A chip dragged out of another editor allows only `move`, and the browser refuses a drop\n      // whose effect the source does not allow: answered with `copy`, it never dropped.\n      if (event.dataTransfer) {\n        const move = payload?.kind === 'move' || event.dataTransfer.effectAllowed === 'move';\n        event.dataTransfer.dropEffect = move ? 'move' : 'copy';\n      }\n      el.setAttribute('data-dragover', 'true');\n      showDropCaret(offsetFromPoint(el, event.clientX, event.clientY, prefix, suffix));\n    },\n\n    dragleave: (event: DragEvent) => {\n      const el = editableRef.current;\n      if (!el) return;\n      if (event.relatedTarget && el.contains(event.relatedTarget as Node)) return;\n      el.removeAttribute('data-dragover');\n      showDropCaret(null);\n    },\n\n    drop: (event: DragEvent) => {\n      const el = editableRef.current;\n      if (!el) return;\n      const payload = dragRef.current;\n      if (!payload && event.dataTransfer && Array.from(event.dataTransfer.types).includes('Files')) return;\n      // Mandatory: without it the browser inserts its own markup into the editable.\n      event.preventDefault();\n      el.removeAttribute('data-dragover');\n      showDropCaret(null);\n      el.querySelector('[data-dragging]')?.removeAttribute('data-dragging');\n      dragRef.current = null;\n      if (editingBlocked) return;\n\n      const current = domToValue(el, prefix, suffix);\n      // Neither caret-from-point API exists in every engine; where it is\n      // missing the drop lands wherever the caret already was.\n      const dropped = offsetFromPoint(el, event.clientX, event.clientY, prefix, suffix) ?? resolveOffsets().start;\n\n      if (payload?.kind === 'insert') {\n        insertTokenAt(payload.id, dropped, dropped);\n        return;\n      }\n      if (payload?.kind === 'move' && payload.source === el) {\n        // Dropped back inside itself: nothing to do.\n        if (dropped >= payload.start && dropped <= payload.end) return;\n        // Spaced as a chip only when it is one: a dragged selection of text moves as it was.\n        const one = isOneToken(payload.text, prefix, suffix);\n        const { value: without } = cutChip(current, payload.start, payload.end, one && smartSpacing);\n        const target = dropped > payload.end ? dropped - (current.length - without.length) : dropped;\n        el.focus({ preventScroll: true });\n        place(payload.text, target, target, without, one);\n        return;\n      }\n      // Anything else: a drag out of another editor, or out of another app.\n      const text = normalizeLineBreaks(event.dataTransfer?.getData('text/plain') ?? '', multiline);\n      if (!text) return;\n      el.focus({ preventScroll: true });\n      place(text, dropped, dropped, current, isOneToken(text, prefix, suffix));\n    },\n\n    dragend: (event: DragEvent) => {\n      const el = editableRef.current;\n      const payload = dragRef.current;\n      dragRef.current = null;\n      el?.removeAttribute('data-dragover');\n      el?.querySelector('[data-dragging]')?.removeAttribute('data-dragging');\n      showDropCaret(null);\n      // Moved somewhere else, such as another editor, so it leaves this one, as dragged text does. A\n      // drop back into this field has already moved it and cleared the payload. Not through\n      // `applyValue`, which would pull the caret back out of the field it was dropped into.\n      if (!el || editingBlocked || payload?.kind !== 'move' || event.dataTransfer?.dropEffect !== 'move') return;\n      const tidy = smartSpacing && isOneToken(payload.text, prefix, suffix);\n      const { value: next, at } = cutChip(domToValue(el, prefix, suffix), payload.start, payload.end, tidy);\n      const { scrollTop } = el;\n      renderValue(el, next, chipContextRef.current);\n      el.scrollTop = scrollTop;\n      lastSelectionRef.current = { start: at, end: at };\n      commit(next, at, { discrete: true });\n    },\n  };\n\n  // Handlers are attached once and dispatched through this ref, so a re-render\n  // never detaches and re-attaches thirteen listeners. Declared before the\n  // attaching effect so the first commit already has them.\n  useEffect(() => {\n    handlersRef.current = handlers;\n    chipContextRef.current = chipContext;\n  });\n\n  useEffect(() => {\n    const el = editableRef.current;\n    if (!el) return;\n    const listeners = EVENT_TYPES.map((type) => {\n      const listener = (event: Event) => handlersRef.current[type]?.(event as never);\n      el.addEventListener(type, listener);\n      return [type, listener] as const;\n    });\n    return () => listeners.forEach(([type, listener]) => el.removeEventListener(type, listener));\n  }, []);\n\n  // The caret only matters while this field has focus, and a document-scoped\n  // listener fires on every selection change anywhere on the page, so it is\n  // registered for exactly as long as it is useful. It writes to a ref, never\n  // to state: the palette must not re-render on every caret move.\n  useEffect(() => {\n    const el = editableRef.current;\n    if (!el) return;\n    const doc = el.ownerDocument;\n    let attached = false;\n\n    const onSelectionChange = () => {\n      const selection = getSelectionOffsets(el, prefix, suffix);\n      if (selection) lastSelectionRef.current = selection;\n      markSelectedChips(el, selection, prefix, suffix);\n      detectSuggest();\n    };\n    const attach = () => {\n      if (attached) return;\n      attached = true;\n      doc.addEventListener('selectionchange', onSelectionChange);\n    };\n    // Cleared here because the listener is gone: the selectionchange of a click elsewhere comes\n    // after the blur, and would otherwise leave the chips drawn selected.\n    const detach = () => {\n      if (!attached) return;\n      attached = false;\n      doc.removeEventListener('selectionchange', onSelectionChange);\n      markSelectedChips(el, null, prefix, suffix);\n    };\n\n    el.addEventListener('focus', attach);\n    el.addEventListener('blur', detach);\n    if (hasFocusWithin(el)) attach();\n    return () => {\n      el.removeEventListener('focus', attach);\n      el.removeEventListener('blur', detach);\n      detach();\n    };\n  }, [detectSuggest, prefix, suffix]);\n\n  // Controlled resync: only when the incoming value is not the one we just emitted.\n  useLayoutEffect(() => {\n    const el = editableRef.current;\n    if (!el) return;\n    const incoming = String(value ?? '');\n    if (incoming === lastEmittedRef.current) return;\n    if (isComposingRef.current) {\n      pendingResyncRef.current = incoming;\n      return;\n    }\n    const caret = hasFocusWithin(el) ? getCaretOffset(el, prefix, suffix) : null;\n    const { scrollTop } = el;\n    renderValue(el, incoming, chipContextRef.current);\n    el.scrollTop = scrollTop;\n    lastEmittedRef.current = incoming;\n    const clamped = caret === null ? incoming.length : Math.min(caret, incoming.length);\n    // A caret from the old value means nothing in the new one, and an insertion there could land\n    // inside one of its tokens and split it: unfocused, the next one appends instead. Focused, it\n    // is read back from the selection, which has been snapped out of any chip.\n    lastSelectionRef.current = null;\n    if (caret !== null) {\n      setSelectionOffsets(el, clamped, clamped, prefix, suffix);\n      lastSelectionRef.current = getSelectionOffsets(el, prefix, suffix);\n      scrollOffsetIntoView(el, clamped, prefix, suffix);\n    }\n    // A value set from outside starts a new history, as it does in a native\n    // textarea. Keeping the old entries would let undo restore text from before\n    // a form reset, a state the field never held right before this one.\n    historyRef.current = freshHistory(incoming, clamped);\n  }, [prefix, suffix, value]);\n\n  // A layout effect, so a preview toggled under `flushSync` has its chips refreshed by the time\n  // `morphChips` lets the browser capture them.\n  useLayoutEffect(() => {\n    const el = editableRef.current;\n    if (el) refreshChips(el, chipContext);\n  }, [chipContext]);\n\n  // `focus()` alone lands the caret at the start in some engines and at the end\n  // in others, so the position is set explicitly afterwards. Mount only:\n  // refocusing on a later prop change would fight the user.\n  useEffect(() => {\n    const el = editableRef.current;\n    if (!autoFocus || !el || editingBlocked) return;\n    el.focus({ preventScroll: true });\n    const end = domToValue(el, prefix, suffix).length;\n    setSelectionOffsets(el, end, end, prefix, suffix);\n    lastSelectionRef.current = { start: end, end };\n    scrollOffsetIntoView(el, end, prefix, suffix);\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, []);\n\n  useEffect(() => {\n    if (!apiRef) return;\n    apiRef.current = {\n      insertToken: (tokenId) => {\n        const { start, end } = resolveOffsets();\n        insertTokenAt(tokenId, start, end);\n      },\n      highlightToken: (tokenId) => {\n        editableRef.current\n          ?.querySelectorAll<HTMLElement>('[data-chip]')\n          .forEach((chip) => chip.toggleAttribute('data-highlighted', chip.dataset.token === tokenId));\n      },\n    };\n    return () => {\n      apiRef.current = null;\n    };\n  }, [apiRef, insertTokenAt, resolveOffsets]);\n\n  // Anchor the popup to the run being typed rather than to the collapsed caret:\n  // a collapsed range reports no client rects in most engines, a run always has one.\n  // The menu anchors to its whole button: a zero-width point on the button's right\n  // edge sits on the field's clipping edge, where the popup counts it as scrolled\n  // out of view and never shows.\n  useLayoutEffect(() => {\n    const el = editableRef.current;\n    const anchor = caretAnchorRef.current;\n    const wrapper = wrapperRef.current;\n    if (!isSuggestOpen || !el || !anchor || !wrapper || !suggest) return;\n    const rect = suggest.fromMenu\n      ? (menuButtonRef.current?.getBoundingClientRect() ?? null)\n      : rectOfOffsets(el, suggest.start, suggest.end, prefix, suffix);\n    if (!rect) return;\n    const host = wrapper.getBoundingClientRect();\n    anchor.style.left = `${rect.left - host.left}px`;\n    anchor.style.width = suggest.fromMenu ? `${rect.width}px` : '';\n    anchor.style.top = `${rect.top - host.top}px`;\n    anchor.style.height = `${rect.height || 16}px`;\n  }, [isSuggestOpen, prefix, suffix, suggest]);\n\n  // A layer of its own, so Escape closes the list rather than a Modal or Drawer the field sits in,\n  // whose own Escape handler stands down only for a layer opened above it.\n  useOverlay({\n    isActive: isSuggestOpen,\n    containerRef: popupPanelRef,\n    onEscape: () => {\n      if (!suggest?.fromMenu) dismissedRunRef.current = suggest?.start ?? null;\n      setSuggest(null);\n    },\n    trapFocus: false,\n    lockScroll: false,\n  });\n\n  usePopoverPosition({\n    isOpen: isSuggestOpen,\n    placement: PopoverPlacement.Bottom,\n    align: suggest?.fromMenu ? PopoverAlign.End : PopoverAlign.Start,\n    offset: 0,\n    triggerRef: caretAnchorRef,\n    floatingRef: popupAnchorRef,\n    panelRef: popupPanelRef,\n    arrowRef: noArrowRef,\n  });\n\n  return (\n    <div\n      ref={wrapperRef}\n      className={cx(styles.templateEditorSurface, !multiline && styles.templateEditorSurfaceSingleLine)}\n    >\n      <div\n        {...(editingBlocked ? {} : { contentEditable: true, suppressContentEditableWarning: true })}\n        ref={editableRef}\n        id={id}\n        role='textbox'\n        aria-multiline={multiline}\n        aria-label={ariaLabel}\n        aria-readonly={readOnly || undefined}\n        aria-disabled={disabled || undefined}\n        aria-autocomplete={isSuggestOpen ? 'list' : undefined}\n        aria-controls={isSuggestOpen ? listId : undefined}\n        aria-activedescendant={isSuggestOpen && highlightedId ? optionId(highlightedId) : undefined}\n        tabIndex={disabled ? -1 : tabIndex}\n        spellCheck\n        data-template-editable\n        className={cx(\n          styles.templateEditorEditable,\n          disabled && styles.templateEditorEditableDisabled,\n          className,\n          classNames?.editable,\n        )}\n        // `minHeight` is set inline rather than in the stylesheet because Input's own multiline\n        // rule sets it, and which of two single-class rules wins is a bundling detail.\n        style={{\n          minHeight: multiline ? `calc(${rows} * 1.5em)` : undefined,\n          ...style,\n          ...slotStyles?.editable,\n        }}\n        onFocus={onFocus}\n        onBlur={(event) => {\n          setSuggest(null);\n          onBlur?.(event);\n        }}\n      />\n      {((menuLabel && !editingBlocked) || previewLabel) && (\n        <span className={styles.templateEditorActions}>\n          {previewLabel && (\n            <ActionIcon\n              icon={faEye}\n              styleType={chipContext.preview ? ButtonStyleType.Light : ButtonStyleType.Plain}\n              color={chipContext.preview ? ThemeColor.Primary : 'var(--text-dark)'}\n              aria-label={previewLabel}\n              title={previewLabel}\n              aria-pressed={!!chipContext.preview}\n              className={classNames?.previewButton}\n              style={slotStyles?.previewButton}\n              // Keeps the field's focus and caret, so editing carries on in the preview.\n              onMouseDown={(event) => event.preventDefault()}\n              onClick={() => {\n                const el = editableRef.current;\n                if (el && onTogglePreview) {\n                  morphChips(el, styles.templateEditorChipMorph, () => flushSync(onTogglePreview));\n                }\n              }}\n            />\n          )}\n          {menuLabel && !editingBlocked && (\n            <ActionIcon\n              ref={menuButtonRef}\n              icon={<span aria-hidden='true'>{'{ }'}</span>}\n              color='var(--text-dark)'\n              disabled={!canPick}\n              aria-label={menuLabel}\n              title={menuLabel}\n              aria-haspopup='listbox'\n              aria-expanded={isMenuOpen}\n              aria-controls={isMenuOpen ? listId : undefined}\n              className={classNames?.menuButton}\n              style={slotStyles?.menuButton}\n              // Keeps the field's focus and selection, which is where the pick lands.\n              onMouseDown={(event) => event.preventDefault()}\n              onClick={toggleMenu}\n            />\n          )}\n        </span>\n      )}\n      <span ref={caretAnchorRef} aria-hidden='true' className={styles.templateEditorCaretAnchor} />\n      <span ref={dropCaretRef} aria-hidden='true' className={styles.templateEditorDropCaret} />\n      {isSuggestOpen && (\n        <BodyEnd>\n          <div ref={popupAnchorRef} className={styles.templateEditorSuggestionsAnchor}>\n            <div\n              ref={popupPanelRef}\n              className={cx(styles.templateEditorSuggestions, classNames?.suggestions)}\n              style={slotStyles?.suggestions}\n            >\n              <TemplateEditorSuggestions\n                groups={suggestionGroups}\n                query={suggest?.query ?? ''}\n                highlightedId={highlightedId}\n                listId={listId}\n                optionId={optionId}\n                onSelect={commitSuggestion}\n                classNames={classNames}\n                styles={slotStyles}\n              />\n            </div>\n          </div>\n        </BodyEnd>\n      )}\n    </div>\n  );\n}\n\nexport default TemplateEditable;\n","import type React from 'react';\nimport type { Ref } from 'react';\nimport type { FormValidator } from 'services/formValidation';\nimport type { SlotClassNames, SlotStyles, HtmlProps } from '../types/slots.types';\nimport type { TemplateChipContext } from './templateEditor.dom';\n\n/** Where the token palette sits relative to the editable field. */\nexport enum TemplateEditorPalettePosition {\n  /** Fixed-width column beside the field (the default). Best for long token lists. */\n  Right = 'right',\n  /** Wrapping row under the field. Best in narrow containers and on mobile. */\n  Bottom = 'bottom',\n  /** A button inside the field opens the tokens as a menu. Best when space is tight, or when several fields share one set of tokens. */\n  Menu = 'menu',\n  /** Neither, for an app that renders a palette of its own and inserts through the `ref`. Typing `prefix` still searches. */\n  None = 'none',\n}\n\n/** Imperative handle, for inserting from a palette of the app's own. */\nexport interface TemplateEditorHandle {\n  /** Inserts a token where the caret last was in this field, replacing a selection; appends when the field was never focused. Does nothing while read only or disabled. */\n  insertToken: (id: string) => void;\n}\n\nexport type TemplateEditorSlots =\n  | 'root'\n  | 'label'\n  | 'container'\n  | 'fieldset'\n  | 'editable'\n  | 'chip'\n  | 'chipRemove'\n  | 'menuButton'\n  | 'previewButton'\n  | 'palette'\n  | 'paletteLabel'\n  | 'paletteGroup'\n  | 'paletteGroupLabel'\n  | 'paletteItem'\n  | 'suggestions'\n  | 'suggestionGroupLabel'\n  | 'suggestionItem';\n\n/** One placeholder the user can place into the text. */\nexport interface TemplateEditorToken {\n  /** The token body written into the serialized string between `prefix` and `suffix`, e.g. `receiver.firstName` in `{{receiver.firstName}}`. Must be unique across all groups: the first match wins. */\n  id: string;\n  /** Human-readable text shown on the chip and in the palette. Falls back to `id` when omitted. */\n  label?: string;\n  /** Secondary line in the palette and the autocomplete list, e.g. \"The recipient's given name\". */\n  description?: string;\n  /** Example of what the token resolves to at send time, e.g. \"Jane\". Shown in place of the chip while the preview is on, and beside the token in the autocomplete. */\n  sample?: string;\n  /** Greys the palette entry out and leaves the token out of the menu and the autocomplete. Typed, pasted or dropped as text, it still becomes a chip, as it does already in the value. */\n  disabled?: boolean;\n}\n\n/** Text the component renders of its own. Every key is optional and falls back to English. */\nexport interface TemplateEditorLabels {\n  /** Accessible name of the palette when `paletteLabel` is not set. */\n  palette?: string;\n  /** The button that opens the menu, read out by screen readers and shown as its tooltip. */\n  insertToken?: string;\n  /** The button that swaps every chip for its token's `sample`, read out by screen readers and shown as its tooltip. */\n  preview?: string;\n  /** Remove button on a placed chip, read out by screen readers. Gets the chip's label. */\n  removeToken?: (label: string) => string;\n  /** Chip text of a token whose label another group's token shares, so the two chips can be told apart. */\n  qualifiedToken?: (group: string, label: string) => string;\n  /** Text on the chip of a token absent from `tokenGroups`. Defaults to the raw id. */\n  unknownToken?: (id: string) => string;\n  /** Tooltip of the chip of a token absent from `tokenGroups`. */\n  unknownTokenTitle?: (id: string) => string;\n}\n\n/** A labelled section of the palette, e.g. \"Recipient\" or \"Sender\". */\nexport interface TemplateEditorTokenGroup {\n  /** Stable identity, used as the React key. Falls back to `label`, then to the array index. */\n  id?: string;\n  /** Section heading. Omit for an unheaded group of loose tokens. */\n  label?: string;\n  /** The tokens in this section, in display order. */\n  tokens: TemplateEditorToken[];\n}\n\n/** One piece of a parsed template: either literal text or a token reference. */\nexport type TemplateSegment = { type: 'text'; text: string } | { type: 'token'; id: string };\n\nexport interface TemplateEditorProps extends Omit<HtmlProps<HTMLDivElement>, 'onChange'> {\n  /** The template string, e.g. `Hello {{receiver.firstName}},\\n\\nWelcome.`. Controlled exactly like `Input`'s `value`: pair it with `onChange`, or leave both off and drive the field through `formValidator`. */\n  value?: string;\n  /** Fires with the full serialized template on every edit: typing, insertion, deletion, paste, drop, undo. Never fires with a value the component was just given. */\n  onChange?: (value: string) => void;\n  /** Tokens offered by the palette and by the `prefix` autocomplete, grouped for display. A token present in `value` but absent here still renders as a chip, flagged as unknown, and round-trips unchanged. */\n  tokenGroups?: TemplateEditorTokenGroup[];\n  /** Where the palette sits (default `Right`). */\n  palettePosition?: TemplateEditorPalettePosition;\n  /** Heading above the palette, e.g. \"Placeholders\". */\n  paletteLabel?: string;\n  /** Opening delimiter of a token in the serialized string (default `{{`). Typing it also opens the autocomplete popup. Regex metacharacters are escaped, so `$[` and `<%` are fine. */\n  prefix?: string;\n  /** Closing delimiter of a token in the serialized string (default `}}`). */\n  suffix?: string;\n  /** Floating field label, forwarded to `Input`. */\n  label?: string;\n  /** Appends a `*` to the label. Implied when `formValidator` carries a `required` validator. */\n  isRequired?: boolean;\n  /** Error text shown in the field's error tooltip while it's focused or hovered. Ignored when `formValidator` is set: the validator's message wins. */\n  errorMessage?: string;\n  /** Renders the error state without a message. Ignored when `formValidator` is set. */\n  isValid?: boolean;\n  /** Drives value, validation, and touched state instead of `value`/`onChange`. `set` receives the serialized template string. */\n  formValidator?: FormValidator | null;\n  /** Hint shown inside the empty, focused field. */\n  placeholder?: string;\n  /** Blocks every edit and drag path, and dims the field. Chips stay visible. */\n  disabled?: boolean;\n  /** Blocks editing but keeps the field focusable, selectable, and copyable. The palette is hidden. */\n  readOnly?: boolean;\n  /** Allow line breaks (default `true`). Turn it off for a subject or an address: Enter then does nothing, and line breaks in pasted or dropped text become spaces. */\n  multiline?: boolean;\n  /** Minimum visible text rows before the field grows (default `4`). Ignored when `multiline` is off. */\n  rows?: number;\n  /** Focuses the field and places the caret at the end of the template on mount. */\n  autoFocus?: boolean;\n  /** Puts a space where a placed chip would touch a letter, a digit or another chip, and removes the one a moved or removed chip leaves redundant (default `true`). Typing, pasting and the `prefix` autocomplete stay literal. Turn it off for an address field, where `{{first}}{{last}}` must stay joined. */\n  smartSpacing?: boolean;\n  /** Fires when the field gains focus. */\n  onFocus?: (event: React.FocusEvent) => void;\n  /** Fires when the field loses focus. Marks `formValidator` as touched first, matching `Input`. */\n  onBlur?: (event: React.FocusEvent) => void;\n  /** Text the component renders of its own, for localisation. */\n  labels?: TemplateEditorLabels;\n  /** Imperative handle for inserting a token from a palette of the app's own. */\n  ref?: Ref<TemplateEditorHandle>;\n  className?: string;\n  style?: React.CSSProperties;\n  classNames?: SlotClassNames<TemplateEditorSlots>;\n  styles?: SlotStyles<TemplateEditorSlots>;\n}\n\n/**\n * What a drag is carrying. Kept in a ref shared between the palette and the\n * editable rather than in `dataTransfer`, because `getData` is unreadable\n * during `dragover` in every browser's protected mode, which is exactly when\n * the drop indicator needs to know what is coming.\n */\nexport type TemplateDragPayload =\n  | { kind: 'insert'; id: string }\n  | { kind: 'move'; text: string; start: number; end: number; source: HTMLElement };\n\n/** Imperative handle the editor shell uses to insert from the palette. */\nexport interface TemplateEditableApi {\n  /** Places a token at the last known caret position, appending when the field was never focused. */\n  insertToken: (id: string) => void;\n  /** Rings every chip of a token, for the palette entry under the pointer or focus; `null` clears them. */\n  highlightToken: (id: string | null) => void;\n}\n\n/** Props of the internal contenteditable surface handed to `Input` as its `customInput`. */\nexport interface TemplateEditableProps {\n  /** Injected by `Input`: the serialized template, from `value` or the form validator. */\n  value?: string | number;\n  /** Injected by `Input`: its own change handler, which accepts a plain string. */\n  onChange?: (value: string) => void;\n  onFocus?: (event: React.FocusEvent) => void;\n  onBlur?: (event: React.FocusEvent) => void;\n  className?: string;\n  style?: React.CSSProperties;\n  id?: string;\n  tabIndex?: number;\n  readOnly?: boolean;\n  disabled?: boolean;\n  /** Populated by the editable so the shell can insert tokens from the palette. */\n  apiRef?: React.RefObject<TemplateEditableApi | null>;\n  /** Shared drag payload, written by the palette and read on drop. */\n  dragRef: React.RefObject<TemplateDragPayload | null>;\n  chipContext: TemplateChipContext;\n  tokenGroups?: TemplateEditorTokenGroup[];\n  ariaLabel?: string;\n  multiline?: boolean;\n  rows?: number;\n  /** Handled here rather than by `Input`, whose autofocus effect targets a ref it never gets for a custom input. */\n  autoFocus?: boolean;\n  smartSpacing?: boolean;\n  /** Set for `TemplateEditorPalettePosition.Menu`: renders the menu button, named by this. */\n  menuLabel?: string;\n  /** Set when a token has a `sample`: renders the preview button, named by this. Its pressed state is `chipContext.preview`. */\n  previewLabel?: string;\n  onTogglePreview?: () => void;\n  classNames?: SlotClassNames<TemplateEditorSlots>;\n  styles?: SlotStyles<TemplateEditorSlots>;\n}\n\n/** Props of the internal autocomplete list. */\nexport interface TemplateEditorSuggestionsProps {\n  groups: TemplateEditorTokenGroup[];\n  /** What has been typed after the opening delimiter, marked where it occurs in each entry. */\n  query: string;\n  highlightedId: string | null;\n  listId: string;\n  optionId: (tokenId: string) => string;\n  onSelect: (tokenId: string) => void;\n  classNames?: SlotClassNames<TemplateEditorSlots>;\n  styles?: SlotStyles<TemplateEditorSlots>;\n}\n\n/** Props of the internal token palette. */\nexport interface TemplateEditorPaletteProps {\n  groups: TemplateEditorTokenGroup[];\n  label?: string;\n  labels: Required<TemplateEditorLabels>;\n  position: TemplateEditorPalettePosition;\n  /** Delimiters, so a dragged entry carries the serialized placeholder rather than a bare id. */\n  prefix: string;\n  suffix: string;\n  disabled?: boolean;\n  dragRef: React.RefObject<TemplateDragPayload | null>;\n  onInsert: (tokenId: string) => void;\n  onHighlight: (tokenId: string | null) => void;\n  classNames?: SlotClassNames<TemplateEditorSlots>;\n  styles?: SlotStyles<TemplateEditorSlots>;\n}\n","import { useId, useMemo, useRef, useState } from 'react';\nimport cx from 'classnames';\n\nimport styles from './templateEditor.module.pcss';\nimport { isSelfActivation } from '../utils/activationKey';\nimport { toneProps } from '../styles/tone';\nimport { ThemeColor } from '../themeProvider/color.types';\nimport { flattenTokens, tokenLabel } from './templateEditor.utils';\nimport { TemplateEditorPalettePosition, type TemplateEditorPaletteProps } from './templateEditor.types';\n\nconst TONE = toneProps(ThemeColor.Primary).className;\nconst DISABLED_TONE = toneProps(ThemeColor.Neutral).className;\n\n/**\n * The list of insertable placeholders. Every entry is reachable three ways: by\n * dragging it into the text, by clicking it, and by tabbing into the toolbar\n * and pressing Enter. Drag alone would be unusable with a keyboard and impossible\n * on touch, where HTML5 drag events never fire.\n */\nfunction TemplateEditorPalette({\n  groups,\n  label,\n  labels,\n  position,\n  prefix,\n  suffix,\n  disabled = false,\n  dragRef,\n  onInsert,\n  onHighlight,\n  classNames,\n  styles: slotStyles,\n}: TemplateEditorPaletteProps) {\n  const groupIdBase = useId();\n  const [activeIndex, setActiveIndex] = useState(0);\n  const itemRefs = useRef<(HTMLDivElement | null)[]>([]);\n  const tokens = useMemo(() => flattenTokens(groups), [groups]);\n  // Roving tabindex needs each item's position in the flattened list, which the\n  // grouped markup cannot count on its own without mutating a render-scope variable.\n  const indexById = useMemo(() => new Map(tokens.map((token, index) => [token.id, index])), [tokens]);\n  const enabled = useMemo(\n    () => (disabled ? [] : tokens.flatMap((token, index) => (token.disabled ? [] : [index]))),\n    [disabled, tokens],\n  );\n  // Falls back to the first enabled entry, or a disabled first token, or one\n  // disabled since it was last focused, would take the palette out of the tab order.\n  const tabStop = enabled.includes(activeIndex) ? activeIndex : enabled[0];\n  const horizontal = position === TemplateEditorPalettePosition.Bottom;\n\n  function focusEnabled(slot: number) {\n    const index = enabled[Math.max(0, Math.min(enabled.length - 1, slot))];\n    if (index === undefined) return;\n    setActiveIndex(index);\n    itemRefs.current[index]?.focus();\n  }\n\n  function handleKeyDown(event: React.KeyboardEvent, index: number, tokenId: string) {\n    const at = enabled.indexOf(index);\n    if (event.key === (horizontal ? 'ArrowRight' : 'ArrowDown') || event.key === 'ArrowDown') {\n      event.preventDefault();\n      focusEnabled(at + 1);\n      return;\n    }\n    if (event.key === (horizontal ? 'ArrowLeft' : 'ArrowUp') || event.key === 'ArrowUp') {\n      event.preventDefault();\n      focusEnabled(at - 1);\n      return;\n    }\n    if (event.key === 'Home') {\n      event.preventDefault();\n      focusEnabled(0);\n      return;\n    }\n    if (event.key === 'End') {\n      event.preventDefault();\n      focusEnabled(enabled.length - 1);\n      return;\n    }\n    if (isSelfActivation(event)) {\n      event.preventDefault();\n      if (at >= 0) onInsert(tokenId);\n    }\n  }\n\n  return (\n    <div className={cx(styles.templateEditorPalette, classNames?.palette)} style={slotStyles?.palette}>\n      {label && (\n        <div\n          className={cx(styles.templateEditorPaletteLabel, classNames?.paletteLabel)}\n          style={slotStyles?.paletteLabel}\n        >\n          {label}\n        </div>\n      )}\n      <div\n        role='toolbar'\n        aria-label={label ?? labels.palette}\n        aria-orientation={horizontal ? 'horizontal' : 'vertical'}\n        className={styles.templateEditorPaletteList}\n      >\n        {groups.map((group, groupIndex) => (\n          <div\n            key={group.id ?? group.label ?? groupIndex}\n            role='group'\n            aria-labelledby={group.label ? `${groupIdBase}-${groupIndex}` : undefined}\n            className={cx(styles.templateEditorPaletteGroup, classNames?.paletteGroup)}\n            style={slotStyles?.paletteGroup}\n          >\n            {group.label && (\n              <div\n                id={`${groupIdBase}-${groupIndex}`}\n                className={cx(styles.templateEditorPaletteGroupLabel, classNames?.paletteGroupLabel)}\n                style={slotStyles?.paletteGroupLabel}\n              >\n                {group.label}\n              </div>\n            )}\n            <div className={styles.templateEditorPaletteItems}>\n              {group.tokens.map((token) => {\n                const index = indexById.get(token.id) ?? 0;\n                const isDisabled = !!token.disabled || disabled;\n                return (\n                  <div\n                    key={token.id}\n                    ref={(node) => {\n                      itemRefs.current[index] = node;\n                    }}\n                    role='button'\n                    aria-disabled={isDisabled || undefined}\n                    tabIndex={isDisabled ? undefined : index === tabStop ? 0 : -1}\n                    draggable={!isDisabled}\n                    className={cx(\n                      isDisabled ? DISABLED_TONE : TONE,\n                      styles.templateEditorChip,\n                      styles.templateEditorPaletteItem,\n                      isDisabled && styles.templateEditorPaletteItemDisabled,\n                      classNames?.paletteItem,\n                    )}\n                    style={slotStyles?.paletteItem}\n                    // Deliberately no `preventDefault` on mousedown here. It would\n                    // hold the editable's selection, but Blink and Gecko treat a\n                    // prevented mousedown as \"no drag\", so `dragstart` would never\n                    // fire and the whole drag path would silently do nothing. The\n                    // caret is recovered from the editable's last known selection\n                    // instead, which survives the blur this causes.\n                    onFocus={() => {\n                      setActiveIndex(index);\n                      onHighlight(token.id);\n                    }}\n                    onBlur={() => onHighlight(null)}\n                    onMouseEnter={() => onHighlight(token.id)}\n                    onMouseLeave={() => onHighlight(null)}\n                    onClick={() => !isDisabled && onInsert(token.id)}\n                    onKeyDown={(event) => handleKeyDown(event, index, token.id)}\n                    onDragStart={(event) => {\n                      if (isDisabled) {\n                        event.preventDefault();\n                        return;\n                      }\n                      dragRef.current = { kind: 'insert', id: token.id };\n                      // The serialized placeholder, not the bare id: a drop into a\n                      // *different* editor never sees this palette's drag ref, so it\n                      // falls back to this text, which the target parses into a chip.\n                      // It is also the useful thing to receive in any other app.\n                      event.dataTransfer.setData('text/plain', prefix + token.id + suffix);\n                      event.dataTransfer.effectAllowed = 'copy';\n                    }}\n                    onDragEnd={() => {\n                      dragRef.current = null;\n                    }}\n                  >\n                    <span className={styles.templateEditorPaletteItemLabel}>{tokenLabel(token)}</span>\n                    {token.description && (\n                      <span className={styles.templateEditorPaletteItemDescription}>{token.description}</span>\n                    )}\n                  </div>\n                );\n              })}\n            </div>\n          </div>\n        ))}\n      </div>\n    </div>\n  );\n}\n\nexport default TemplateEditorPalette;\n","import { useCallback, useImperativeHandle, useMemo, useRef, useState } from 'react';\nimport cx from 'classnames';\nimport Input from 'components/common/input';\nimport { useComponentDefaults, useLabels } from 'components/common/configProvider';\nimport { ThemeColor } from 'components/common/themeProvider/color.types';\nimport { toneProps } from 'components/common/styles/tone';\n\nimport styles from './templateEditor.module.pcss';\nimport TemplateEditable from './templateEditable';\nimport TemplateEditorPalette from './templateEditorPalette';\nimport {\n  buildChipLabels,\n  buildTokenIndex,\n  DEFAULT_LABELS,\n  DEFAULT_PREFIX,\n  DEFAULT_SUFFIX,\n} from './templateEditor.utils';\nimport type { TemplateChipContext } from './templateEditor.dom';\nimport {\n  TemplateEditorPalettePosition,\n  type TemplateDragPayload,\n  type TemplateEditableApi,\n  type TemplateEditorProps,\n} from './templateEditor.types';\n\n// Tone classes rather than the theme's -lighter pastels, so a chip reads the same as a\n// Light Chip on a dark theme too. Two classes, because a chip can carry only one tone.\nconst KNOWN_TONE = toneProps(ThemeColor.Primary).className;\nconst UNKNOWN_TONE = toneProps(ThemeColor.Warn).className;\n\n/**\n * A single form field for authoring templates that mix prose with placeholders.\n *\n * The field is a contenteditable whose content React never owns: text and chips\n * are built imperatively, which is what keeps the caret stable while typing.\n * The chrome around it (floating label, error tooltip, form validation) comes\n * from `Input`, which accepts this surface as its `customInput`, so the field\n * looks and validates exactly like every other input in the library.\n */\nfunction TemplateEditor(props: TemplateEditorProps) {\n  const {\n    value = '',\n    onChange,\n    tokenGroups = [],\n    palettePosition = TemplateEditorPalettePosition.Right,\n    paletteLabel,\n    prefix = DEFAULT_PREFIX,\n    suffix = DEFAULT_SUFFIX,\n    label = '',\n    isRequired = false,\n    errorMessage = '',\n    isValid = true,\n    formValidator = null,\n    placeholder = '',\n    disabled = false,\n    readOnly = false,\n    multiline = true,\n    rows = 4,\n    autoFocus = false,\n    smartSpacing = true,\n    onFocus,\n    onBlur,\n    labels,\n    ref,\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    ...rest\n  } = useComponentDefaults('TemplateEditor', props);\n  const text = useLabels('TemplateEditor', labels, DEFAULT_LABELS);\n  // Taken apart because `useLabels` returns a new object every render, and the\n  // chip context must stay stable for as long as the strings themselves do.\n  const { removeToken, qualifiedToken, unknownToken, unknownTokenTitle } = text;\n\n  const apiRef = useRef<TemplateEditableApi | null>(null);\n  const dragRef = useRef<TemplateDragPayload | null>(null);\n  const [previewOn, setPreviewOn] = useState(false);\n\n  const tokenIndex = useMemo(() => buildTokenIndex(tokenGroups), [tokenGroups]);\n  const chipLabels = useMemo(() => buildChipLabels(tokenGroups, qualifiedToken), [qualifiedToken, tokenGroups]);\n  const canPreview = !disabled && Array.from(tokenIndex.values()).some((token) => token.sample);\n  const preview = previewOn && canPreview;\n\n  const chipContext = useMemo<TemplateChipContext>(\n    () => ({\n      prefix,\n      suffix,\n      tokenIndex,\n      chipLabels,\n      labels: { removeToken, unknownToken, unknownTokenTitle },\n      preview,\n      chipClassName: cx(styles.templateEditorChip, classNames?.chip),\n      knownChipClassName: KNOWN_TONE,\n      unknownChipClassName: cx(styles.templateEditorChipUnknown, UNKNOWN_TONE),\n      previewChipClassName: styles.templateEditorChipPreview,\n      chipStyle: slotStyles?.chip,\n      // A remove button on a field that cannot be edited would be a dead control, and in the\n      // preview it would break up text that is meant to read as the message.\n      showRemove: !readOnly && !disabled && !preview,\n      removeClassName: cx(styles.templateEditorChipRemove, classNames?.chipRemove),\n      removeStyle: slotStyles?.chipRemove,\n    }),\n    [\n      chipLabels,\n      classNames?.chip,\n      classNames?.chipRemove,\n      disabled,\n      prefix,\n      preview,\n      readOnly,\n      removeToken,\n      slotStyles?.chip,\n      slotStyles?.chipRemove,\n      suffix,\n      tokenIndex,\n      unknownToken,\n      unknownTokenTitle,\n    ],\n  );\n\n  const handlePaletteInsert = useCallback((tokenId: string) => {\n    apiRef.current?.insertToken(tokenId);\n  }, []);\n\n  useImperativeHandle(ref, () => ({ insertToken: handlePaletteInsert }), [handlePaletteInsert]);\n\n  const isMenu = palettePosition === TemplateEditorPalettePosition.Menu;\n  const showPalette =\n    !readOnly &&\n    palettePosition !== TemplateEditorPalettePosition.Menu &&\n    palettePosition !== TemplateEditorPalettePosition.None;\n  const isBottom = palettePosition === TemplateEditorPalettePosition.Bottom;\n\n  return (\n    <div\n      {...rest}\n      className={cx(\n        styles.templateEditor,\n        isBottom ? styles.templateEditorBottom : styles.templateEditorRight,\n        classNames?.root,\n        className,\n      )}\n      style={{ ...slotStyles?.root, ...style }}\n    >\n      <div className={styles.templateEditorField}>\n        <Input\n          multiline={multiline}\n          label={label}\n          isRequired={isRequired}\n          isValid={isValid}\n          errorMessage={errorMessage}\n          formValidator={formValidator}\n          placeholder={placeholder}\n          disabled={disabled}\n          readOnly={readOnly}\n          value={value}\n          onChange={(next) => onChange?.(next)}\n          onFocus={onFocus}\n          onBlur={onBlur}\n          classNames={{\n            label: classNames?.label,\n            container: classNames?.container,\n            fieldset: classNames?.fieldset,\n          }}\n          styles={{\n            label: slotStyles?.label,\n            // Input gives a multiline container a resize grip, which here only stretched the frame:\n            // the editable grows with its text instead. Inline, to beat Input's one-class rule.\n            container: { resize: 'none', ...slotStyles?.container },\n            fieldset: slotStyles?.fieldset,\n          }}\n          customInput={\n            <TemplateEditable\n              apiRef={apiRef}\n              dragRef={dragRef}\n              chipContext={chipContext}\n              tokenGroups={tokenGroups}\n              ariaLabel={label || undefined}\n              multiline={multiline}\n              rows={rows}\n              autoFocus={autoFocus}\n              smartSpacing={smartSpacing}\n              menuLabel={isMenu ? text.insertToken : undefined}\n              previewLabel={canPreview ? text.preview : undefined}\n              onTogglePreview={() => setPreviewOn(!preview)}\n              classNames={classNames}\n              styles={slotStyles}\n            />\n          }\n        />\n      </div>\n      {showPalette && (\n        <TemplateEditorPalette\n          groups={tokenGroups}\n          label={paletteLabel}\n          labels={text}\n          position={palettePosition}\n          prefix={prefix}\n          suffix={suffix}\n          disabled={disabled}\n          dragRef={dragRef}\n          onInsert={handlePaletteInsert}\n          onHighlight={(tokenId) => apiRef.current?.highlightToken(tokenId)}\n          classNames={classNames}\n          styles={slotStyles}\n        />\n      )}\n    </div>\n  );\n}\n\nexport default TemplateEditor;\n","import React from 'react';\nimport Input from 'components/common/input';\nimport { useComponentDefaults } from 'components/common/configProvider';\nimport type { TextareaProps } from './textarea.types';\n\nexport default function Textarea(props: TextareaProps) {\n  return <Input {...useComponentDefaults('Textarea', props)} multiline />;\n}\n","import { useSyncExternalStore } from 'react';\n\nimport { useMediaQuery } from '../hooks/useMediaQuery';\nimport { defaultTheme } from './defaultTheme';\nimport { lightTheme } from './lightTheme';\nimport { revealTheme } from './themeReveal';\n\n/** The themes `'system'` picks between, by the operating system's light or dark setting. */\nexport interface SystemThemes {\n  light: string;\n  dark: string;\n}\n\n/** How `setCurrentThemeId` makes the change. */\nexport interface SetThemeOptions {\n  /**\n   * Grows the new theme out of this element, usually the control that chose it, as a rippling\n   * blob across the page. Only where the browser has view transitions and the user has not asked for\n   * reduced motion; elsewhere the theme just changes.\n   */\n  revealFrom?: Element | null;\n}\n\nexport interface UseThemeResult {\n  /** The theme to apply, for `currentThemeId`. Never `'system'`: that is resolved to a theme. */\n  currentThemeId: string;\n  /** What was chosen, `'system'` included, for marking the active choice in a theme switcher. */\n  selectedThemeId: string;\n  /** Chooses a theme by its `id`, or `'system'`, and remembers it for the next visit. */\n  setCurrentThemeId: (themeId: string, options?: SetThemeOptions) => void;\n}\n\nconst STORAGE_KEY = 'theme';\n\n// One store for every caller, so two components using this hook cannot show different themes.\nconst listeners = new Set<() => void>();\n\n// Only set while storage refuses writes, so the switch still works for the rest of the visit.\nlet unsaved: string | null = null;\n\nfunction read(): string | null {\n  if (unsaved !== null) return unsaved;\n  try {\n    return localStorage.getItem(STORAGE_KEY);\n  } catch {\n    return null;\n  }\n}\n\nfunction subscribe(onChange: () => void): () => void {\n  listeners.add(onChange);\n  // A choice made in another tab. `key` is null when that tab cleared its storage.\n  const onStorage = (event: StorageEvent) => {\n    if (event.key === STORAGE_KEY || event.key === null) onChange();\n  };\n  window.addEventListener('storage', onStorage);\n  return () => {\n    listeners.delete(onChange);\n    window.removeEventListener('storage', onStorage);\n  };\n}\n\nfunction select(themeId: string, { revealFrom }: SetThemeOptions = {}): void {\n  // Choosing the theme already chosen changes nothing, so there is nothing to reveal.\n  if (revealFrom && themeId !== read()) revealTheme(() => store(themeId), revealFrom);\n  else store(themeId);\n}\n\nfunction store(themeId: string): void {\n  try {\n    localStorage.setItem(STORAGE_KEY, themeId);\n    unsaved = null;\n  } catch {\n    unsaved = themeId;\n  }\n  for (const listener of listeners) listener();\n}\n\n/** The server has no storage, so it renders the default and the client takes over after hydration. */\nconst readOnServer = () => null;\n\n/**\n * The theme the user chose, remembered across visits. Pass `'system'` as the default, or let the\n * user choose it, to follow the operating system's light or dark setting.\n */\nexport function useTheme(\n  defaultThemeId: string = defaultTheme.id,\n  systemThemes: SystemThemes = { light: lightTheme.id, dark: defaultTheme.id },\n): UseThemeResult {\n  const selectedThemeId = useSyncExternalStore(subscribe, read, readOnServer) ?? defaultThemeId;\n  const prefersDark = useMediaQuery('(prefers-color-scheme: dark)');\n\n  let currentThemeId = selectedThemeId;\n  if (selectedThemeId === 'system') currentThemeId = prefersDark ? systemThemes.dark : systemThemes.light;\n\n  return { currentThemeId, selectedThemeId, setCurrentThemeId: select };\n}\n","import {\n  createContext,\n  useContext,\n  useRef,\n  useState,\n  type PointerEvent as ReactPointerEvent,\n  type ReactElement,\n} from 'react';\nimport type { TiltGroupContextValue, TiltGroupProps } from './tiltGroup.types';\n\nexport const TiltGroupContext = createContext<TiltGroupContextValue | null>(null);\n\nexport function useTiltGroup(): TiltGroupContextValue | null {\n  return useContext(TiltGroupContext);\n}\n\nfunction TiltGroup({ children, className, style, ...rest }: TiltGroupProps): ReactElement {\n  const rootRef = useRef<HTMLDivElement>(null);\n  const rafRef = useRef<number | undefined>(undefined);\n  // See Tilt's identical `rafPendingRef` for why this is tracked separately from `rafRef`.\n  const rafPendingRef = useRef(false);\n  const latestClientPos = useRef<{ x: number; y: number } | null>(null);\n  const [value, setValue] = useState<TiltGroupContextValue>({ pointer: null, rect: null });\n\n  const scheduleUpdate = () => {\n    if (rafPendingRef.current) return;\n    rafPendingRef.current = true;\n    rafRef.current = requestAnimationFrame(() => {\n      rafPendingRef.current = false;\n      rafRef.current = undefined;\n      const root = rootRef.current;\n      const pos = latestClientPos.current;\n      if (!root || !pos) return;\n      const rect = root.getBoundingClientRect();\n      setValue({ pointer: { x: pos.x - rect.left, y: pos.y - rect.top }, rect });\n    });\n  };\n\n  const handlePointerMove = (event: ReactPointerEvent<HTMLDivElement>) => {\n    latestClientPos.current = { x: event.clientX, y: event.clientY };\n    scheduleUpdate();\n  };\n\n  const handlePointerLeave = () => {\n    latestClientPos.current = null;\n    if (rafPendingRef.current && rafRef.current !== undefined) {\n      cancelAnimationFrame(rafRef.current);\n      rafPendingRef.current = false;\n      rafRef.current = undefined;\n    }\n    setValue({ pointer: null, rect: null });\n  };\n\n  return (\n    <div\n      {...rest}\n      ref={rootRef}\n      className={className}\n      style={style}\n      onPointerMove={handlePointerMove}\n      onPointerLeave={handlePointerLeave}\n    >\n      <TiltGroupContext.Provider value={value}>{children}</TiltGroupContext.Provider>\n    </div>\n  );\n}\n\nexport default TiltGroup;\n","export type TiltAxisLock = 'x' | 'y' | 'none';\n\nexport interface ComputeTiltOptions {\n  maxTiltX: number;\n  maxTiltY: number;\n  easing: (t: number) => number;\n  lockAxis: TiltAxisLock;\n}\n\nexport interface TiltResult {\n  rotateX: number;\n  rotateY: number;\n}\n\n/**\n * Eases faster near the edges (|t| close to 1) and flatter near the center (|t| close to 0),\n * while staying odd-symmetric so tilt direction always matches cursor direction.\n */\nexport function defaultTiltEasing(t: number): number {\n  const clamped = Math.max(-1, Math.min(1, t));\n  return Math.sign(clamped) * Math.pow(Math.abs(clamped), 1.6);\n}\n\nfunction clamp(value: number, max: number): number {\n  return Math.max(-max, Math.min(max, value));\n}\n\n/**\n * Computes the tilt rotation for a pointer at (x, y) relative to `rect`'s top-left corner.\n * Vertical cursor position drives rotateX (tilts top/bottom edges), horizontal drives rotateY.\n */\nexport function computeTilt(\n  x: number,\n  y: number,\n  rect: { width: number; height: number },\n  { maxTiltX, maxTiltY, easing, lockAxis }: ComputeTiltOptions,\n): TiltResult {\n  const nx = rect.width > 0 ? (x / rect.width) * 2 - 1 : 0;\n  const ny = rect.height > 0 ? (y / rect.height) * 2 - 1 : 0;\n\n  const rotateX = lockAxis === 'x' ? 0 : clamp(-easing(ny) * maxTiltX, maxTiltX);\n  const rotateY = lockAxis === 'y' ? 0 : clamp(easing(nx) * maxTiltY, maxTiltY);\n\n  return { rotateX, rotateY };\n}\n",".tilt {\n  display: inline-block;\n  touch-action: none;\n\n  &-perspective {\n    width: 100%;\n    height: 100%;\n  }\n\n  &-surface {\n    position: relative;\n    width: 100%;\n    height: 100%;\n    transform-style: preserve-3d;\n    will-change: transform;\n  }\n}\n","import { useEffect, useImperativeHandle, useRef, type PointerEvent as ReactPointerEvent } from 'react';\nimport cx from 'classnames';\n\nimport { useComponentDefaults } from '../configProvider';\nimport { useTiltGroup } from '../tiltGroup';\nimport { computeTilt, defaultTiltEasing } from './tiltMath';\nimport type { TiltProps } from './tilt.types';\nimport styles from './tilt.module.pcss';\nimport { isDev } from '../utils/isDev';\n\nfunction prefersReducedMotion(): boolean {\n  return typeof window !== 'undefined' && window.matchMedia?.('(prefers-reduced-motion: reduce)').matches === true;\n}\n\n// Per-frame catch-up rate (fraction of the remaining gap closed each tick) and the gap size,\n// in degrees, below which we consider the catch-up finished and switch to direct 1:1 writes.\nconst TILT_SMOOTHING = 0.3;\nconst TILT_CONVERGE_EPSILON = 0.05;\n\nfunction Tilt(props: TiltProps) {\n  const {\n    ref,\n    children,\n    maxTilt = 15,\n    maxTiltX,\n    maxTiltY,\n    perspective = 800,\n    easing = defaultTiltEasing,\n    lockAxis = 'none',\n    scaleOnHover = 1,\n    trackingMode = 'self',\n    disabled = false,\n    onTiltChange,\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    ...rest\n  } = useComponentDefaults('Tilt', props);\n\n  const rootRef = useRef<HTMLDivElement>(null);\n  const surfaceRef = useRef<HTMLDivElement>(null);\n  const rafRef = useRef<number | undefined>(undefined);\n  const loopActiveRef = useRef(false);\n  // Whether the pointer is currently \"over\" the element (self mode: within its own bounds;\n  // group mode: the shared group pointer is set). The tilt loop's target is (0,0) whenever this\n  // is false, and computed from `latestPointerRef` whenever it's true.\n  const isHoveringRef = useRef(false);\n  // True while the visually-applied tilt is still catching up to the target after a\n  // discontinuity (entering from flat, or leaving back to flat), eased via a per-frame lerp\n  // toward whatever the *current* target is. Once the gap closes, updates apply directly (1:1,\n  // no lag) so continuous tracking stays crisp. A CSS transition can't do this cleanly: retargeting\n  // one mid-flight restarts its full duration from scratch, so a continuously-moving cursor makes\n  // it fall further and further behind instead of ever catching up.\n  const isCatchingUpRef = useRef(false);\n  const latestPointerRef = useRef<{ x: number; y: number } | null>(null);\n  const currentTiltRef = useRef({ rotateX: 0, rotateY: 0 });\n\n  const group = useTiltGroup();\n  const usingGroup = trackingMode === 'group';\n  const groupActive = usingGroup && group !== null;\n\n  useEffect(() => {\n    if (usingGroup && group === null && isDev()) {\n      console.warn('Tilt: trackingMode=\"group\" was set with no ancestor TiltGroup, falling back to self tracking.');\n    }\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [usingGroup, group === null]);\n\n  const clampX = maxTiltX ?? maxTilt;\n  const clampY = maxTiltY ?? maxTilt;\n\n  function writeTiltTransform(rotateX: number, rotateY: number, scale: number) {\n    const surface = surfaceRef.current;\n    if (!surface) return;\n    surface.style.transform = `rotateX(${rotateX}deg) rotateY(${rotateY}deg)` + (scale !== 1 ? ` scale(${scale})` : '');\n  }\n\n  function stopTiltLoop() {\n    if (rafRef.current !== undefined) {\n      cancelAnimationFrame(rafRef.current);\n      rafRef.current = undefined;\n    }\n    loopActiveRef.current = false;\n  }\n\n  // The single continuous per-frame driver for tilt. Reads live authoritative state\n  // (`isHoveringRef`/`latestPointerRef`) fresh every tick rather than closing over values\n  // captured at schedule time, so there's no possibility of a stale queued frame overwriting a\n  // newer state.\n  function tick() {\n    rafRef.current = undefined;\n    const surface = surfaceRef.current;\n    if (!surface) {\n      loopActiveRef.current = false;\n      return;\n    }\n\n    const hovering = isHoveringRef.current && !disabled && !prefersReducedMotion();\n    let targetX = 0;\n    let targetY = 0;\n    if (hovering) {\n      const pos = latestPointerRef.current;\n      const rect = rootRef.current?.getBoundingClientRect();\n      if (pos && rect) {\n        const target = computeTilt(pos.x - rect.left, pos.y - rect.top, rect, {\n          maxTiltX: clampX,\n          maxTiltY: clampY,\n          easing,\n          lockAxis,\n        });\n        targetX = target.rotateX;\n        targetY = target.rotateY;\n      }\n    }\n\n    const cur = currentTiltRef.current;\n    let nextX: number;\n    let nextY: number;\n    if (isCatchingUpRef.current) {\n      nextX = cur.rotateX + (targetX - cur.rotateX) * TILT_SMOOTHING;\n      nextY = cur.rotateY + (targetY - cur.rotateY) * TILT_SMOOTHING;\n      if (Math.abs(targetX - nextX) < TILT_CONVERGE_EPSILON && Math.abs(targetY - nextY) < TILT_CONVERGE_EPSILON) {\n        nextX = targetX;\n        nextY = targetY;\n        isCatchingUpRef.current = false;\n      }\n    } else {\n      // Locked on: apply the target directly, every frame. This is what keeps continuous\n      // tracking crisp with zero added lag once the catch-up phase has finished.\n      nextX = targetX;\n      nextY = targetY;\n    }\n\n    currentTiltRef.current = { rotateX: nextX, rotateY: nextY };\n    writeTiltTransform(nextX, nextY, hovering ? scaleOnHover : 1);\n    onTiltChange?.(nextX, nextY);\n\n    // Once locked on (nothing left to ease), stop rescheduling. The next native pointermove\n    // (self mode) or group pointer update (group mode) calls `ensureTiltLoopRunning()` again on\n    // its own, restarting the loop for exactly the one instant tick it needs.\n    if (!isCatchingUpRef.current) {\n      loopActiveRef.current = false;\n      return;\n    }\n    rafRef.current = requestAnimationFrame(tick);\n  }\n\n  function ensureTiltLoopRunning() {\n    if (loopActiveRef.current) return;\n    loopActiveRef.current = true;\n    rafRef.current = requestAnimationFrame(tick);\n  }\n\n  // Marks a hover-state transition (entering or leaving) so the loop re-arms its catch-up phase.\n  // Only on an actual flip, not on every pointermove while already hovering, so continuous\n  // tracking never re-triggers easing.\n  function setHovering(next: boolean) {\n    if (isHoveringRef.current !== next) {\n      isCatchingUpRef.current = true;\n    }\n    isHoveringRef.current = next;\n    ensureTiltLoopRunning();\n  }\n\n  // Self-mode pointer handlers.\n  const handlePointerMove = (event: ReactPointerEvent<HTMLDivElement>) => {\n    if (disabled || groupActive || prefersReducedMotion()) return;\n    latestPointerRef.current = { x: event.clientX, y: event.clientY };\n    setHovering(true);\n  };\n\n  const handlePointerLeave = () => {\n    if (groupActive) return;\n    setHovering(false);\n  };\n\n  // Group-mode tracking: react to the shared pointer position published by TiltGroup.\n  useEffect(() => {\n    if (!groupActive) return;\n    if (disabled || prefersReducedMotion()) {\n      setHovering(false);\n      return;\n    }\n    if (group?.pointer && group.rect) {\n      latestPointerRef.current = { x: group.rect.left + group.pointer.x, y: group.rect.top + group.pointer.y };\n      setHovering(true);\n    } else {\n      setHovering(false);\n    }\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [groupActive, disabled, group?.pointer, group?.rect]);\n\n  useEffect(() => {\n    // Must go through stopTiltLoop() (not just cancelAnimationFrame) so loopActiveRef is reset\n    // too. Under React StrictMode, every effect's cleanup runs once synchronously right after\n    // its setup, before setup runs again, so this fires even though the component isn't really\n    // unmounting. The group-mode effect above runs on every mount (it schedules the loop as soon\n    // as there's a group context, even with a still-null pointer), so cancelling its pending\n    // frame here without also clearing loopActiveRef left the loop believing it was still\n    // \"active\" with nothing actually scheduled: permanently wedged, since every later call to\n    // ensureTiltLoopRunning() saw loopActiveRef already true and silently no-opped forever.\n    return () => {\n      stopTiltLoop();\n    };\n  }, []);\n\n  useImperativeHandle(ref, () => ({\n    setTilt(rotateX: number, rotateY: number) {\n      const clamped = {\n        rotateX: Math.max(-clampX, Math.min(clampX, rotateX)),\n        rotateY: Math.max(-clampY, Math.min(clampY, rotateY)),\n      };\n      // Stops the loop so live tracking (if any is active) can't overwrite this explicit value\n      // on the very next frame. An external caller driving custom choreography expects the\n      // value it just set to actually stick. A later real pointer move restarts tracking fresh.\n      stopTiltLoop();\n      isHoveringRef.current = false;\n      isCatchingUpRef.current = false;\n      currentTiltRef.current = clamped;\n      writeTiltTransform(clamped.rotateX, clamped.rotateY, scaleOnHover);\n      onTiltChange?.(clamped.rotateX, clamped.rotateY);\n    },\n    reset() {\n      stopTiltLoop();\n      isHoveringRef.current = false;\n      isCatchingUpRef.current = false;\n      currentTiltRef.current = { rotateX: 0, rotateY: 0 };\n      writeTiltTransform(0, 0, 1);\n    },\n  }));\n\n  return (\n    <div\n      {...rest}\n      ref={rootRef}\n      className={cx(styles.tilt, className, classNames?.root)}\n      style={{ ...style, ...slotStyles?.root }}\n      onPointerMove={handlePointerMove}\n      onPointerLeave={handlePointerLeave}\n      onPointerCancel={handlePointerLeave}\n    >\n      <div\n        className={cx(styles.tiltPerspective, classNames?.perspective)}\n        style={{ perspective: `${perspective}px`, ...slotStyles?.perspective }}\n      >\n        <div ref={surfaceRef} className={cx(styles.tiltSurface, classNames?.surface)} style={slotStyles?.surface}>\n          {children}\n        </div>\n      </div>\n    </div>\n  );\n}\n\nexport default Tilt;\n","export interface TimeValue {\n  hours: number; // 0–23\n  minutes: number; // 0–59\n}\n\nexport const clampHour = (h: number): number => ((Math.trunc(h) % 24) + 24) % 24;\nexport const clampMinute = (m: number): number => ((Math.trunc(m) % 60) + 60) % 60;\n\n/** Parse a \"HH:MM\" (24-hour) string. Returns null for anything that isn't a valid time. */\nexport function parseTimeString(value: string | null | undefined): TimeValue | null {\n  if (!value) return null;\n  const match = value.match(/^(\\d{1,2}):(\\d{1,2})$/);\n  if (!match) return null;\n  const hours = Number(match[1]);\n  const minutes = Number(match[2]);\n  if (hours > 23 || minutes > 59) return null;\n  return { hours, minutes };\n}\n\n/** Format a TimeValue as a zero-padded \"HH:MM\" string. */\nexport function formatTime(time: TimeValue | null | undefined): string {\n  if (!time) return '';\n  const hh = String(clampHour(time.hours)).padStart(2, '0');\n  const mm = String(clampMinute(time.minutes)).padStart(2, '0');\n  return `${hh}:${mm}`;\n}\n\n/** Snap a minute value to the nearest multiple of `step` (step <= 1 is a no-op). */\nexport function snapMinute(minute: number, step: number): number {\n  if (!step || step <= 1) return clampMinute(minute);\n  return clampMinute(Math.round(clampMinute(minute) / step) * step);\n}\n\nexport const timeToMinutes = (time: TimeValue): number => time.hours * 60 + time.minutes;\n\n/** Clamp a time into [min, max] (both \"HH:MM\" strings, either optional). */\nexport function clampToRange(time: TimeValue, min?: string | null, max?: string | null): TimeValue {\n  let total = timeToMinutes(time);\n  const minVal = parseTimeString(min);\n  const maxVal = parseTimeString(max);\n  if (minVal) total = Math.max(total, timeToMinutes(minVal));\n  if (maxVal) total = Math.min(total, timeToMinutes(maxVal));\n  return { hours: Math.floor(total / 60), minutes: total % 60 };\n}\n\nexport function isWithinRange(time: TimeValue, min?: string | null, max?: string | null): boolean {\n  const total = timeToMinutes(time);\n  const minVal = parseTimeString(min);\n  const maxVal = parseTimeString(max);\n  if (minVal && total < timeToMinutes(minVal)) return false;\n  if (maxVal && total > timeToMinutes(maxVal)) return false;\n  return true;\n}\n\n/**\n * Auto-correct a masked \"HH:MM\" value as the user types: clamp the hour segment to 23\n * and the minute segment to 59 so the field can never hold an impossible time.\n */\nexport function correctTimeMaskValue(value: string): string {\n  const digits = value.replace(/[^\\d]/g, '');\n  if (!digits) return value;\n\n  let hourDigits = digits.slice(0, 2);\n  const minuteDigits = digits.slice(2, 4);\n\n  // First hour digit > 2 → it can only be a single-digit hour (e.g. \"9\" → \"09\").\n  if (hourDigits.length === 1 && Number(hourDigits) > 2) {\n    hourDigits = `0${hourDigits}`;\n  }\n  if (hourDigits.length === 2 && Number(hourDigits) > 23) hourDigits = '23';\n\n  let result = hourDigits;\n  if (minuteDigits.length > 0 || /:/.test(value)) result += ':';\n  if (minuteDigits.length > 0) {\n    const clamped = minuteDigits.length === 2 && Number(minuteDigits) > 59 ? '59' : minuteDigits;\n    result += clamped;\n  }\n  return result;\n}\n\n// ── Clock-dial geometry ─────────────────────────────────────────────────────\n\nexport interface DialPoint {\n  x: number;\n  y: number;\n}\n\n/**\n * Cartesian coordinates for a point on the dial. `slot` is the step index from the\n * top of the clock, going clockwise; `stepDegrees` is the angle between steps\n * (30° for hours, 6° for minutes). Returns coordinates relative to (cx, cy).\n */\nexport function polarPoint(cx: number, cy: number, radius: number, slot: number, stepDegrees: number): DialPoint {\n  const theta = (slot * stepDegrees * Math.PI) / 180;\n  return {\n    x: cx + radius * Math.sin(theta),\n    y: cy - radius * Math.cos(theta),\n  };\n}\n\n/** Rotation (deg) of a hand pointing at `slot`. A 3-o'clock-default hand points up at slot 0. */\nexport const handAngle = (slot: number, stepDegrees: number): number => slot * stepDegrees - 90;\n\n/** Angle (0–360, clockwise from top) of a pointer at (dx, dy) relative to the dial centre. */\nexport function pointerAngle(dx: number, dy: number): number {\n  const deg = (Math.atan2(dx, -dy) * 180) / Math.PI;\n  return (deg + 360) % 360;\n}\n\nexport const pointerRadius = (dx: number, dy: number): number => Math.sqrt(dx * dx + dy * dy);\n",".clockFace {\n  position: relative;\n  width: var(--clock-size, 256px);\n  height: var(--clock-size, 256px);\n  margin: 4px auto;\n  border-radius: 50%;\n  background: var(--background-accent-light, var(--background-accent));\n  touch-action: none;\n  user-select: none;\n  cursor: pointer;\n  -webkit-tap-highlight-color: transparent;\n}\n\n.clockHand {\n  position: absolute;\n  inset: 0;\n  width: 100%;\n  height: 100%;\n  pointer-events: none;\n  overflow: visible;\n\n  &-line {\n    stroke: var(--primary-color);\n    stroke-width: 0.7;\n  }\n\n  &-center {\n    fill: var(--primary-color);\n  }\n\n  &-knob {\n    fill: var(--primary-color);\n  }\n}\n\n.clockNumber {\n  position: absolute;\n  transform: translate(-50%, -50%);\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  width: 32px;\n  height: 32px;\n  border-radius: 50%;\n  color: var(--text-color);\n  font-size: 15px;\n  font-weight: 500;\n  line-height: 1;\n\n  /* Visual only — the face handles all pointer interaction. */\n  pointer-events: none;\n  z-index: 1;\n\n  &-inner {\n    font-size: 12px;\n    color: var(--text-dark);\n  }\n\n  &-selected {\n    color: var(--text-on-primary);\n    font-weight: 600;\n  }\n}\n","import React, { useRef } from 'react';\nimport cx from 'classnames';\nimport { type TimeValue, clampHour, snapMinute, polarPoint, pointerAngle, pointerRadius } from '../../timeFunctions';\nimport type { ClockDialProps, DialNumber } from './clockDial.types';\n\nimport styles from './clockDial.module.pcss';\n\n// viewBox units (0–100). The face scales with its CSS box; geometry stays in this space.\nconst CENTER = 50;\nconst OUTER_RADIUS = 40;\nconst INNER_RADIUS = 25.5;\nconst RING_THRESHOLD = (OUTER_RADIUS + INNER_RADIUS) / 2;\n\nconst HOUR_STEP_DEG = 30;\nconst MINUTE_STEP_DEG = 6;\n\nconst to12 = (hour: number): number => ((hour + 11) % 12) + 1;\n\n/** Build the labelled numbers for the current mode/format. */\nfunction buildNumbers(mode: 'hours' | 'minutes', use24Hour: boolean): DialNumber[] {\n  if (mode === 'minutes') {\n    return Array.from({ length: 12 }, (_, slot) => {\n      const value = slot * 5;\n      return { label: String(value).padStart(2, '0'), slot, value, ring: 'outer' as const };\n    });\n  }\n\n  if (!use24Hour) {\n    return Array.from({ length: 12 }, (_, slot) => {\n      const value = slot === 0 ? 12 : slot; // displayed hour (1–12)\n      return { label: String(value), slot, value, ring: 'outer' as const };\n    });\n  }\n\n  // 24-hour double ring: outer 1–12, inner 00/13–23.\n  const outer = Array.from({ length: 12 }, (_, slot) => {\n    const value = slot === 0 ? 12 : slot;\n    return { label: String(value), slot, value, ring: 'outer' as const };\n  });\n  const inner = Array.from({ length: 12 }, (_, slot) => {\n    const value = slot === 0 ? 0 : slot + 12;\n    return { label: String(value).padStart(2, '0'), slot, value, ring: 'inner' as const };\n  });\n  return [...outer, ...inner];\n}\n\nexport default function ClockDial({\n  time,\n  mode,\n  use24Hour,\n  minuteStep,\n  onChange,\n  onCommit,\n  classNames,\n  styles: slotStyles,\n}: ClockDialProps) {\n  const faceRef = useRef<HTMLDivElement>(null);\n  const draggingRef = useRef(false);\n\n  const numbers = buildNumbers(mode, use24Hour);\n\n  // Resolve the active value and the hand geometry for the current time.\n  const isPm = time.hours >= 12;\n  const selectedHour12 = to12(time.hours);\n\n  const hand = (() => {\n    if (mode === 'minutes') {\n      // 6° per minute → angle = minutes * MINUTE_STEP_DEG.\n      return { slot: time.minutes, radius: OUTER_RADIUS };\n    }\n    if (!use24Hour) {\n      return { slot: selectedHour12 % 12, radius: OUTER_RADIUS };\n    }\n    if (time.hours === 0) return { slot: 0, radius: INNER_RADIUS };\n    if (time.hours <= 12) return { slot: time.hours % 12, radius: OUTER_RADIUS };\n    return { slot: (time.hours - 12) % 12, radius: INNER_RADIUS };\n  })();\n\n  const knob = polarPoint(CENTER, CENTER, hand.radius, hand.slot, mode === 'minutes' ? MINUTE_STEP_DEG : HOUR_STEP_DEG);\n\n  const isSelected = (n: DialNumber): boolean =>\n    mode === 'minutes'\n      ? snapMinute(time.minutes, minuteStep) === n.value || time.minutes === n.value\n      : use24Hour\n        ? time.hours === n.value\n        : selectedHour12 === n.value;\n\n  const valueFromPointer = (clientX: number, clientY: number): TimeValue => {\n    const rect = faceRef.current?.getBoundingClientRect();\n    if (!rect) return time;\n    const dx = clientX - (rect.left + rect.width / 2);\n    const dy = clientY - (rect.top + rect.height / 2);\n    const angle = pointerAngle(dx, dy);\n\n    if (mode === 'minutes') {\n      const minutes = snapMinute(Math.round(angle / MINUTE_STEP_DEG), minuteStep);\n      return { hours: time.hours, minutes };\n    }\n\n    const slot = Math.round(angle / HOUR_STEP_DEG) % 12;\n    if (!use24Hour) {\n      const display = slot === 0 ? 12 : slot;\n      const hours = isPm ? (display % 12) + 12 : display % 12;\n      return { hours: clampHour(hours), minutes: time.minutes };\n    }\n\n    // 24h: inner vs outer ring decided by pointer distance from centre.\n    const radiusFrac = (pointerRadius(dx, dy) / (rect.width / 2)) * CENTER;\n    const inner = radiusFrac < RING_THRESHOLD;\n    const hours = inner ? (slot === 0 ? 0 : slot + 12) : slot === 0 ? 12 : slot;\n    return { hours: clampHour(hours), minutes: time.minutes };\n  };\n\n  const handlePointerDown = (e: React.PointerEvent<HTMLDivElement>) => {\n    draggingRef.current = true;\n    // setPointerCapture keeps move/up events flowing to the face even when the\n    // pointer started on (or drags over) a number. Guarded for jsdom.\n    (e.currentTarget as HTMLElement).setPointerCapture?.(e.pointerId);\n    onChange(valueFromPointer(e.clientX, e.clientY));\n  };\n\n  const handlePointerMove = (e: React.PointerEvent<HTMLDivElement>) => {\n    if (!draggingRef.current) return;\n    onChange(valueFromPointer(e.clientX, e.clientY));\n  };\n\n  const handlePointerUp = (e: React.PointerEvent<HTMLDivElement>) => {\n    if (!draggingRef.current) return;\n    draggingRef.current = false;\n    onCommit(valueFromPointer(e.clientX, e.clientY));\n  };\n\n  return (\n    <div\n      ref={faceRef}\n      className={cx(styles.clockFace, classNames?.clockFace)}\n      style={slotStyles?.clockFace}\n      onPointerDown={handlePointerDown}\n      onPointerMove={handlePointerMove}\n      onPointerUp={handlePointerUp}\n      role='slider'\n      aria-label={mode === 'minutes' ? 'Minutes' : 'Hours'}\n      aria-valuetext={mode === 'minutes' ? `${time.minutes} minutes` : `${time.hours} hours`}\n    >\n      <svg className={styles.clockHand} viewBox='0 0 100 100' aria-hidden>\n        <circle cx={CENTER} cy={CENTER} r={1.8} className={styles.clockHandCenter} />\n        <line x1={CENTER} y1={CENTER} x2={knob.x} y2={knob.y} className={styles.clockHandLine} />\n        <circle\n          cx={knob.x}\n          cy={knob.y}\n          r={7}\n          className={cx(styles.clockHandKnob, classNames?.clockHand)}\n          style={slotStyles?.clockHand as React.CSSProperties}\n        />\n      </svg>\n\n      {numbers.map((n) => {\n        const p = polarPoint(CENTER, CENTER, n.ring === 'inner' ? INNER_RADIUS : OUTER_RADIUS, n.slot, HOUR_STEP_DEG);\n        const selected = isSelected(n);\n        // Purely visual — the face owns all pointer handling so click and drag\n        // behave identically whether the pointer lands on a number or between two.\n        return (\n          <div\n            key={`${n.ring}-${n.value}`}\n            className={cx(\n              styles.clockNumber,\n              n.ring === 'inner' && styles.clockNumberInner,\n              selected && styles.clockNumberSelected,\n              classNames?.clockNumber,\n            )}\n            style={{ left: `${p.x}%`, top: `${p.y}%`, ...slotStyles?.clockNumber }}\n          >\n            {n.label}\n          </div>\n        );\n      })}\n    </div>\n  );\n}\n",".timeInput {\n  position: relative;\n  width: 100%;\n  flex: 1;\n  min-width: 105px;\n\n  &-clock {\n    display: flex;\n    flex-direction: column;\n    gap: 8px;\n    padding: 12px;\n    min-width: 280px;\n  }\n\n  &-clockHeader {\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    gap: 4px;\n    margin-bottom: 4px;\n  }\n\n  &-headerSegment {\n    border: none;\n    background: var(--background-accent);\n    color: var(--text-dark);\n    font-size: 32px;\n    font-weight: 600;\n    line-height: 1;\n    padding: 6px 10px;\n    border-radius: var(--default-border-radius);\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n\n    &-active {\n      background: var(--primary-color);\n      color: var(--text-on-primary);\n    }\n  }\n\n  &-headerColon {\n    font-size: 32px;\n    font-weight: 600;\n    color: var(--text-dark);\n  }\n\n  &-meridiem {\n    display: flex;\n    flex-direction: column;\n    gap: 2px;\n    margin-left: 8px;\n\n    & button {\n      border: 1px solid var(--border-color);\n      background: var(--background-accent);\n      color: var(--text-dark);\n      font-size: 12px;\n      font-weight: 600;\n      padding: 3px 8px;\n      cursor: pointer;\n      -webkit-tap-highlight-color: transparent;\n\n      &:first-child {\n        border-radius: var(--default-border-radius) var(--default-border-radius) 0 0;\n      }\n\n      &:last-child {\n        border-radius: 0 0 var(--default-border-radius) var(--default-border-radius);\n      }\n    }\n\n    &-active {\n      background: var(--primary-color) !important;\n      color: var(--text-on-primary) !important;\n    }\n  }\n\n  &-footer {\n    display: flex;\n    justify-content: flex-end;\n    gap: 8px;\n    margin-top: 4px;\n  }\n}\n","import React, { useState, useEffect, useRef } from 'react';\nimport cx from 'classnames';\nimport MaskedInput from '../maskedInput';\nimport type { BeforeMaskedStateChangeStates, MaskState } from '../maskedInput';\nimport { faClock } from '@fortawesome/free-solid-svg-icons';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport Input from '../input';\nimport Modal from '../modal';\nimport Popover from '../popover';\nimport Button from '../button';\nimport Tooltip from '../tooltip';\nimport ClockDial from './components/clockDial';\nimport { PopoverAlign, PopoverPlacement } from '../popover/popover.types';\nimport { type TimeValue, parseTimeString, formatTime, snapMinute, clampToRange, clampHour } from './timeFunctions';\nimport type { TimeInputProps } from './timeInput.types';\n\nimport styles from './timeInput.module.pcss';\n\ntype ClockMode = 'hours' | 'minutes';\n\nexport default function TimeInput({\n  value,\n  selected,\n  onChange = () => null,\n  onSelect = () => null,\n  formValidator = null,\n  isInline = false,\n  isModal = false,\n  noInput = false,\n  noHeader = false,\n  showClearButton = false,\n  showNowButton = false,\n  minuteStep = 1,\n  use24Hour = true,\n  minTime = null,\n  maxTime = null,\n  label = '',\n  isValid = true,\n  errorMessage = '',\n  isRequired = false,\n  disabled = false,\n  className = '',\n  style,\n  classNames,\n  styles: slotStyles,\n  ...rest\n}: TimeInputProps) {\n  const getExternalString = (): string => {\n    if (formValidator) return (formValidator.value as string) || '';\n    return value ?? selected ?? '';\n  };\n\n  const [currentTimeString, setCurrentTimeString] = useState(getExternalString);\n  const [isPickerOpen, setIsPickerOpen] = useState(false);\n  const [mode, setMode] = useState<ClockMode>('hours');\n  const [isFocused, setIsFocused] = useState(false);\n  const [isHovered, setIsHovered] = useState(false);\n  const [, forceUpdate] = useState(0);\n\n  const externalString = getExternalString();\n  const [prevExternal, setPrevExternal] = useState(externalString);\n  const fvRef = useRef(formValidator);\n\n  // Sync when the controlled value changes externally.\n  if (!formValidator && prevExternal !== externalString) {\n    setPrevExternal(externalString);\n    setCurrentTimeString(externalString);\n  }\n\n  useEffect(() => {\n    fvRef.current = formValidator;\n    if (!formValidator) return;\n    const listener = () => {\n      setCurrentTimeString((formValidator.value as string) || '');\n      forceUpdate((n) => n + 1);\n    };\n    formValidator.registerOnUpdateListener(listener);\n    return () => formValidator.removeOnUpdateListener(listener);\n  }, [formValidator]);\n\n  const hasError = (): boolean => (formValidator ? formValidator.shouldShowError : !isValid || !!errorMessage);\n\n  const computedIsRequired = (): boolean =>\n    !!(isRequired || formValidator?.validators.find((v) => (v as { validatorId?: string }).validatorId === 'required'));\n\n  /** Push a value out to formValidator/onChange without touching the input string. */\n  const emit = (time: TimeValue | null) => {\n    const str = time ? formatTime(time) : null;\n    if (formValidator) formValidator.set(str);\n    onChange?.(str);\n  };\n\n  const applyTime = (time: TimeValue | null) => {\n    setCurrentTimeString(time ? formatTime(time) : '');\n    emit(time);\n  };\n\n  const parsed = parseTimeString(currentTimeString);\n  const dialTime: TimeValue = parsed ?? { hours: 12, minutes: 0 };\n\n  const handleDialChange = (time: TimeValue) => {\n    const snapped = { hours: time.hours, minutes: snapMinute(time.minutes, minuteStep) };\n    applyTime(clampToRange(snapped, minTime, maxTime));\n  };\n\n  const handleDialCommit = (time: TimeValue) => {\n    const snapped = { hours: time.hours, minutes: snapMinute(time.minutes, minuteStep) };\n    const next = clampToRange(snapped, minTime, maxTime);\n    applyTime(next);\n    if (mode === 'hours') {\n      setMode('minutes');\n      return;\n    }\n    onSelect?.(formatTime(next));\n    if (!isInline) setIsPickerOpen(false);\n  };\n\n  const openPicker = () => {\n    if (disabled) return;\n    setMode('hours');\n    setIsPickerOpen(true);\n  };\n\n  const beforeMaskedStateChange = ({ previousState, nextState }: BeforeMaskedStateChangeStates): MaskState => {\n    if (nextState.value === previousState.value) return nextState;\n    let v = nextState.value;\n    const hourStr = v.slice(0, 2);\n    if (/^\\d\\d$/.test(hourStr) && Number(hourStr) > 23) v = `23${v.slice(2)}`;\n    const minStr = v.slice(3, 5);\n    if (/^\\d\\d$/.test(minStr) && Number(minStr) > 59) v = `${v.slice(0, 3)}59`;\n    return v === nextState.value ? nextState : { ...nextState, value: v };\n  };\n\n  const handleInputChange = (v: string) => {\n    setCurrentTimeString(v);\n    const t = parseTimeString(v);\n    if (t) emit(t);\n  };\n\n  const handleBlur = () => {\n    if (fvRef.current) fvRef.current.touched = true;\n    setIsFocused(false);\n    emit(parseTimeString(currentTimeString));\n  };\n\n  const setMeridiem = (pm: boolean) => {\n    const isPm = dialTime.hours >= 12;\n    if (pm === isPm) return;\n    applyTime({ hours: clampHour(dialTime.hours + (pm ? 12 : -12)), minutes: dialTime.minutes });\n  };\n\n  const hasErr = hasError();\n  const displayHours = parsed\n    ? String(use24Hour ? parsed.hours : ((parsed.hours + 11) % 12) + 1).padStart(2, '0')\n    : '--';\n  const displayMinutes = parsed ? String(parsed.minutes).padStart(2, '0') : '--';\n\n  const header = noHeader ? null : (\n    <div className={cx(styles.timeInputClockHeader, classNames?.clockHeader)} style={slotStyles?.clockHeader}>\n      <button\n        type='button'\n        className={cx(\n          styles.timeInputHeaderSegment,\n          mode === 'hours' && styles.timeInputHeaderSegmentActive,\n          classNames?.headerSegment,\n        )}\n        style={slotStyles?.headerSegment}\n        onClick={() => setMode('hours')}\n      >\n        {displayHours}\n      </button>\n      <span className={styles.timeInputHeaderColon}>:</span>\n      <button\n        type='button'\n        className={cx(\n          styles.timeInputHeaderSegment,\n          mode === 'minutes' && styles.timeInputHeaderSegmentActive,\n          classNames?.headerSegment,\n        )}\n        style={slotStyles?.headerSegment}\n        onClick={() => setMode('minutes')}\n      >\n        {displayMinutes}\n      </button>\n      {!use24Hour && (\n        <div className={cx(styles.timeInputMeridiem, classNames?.meridiem)} style={slotStyles?.meridiem}>\n          <button\n            type='button'\n            className={cx(dialTime.hours < 12 && styles.timeInputMeridiemActive)}\n            onClick={() => setMeridiem(false)}\n          >\n            AM\n          </button>\n          <button\n            type='button'\n            className={cx(dialTime.hours >= 12 && styles.timeInputMeridiemActive)}\n            onClick={() => setMeridiem(true)}\n          >\n            PM\n          </button>\n        </div>\n      )}\n    </div>\n  );\n\n  const footer =\n    showClearButton || showNowButton ? (\n      <div className={cx(styles.timeInputFooter, classNames?.footer)} style={slotStyles?.footer}>\n        {showNowButton && (\n          <Button\n            onClick={() => {\n              const now = new Date();\n              handleDialChange({ hours: now.getHours(), minutes: now.getMinutes() });\n            }}\n          >\n            Now\n          </Button>\n        )}\n        {showClearButton && (\n          <Button\n            onClick={() => {\n              applyTime(null);\n              onSelect?.(null);\n            }}\n          >\n            Clear\n          </Button>\n        )}\n      </div>\n    ) : null;\n\n  const pickerContent = (\n    <div className={cx(styles.timeInputClock, classNames?.clock)} style={slotStyles?.clock}>\n      {header}\n      <ClockDial\n        time={dialTime}\n        mode={mode}\n        use24Hour={use24Hour}\n        minuteStep={minuteStep}\n        onChange={handleDialChange}\n        onCommit={handleDialCommit}\n        classNames={classNames}\n        styles={slotStyles}\n      />\n      {footer}\n    </div>\n  );\n\n  const input = noInput ? null : (\n    <Input\n      autoComplete='off'\n      label={`${label}${computedIsRequired() ? ' *' : ''}`}\n      value={currentTimeString}\n      icon={<FontAwesomeIcon icon={faClock} />}\n      onIconClick={openPicker}\n      onClick={openPicker}\n      readOnly={isModal}\n      disabled={disabled}\n      isValid={!hasErr}\n      customInput={<MaskedInput mask='99:99' beforeMaskedStateChange={beforeMaskedStateChange} />}\n      onChange={(v) => handleInputChange(v as string)}\n      onBlur={handleBlur}\n      onFocus={() => setIsFocused(true)}\n      onMouseEnter={() => setIsHovered(true)}\n      onMouseLeave={() => setIsHovered(false)}\n    />\n  );\n\n  const picker = (() => {\n    if (isInline) return pickerContent;\n    if (isModal) {\n      return (\n        <Modal hideHeader isOpen={isPickerOpen} onClose={() => setIsPickerOpen(false)}>\n          {pickerContent}\n        </Modal>\n      );\n    }\n    return (\n      <Popover\n        isOpen={isPickerOpen}\n        onOpenChange={setIsPickerOpen}\n        content={pickerContent}\n        placement={PopoverPlacement.Bottom}\n        align={PopoverAlign.Start}\n        // A click to place the caret in the field must not take the panel away.\n        // The focus stays there on its own: Popover only follows a keyboard open.\n        closeOnTriggerClick={false}\n        constrainWidth={false}\n      >\n        {input}\n      </Popover>\n    );\n  })();\n\n  return (\n    <div\n      className={cx(styles.timeInput, classNames?.root, className)}\n      style={{ ...slotStyles?.root, ...style }}\n      {...rest}\n    >\n      {(isModal || isInline) && input}\n      {picker}\n      <Tooltip\n        variant='error'\n        message={formValidator ? (formValidator.getCurrentErrorMessage() ?? '') : errorMessage || ''}\n        isVisible={hasErr && (isFocused || isHovered)}\n      />\n    </div>\n  );\n}\n",".timeline {\n  /* Theme tokens resolved once, each with a built-in fallback. Set any of the\n     public `--timeline-*` variables via ThemeProvider (theme-wide) or `style`\n     (per instance) to override; unset ones fall back to the values below. */\n  --custom-timeline-dot-size: var(--timeline-dot-size, 14px);\n  --custom-timeline-dot-color: var(--timeline-dot-color, var(--primary-color));\n  --custom-timeline-dot-border: var(--timeline-dot-border, var(--background));\n  --custom-timeline-line-color: var(--timeline-line-color, var(--border-color));\n  --custom-timeline-line-thickness: var(--timeline-line-thickness, 2px);\n  --custom-timeline-gap: var(--timeline-gap, 16px);\n  --custom-timeline-item-gap: var(--timeline-item-gap, 20px);\n  --custom-timeline-icon-dot-size: var(--timeline-icon-dot-size, 28px);\n\n  display: flex;\n  flex-direction: column;\n  color: var(--text-color);\n\n  &-item {\n    display: flex;\n    gap: var(--custom-timeline-gap);\n    border-radius: var(--default-border-radius);\n\n    &:last-child &-connector {\n      display: none;\n    }\n  }\n\n  &-item-clickable {\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n    transition: background-color 0.15s ease;\n    margin: 0 -10px;\n    padding: 0 10px;\n\n    &:hover {\n      background-color: var(--background-accent-light);\n    }\n\n    &:focus-visible {\n      outline: 2px solid var(--focus-border-color, var(--primary-color));\n      outline-offset: -2px;\n    }\n  }\n\n  &-item-disabled {\n    opacity: 0.5;\n  }\n\n  &-marker {\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    flex-shrink: 0;\n  }\n\n  &-dot {\n    width: var(--custom-timeline-dot-size);\n    height: var(--custom-timeline-dot-size);\n    border-radius: 50%;\n    background-color: var(--custom-timeline-dot-color);\n    box-shadow: 0 0 0 3px var(--custom-timeline-dot-border);\n    flex-shrink: 0;\n  }\n\n  /* Set by the component whenever the item has an icon — not inferred from the\n     rendered markup (`:has(svg)`), which would miss a custom icon element that\n     isn't an <svg>, e.g. an <img>. */\n  &-dot-with-icon {\n    width: var(--custom-timeline-icon-dot-size);\n    height: var(--custom-timeline-icon-dot-size);\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    color: var(--text-on-primary);\n    font-size: calc(var(--custom-timeline-icon-dot-size) * 0.5);\n  }\n\n  /* After -dot-with-icon, which it ties on specificity: a coloured dot's icon reads on its colour. */\n  &-dot-toned {\n    background-color: var(--tone);\n    color: var(--tone-contrast);\n  }\n\n  /* Neutral's --tone is its contrast colour too, so an icon needs the see-through fill behind it. */\n  &-dot-toned&-dot-with-icon {\n    background-color: var(--tone-fill);\n  }\n\n  /* Set by the component when ANY item has an icon. Every marker is then as wide as the\n     widest dot it could hold, so the connectors line up into one vertical spine and every\n     content column starts at the same x. max(), because a consumer is free to set the icon\n     dot smaller than the plain one. */\n  &-with-icons &-marker {\n    width: max(var(--custom-timeline-dot-size), var(--custom-timeline-icon-dot-size));\n  }\n\n  &-connector {\n    flex: 1 1 auto;\n    width: var(--custom-timeline-line-thickness);\n    min-height: 12px;\n    background-color: var(--custom-timeline-line-color);\n    margin: 4px 0;\n  }\n\n  &-content {\n    flex: 1 1 auto;\n    min-width: 0;\n    padding-bottom: var(--custom-timeline-item-gap);\n  }\n\n  &-header {\n    display: flex;\n    align-items: baseline;\n    justify-content: space-between;\n    gap: 8px;\n  }\n\n  &-title {\n    font-weight: 600;\n    color: var(--text-color);\n  }\n\n  &-timestamp {\n    flex-shrink: 0;\n    color: var(--text-dark);\n    font-size: var(--text-small-font-size, 0.85em);\n    white-space: nowrap;\n  }\n\n  &-description {\n    color: var(--text-dark);\n    margin-top: 2px;\n  }\n\n  /* Right: markers + line on the right, content on the left */\n  &-right &-item {\n    flex-direction: row-reverse;\n    text-align: right;\n  }\n\n  &-right &-header {\n    flex-direction: row-reverse;\n  }\n\n  /* Alternate: two-column layout, entries alternating left/right of a centered line */\n  &-alternate &-item {\n    display: grid;\n    grid-template-columns: 1fr auto 1fr;\n    align-items: start;\n    gap: var(--custom-timeline-gap);\n  }\n\n  &-alternate &-marker {\n    grid-column: 2;\n    grid-row: 1;\n  }\n\n  &-alternate &-content {\n    grid-column: 1;\n    grid-row: 1;\n    text-align: right;\n  }\n\n  &-alternate &-header {\n    flex-direction: row-reverse;\n  }\n\n  &-alternate &-item-right &-content {\n    grid-column: 3;\n    text-align: left;\n  }\n\n  &-alternate &-item-right &-header {\n    flex-direction: row;\n  }\n}\n","import type React from 'react';\nimport type { IconContent } from '../types/icon.types';\nimport type { SlotClassNames, SlotStyles, HtmlProps } from '../types/slots.types';\nimport type { ColorValue } from '../themeProvider/color.types';\n\n/** Layout of entries relative to the connecting line. */\nexport enum TimelineAlignment {\n  /** Line and markers on the left, content to the right (the default). */\n  Left = 'left',\n  /** Line and markers on the right, content to the left. */\n  Right = 'right',\n  /** Line down the center, entries alternating left/right. */\n  Alternate = 'alternate',\n}\n\nexport type TimelineSlots =\n  | 'root'\n  | 'item'\n  | 'marker'\n  | 'dot'\n  | 'connector'\n  | 'content'\n  | 'title'\n  | 'description'\n  | 'timestamp';\n\nexport interface TimelineItem {\n  /** Stable identity for the entry, used as its React key. Falls back to its array index when omitted; pass one whenever `items` can be reordered or have entries prepended (e.g. a live \"newest first\" audit log), so React doesn't misattribute row state across the shift. */\n  id?: string | number;\n  /** Primary label for the entry. */\n  title: React.ReactNode;\n  /** Secondary line under the title. */\n  description?: React.ReactNode;\n  /** Rendered next to the title, e.g. a date or relative time (\"2 hours ago\"). */\n  timestamp?: React.ReactNode;\n  /** Icon shown in the marker instead of a plain dot — a FontAwesome icon or any node. */\n  icon?: IconContent;\n  /** The marker's colour: a `ThemeColor` or any CSS colour. Unset, it is `--timeline-dot-color`. */\n  color?: ColorValue;\n  /** Extra content rendered under the description, e.g. a diff, an attachment, an actor avatar. */\n  content?: React.ReactNode;\n  /** Makes the whole entry clickable (e.g. to open the full record), rendering it as an accessible button. */\n  onClick?: (event: React.MouseEvent<HTMLDivElement>) => void;\n  /** Disables the entry: never clickable, dimmed. Only relevant alongside `onClick`. */\n  disabled?: boolean;\n}\n\nexport interface TimelineProps extends HtmlProps {\n  /** The entries to render, in display order (typically newest first for an audit log). */\n  items?: TimelineItem[];\n  /** Layout of entries relative to the connecting line (default `Left`). */\n  alignment?: TimelineAlignment;\n  className?: string;\n  style?: React.CSSProperties;\n  classNames?: SlotClassNames<TimelineSlots>;\n  styles?: SlotStyles<TimelineSlots>;\n}\n","import React from 'react';\nimport cx from 'classnames';\nimport InteractableDiv from '../interactableDiv';\nimport styles from './timeline.module.pcss';\nimport { TimelineAlignment, type TimelineProps, type TimelineItem } from './timeline.types';\nimport { renderIcon } from '../utils/renderIcon';\nimport { toneProps, useToneCheck } from '../styles/tone';\n\nexport default function Timeline({\n  items = [],\n  alignment = TimelineAlignment.Left,\n  className,\n  style,\n  classNames,\n  styles: slotStyles,\n  ...rest\n}: TimelineProps) {\n  // An icon dot is bigger than a plain one, and the marker is only as wide as the dot\n  // inside it — so mixing the two put every row's spine and content column at a different\n  // x. Widening the marker for the whole timeline fixes the alignment; doing it only when\n  // an icon is actually present leaves an icon-less timeline exactly where it was.\n  const hasIcons = items.some((item: TimelineItem) => !!item.icon);\n  useToneCheck(\n    'Timeline',\n    items.map((item) => item.color),\n  );\n\n  return (\n    <div\n      {...rest}\n      className={cx(\n        styles.timeline,\n        {\n          [styles.timelineRight]: alignment === TimelineAlignment.Right,\n          [styles.timelineAlternate]: alignment === TimelineAlignment.Alternate,\n          [styles.timelineWithIcons]: hasIcons,\n        },\n        classNames?.root,\n        className,\n      )}\n      style={{ ...slotStyles?.root, ...style }}\n    >\n      {items.map((item: TimelineItem, index: number) => {\n        const key = item.id ?? index;\n        const isLast = index === items.length - 1;\n        const isAlternateRight = alignment === TimelineAlignment.Alternate && index % 2 === 1;\n        const isClickable = !!item.onClick && !item.disabled;\n        const tone = item.color ? toneProps(item.color) : undefined;\n\n        const itemContent = (\n          <>\n            <div className={cx(styles.timelineMarker, classNames?.marker)} style={slotStyles?.marker}>\n              <div\n                className={cx(\n                  styles.timelineDot,\n                  { [styles.timelineDotWithIcon]: !!item.icon },\n                  tone && [tone.className, styles.timelineDotToned],\n                  classNames?.dot,\n                )}\n                style={{ ...tone?.style, ...slotStyles?.dot }}\n              >\n                {item.icon ? renderIcon(item.icon) : null}\n              </div>\n              {!isLast && (\n                <div className={cx(styles.timelineConnector, classNames?.connector)} style={slotStyles?.connector} />\n              )}\n            </div>\n            <div className={cx(styles.timelineContent, classNames?.content)} style={slotStyles?.content}>\n              <div className={styles.timelineHeader}>\n                <div className={cx(styles.timelineTitle, classNames?.title)} style={slotStyles?.title}>\n                  {item.title}\n                </div>\n                {item.timestamp != null && (\n                  <div className={cx(styles.timelineTimestamp, classNames?.timestamp)} style={slotStyles?.timestamp}>\n                    {item.timestamp}\n                  </div>\n                )}\n              </div>\n              {item.description != null && (\n                <div\n                  className={cx(styles.timelineDescription, classNames?.description)}\n                  style={slotStyles?.description}\n                >\n                  {item.description}\n                </div>\n              )}\n              {item.content}\n            </div>\n          </>\n        );\n\n        const itemClassName = cx(\n          styles.timelineItem,\n          { [styles.timelineItemRight]: isAlternateRight },\n          { [styles.timelineItemClickable]: isClickable },\n          { [styles.timelineItemDisabled]: item.disabled },\n          classNames?.item,\n        );\n\n        if (isClickable) {\n          return (\n            <InteractableDiv key={key} className={itemClassName} style={slotStyles?.item} onClick={item.onClick}>\n              {itemContent}\n            </InteractableDiv>\n          );\n        }\n\n        return (\n          <div key={key} className={itemClassName} style={slotStyles?.item} aria-disabled={item.disabled || undefined}>\n            {itemContent}\n          </div>\n        );\n      })}\n    </div>\n  );\n}\n","// A short two-tone chime (880Hz -> 1320Hz, ~0.35s, 11025Hz mono WAV), used as the default\n// completion sound for playSoundOnComplete. Generated, not recorded — kept tiny (~10KB\n// base64) since it's opt-in and only paid for by consumers who import Timer.\nexport const DEFAULT_CHIME_DATA_URI =\n  'data:audio/wav;base64,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AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANACUAKwAKAMr/kv+R/9n/TwCxALwAVwCs/xb/8/5p/0YAFgFfAeYA3f/N/lP+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';\n",".timer {\n  /* em, because the readout inside the ring is em too: a fixed ring stayed put while its own\n     digits grew with the theme until they filled it. */\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 1em;\n  width: fit-content;\n\n  &-ringWrapper {\n    position: relative;\n    width: var(--timer-size, 10em);\n    height: var(--timer-size, 10em);\n    transform-origin: center;\n  }\n\n  &-ringSvg {\n    position: relative;\n    width: 100%;\n    height: 100%;\n    overflow: visible;\n  }\n\n  &-ripples {\n    position: absolute;\n    inset: 0;\n    pointer-events: none;\n  }\n\n  &-ripple {\n    position: absolute;\n    inset: 0;\n    border-radius: 50%;\n    border: 0.125em solid var(--timer-ripple-color, var(--timer-progress-color, var(--primary-color)));\n    opacity: 0;\n    transform: scale(0.94);\n\n    /* One-shot per spawned instance (see the ripple spawner in timer.tsx), not an infinite\n       loop — so pausing simply stops new spawns while already-mid-flight ripples keep\n       animating to completion via `forwards` instead of vanishing mid-cycle. */\n    animation: timerRippleHalo 3s ease-out forwards;\n  }\n\n  /* Not em, unlike everything else here: inside the svg this is a viewBox user unit, so it\n     already scales with the ring, and it has to stay equal to the `strokeWidth` prop that\n     timer.tsx subtracts from the circle's radius. */\n  &-ringTrack {\n    fill: none;\n\n    /* Not --background-accent-light, which is Card's own surface: on a Card the ring vanished. A\n       tint of the text colour shows on any surface. */\n    stroke: var(--timer-track-color, color-mix(in srgb, currentcolor 20%, transparent));\n    stroke-width: var(--timer-stroke-width, 10px);\n  }\n\n  &-ringProgress {\n    fill: none;\n    stroke: var(--timer-progress-color, var(--primary-color));\n    stroke-width: var(--timer-stroke-width, 10px);\n    stroke-linecap: round;\n\n    /* Single combined transform, not split across an SVG `transform` attribute on this element\n       plus a separate CSS transform on an ancestor — that split combination, together with an\n       actively-transitioning stroke-dashoffset, is a known trigger for SVG dash-rendering glitches\n       in some engines (the visible arc appearing to jump/drift independent of the actual\n       dashoffset value). scaleX(-1) mirrors so the depleting boundary sweeps clockwise (matching\n       a clock face) instead of the counter-clockwise direction a <circle>'s implicit path winds\n       by default; rotate(-90deg) moves the fixed anchor from 3 o'clock to 12 o'clock. Both applied\n       around the circle's own center, which coincides with the viewBox center here. `counterClockwise`\n       overrides this inline (timer.tsx) by dropping the scaleX(-1), keeping just the rotate — that\n       default winding direction is exactly counter-clockwise, so no mirroring is needed for it. */\n    transform: scaleX(-1) rotate(-90deg);\n    transform-origin: 50% 50%;\n\n    /* Matches (slightly exceeds) the JS tick interval (TICK_MS in timer.tsx) so the ring\n       tracks the real remaining time closely instead of perpetually chasing a target that's\n       always a full second stale — a much longer transition here reads as sluggish since the\n       rendered ring never catches up to the true value while ticking continues. */\n    transition:\n      stroke-dashoffset 220ms linear,\n      stroke 0.3s ease;\n\n    &-urgent {\n      stroke: var(--timer-urgent-color, var(--error-color));\n    }\n  }\n\n  &-display {\n    position: absolute;\n    inset: 0;\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    justify-content: center;\n    text-align: center;\n    padding: 0 0.75em;\n  }\n\n  &-time {\n    font-size: 1.4em;\n    font-weight: 600;\n    font-variant-numeric: tabular-nums;\n    color: var(--timer-text-color, var(--text-color));\n  }\n\n  &-label {\n    margin-top: 0.25em;\n    font-size: 0.8em;\n    color: var(--timer-label-color, var(--text-dark));\n  }\n\n  &-controls {\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    gap: 0.5em;\n  }\n\n  &-controlsRow {\n    display: flex;\n    align-items: center;\n    gap: 0.5em;\n  }\n\n  &-cancelButton {\n    opacity: 0.6;\n\n    &:hover {\n      opacity: 1;\n    }\n  }\n\n  &-pulsing &-ringWrapper {\n    animation: timerHeartbeat 1s ease-in-out infinite;\n  }\n\n  &-critical &-ringWrapper {\n    animation-duration: 0.6s;\n  }\n}\n\n@keyframes timerHeartbeat {\n  0%,\n  100% {\n    transform: scale(1);\n  }\n\n  50% {\n    transform: scale(1.045);\n  }\n}\n\n@keyframes timerRippleHalo {\n  0% {\n    transform: scale(0.94);\n    opacity: 0.5;\n  }\n\n  100% {\n    transform: scale(1.35);\n    opacity: 0;\n  }\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .timer-ringProgress {\n    transition: stroke 0.3s ease;\n  }\n\n  .timer-pulsing .timer-ringWrapper {\n    animation: none;\n  }\n\n  .timer-ripple {\n    animation: none;\n    opacity: 0;\n  }\n}\n","import { useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';\nimport cx from 'classnames';\nimport { faPlus, faMinus, faPause, faPlay, faXmark } from '@fortawesome/free-solid-svg-icons';\n\nimport ActionIcon from '../actionIcon';\nimport Button from '../button';\nimport { ButtonSize } from '../button/button.types';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport { DEFAULT_CHIME_DATA_URI } from './timer.chime';\nimport type { TimerLabels, TimerProps, TimerStatus } from './timer.types';\nimport styles from './timer.module.pcss';\n\nconst TICK_MS = 200;\nconst RIPPLE_SPAWN_INTERVAL_MS = 1000;\n\nconst DEFAULT_LABELS: Required<TimerLabels> = {\n  cancel: 'Cancel timer',\n};\n\nfunction prefersReducedMotion(): boolean {\n  return typeof window !== 'undefined' && window.matchMedia?.('(prefers-reduced-motion: reduce)').matches === true;\n}\n\nfunction formatTimeDefault(remainingMs: number) {\n  const totalSeconds = Math.ceil(remainingMs / 1000);\n  const hours = Math.floor(totalSeconds / 3600);\n  const minutes = Math.floor((totalSeconds % 3600) / 60);\n  const seconds = totalSeconds % 60;\n  const mm = String(minutes).padStart(2, '0');\n  const ss = String(seconds).padStart(2, '0');\n  return hours > 0 ? `${hours}:${mm}:${ss}` : `${mm}:${ss}`;\n}\n\nfunction Timer(props: TimerProps) {\n  const {\n    ref,\n    durationSeconds,\n    autoStart = true,\n    resetOnDurationChange = true,\n\n    onTick,\n    onComplete,\n    onStart,\n    onPause,\n    onResume,\n    onCancel,\n    onAddTime,\n    onRemoveTime,\n\n    showControls = true,\n    hideAddButton = false,\n    hideRemoveButton = false,\n    hidePauseButton = false,\n    hideCancelButton = false,\n    adjustSeconds = 30,\n    maxSeconds,\n\n    label,\n    formatTime = formatTimeDefault,\n    size,\n    strokeWidth = 10,\n    counterClockwise = false,\n\n    urgentThreshold = 0.1,\n    urgentMinSeconds = 3,\n    pulseOnUrgent = true,\n    showRunningRipples = false,\n\n    playSoundOnComplete = false,\n    soundUrl,\n    soundVolume = 0.5,\n    notifyOnComplete = false,\n    notificationTitle,\n    notificationBody,\n\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    labels,\n    ...rest\n  } = useComponentDefaults('Timer', props);\n  const text = useLabels('Timer', labels, DEFAULT_LABELS);\n\n  const initialMs = durationSeconds * 1000;\n  const [status, setStatusState] = useState<TimerStatus>(autoStart ? 'running' : 'idle');\n  const [remainingMs, setRemainingMsState] = useState(initialMs);\n  const [epoch, setEpoch] = useState(0);\n  const [ripples, setRipples] = useState<number[]>([]);\n\n  const remainingRef = useRef(remainingMs);\n  const statusRef = useRef(status);\n  const totalMsRef = useRef(initialMs);\n  const completedFiredRef = useRef(false);\n  const notifyPermissionRequestedRef = useRef(false);\n  const mountedRef = useRef(false);\n  const rippleIdRef = useRef(0);\n\n  function setRemaining(next: number) {\n    remainingRef.current = next;\n    setRemainingMsState(next);\n  }\n\n  function setStatus(next: TimerStatus) {\n    statusRef.current = next;\n    setStatusState(next);\n  }\n\n  function bumpEpoch() {\n    setEpoch((e) => e + 1);\n  }\n\n  // Ticking effect. Re-derives the remaining time from Date.now() every tick (rather than\n  // accumulating setInterval's own drift), so it stays accurate over long countdowns. Any\n  // external mutation of remainingRef (addTime/removeTime while already running, or a resume)\n  // must bump `epoch` so this effect re-arms and re-captures a fresh start/startRemaining —\n  // without that, a stale `startRemaining` captured before the mutation would silently\n  // overwrite it on the next tick.\n  useEffect(() => {\n    if (status !== 'running') return undefined;\n    const start = Date.now();\n    const startRemaining = remainingRef.current;\n    const id = setInterval(() => {\n      const next = Math.max(0, startRemaining - (Date.now() - start));\n      setRemaining(next);\n      onTick?.(next);\n      if (next <= 0) setStatus('completed');\n    }, TICK_MS);\n    return () => clearInterval(id);\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [status, epoch]);\n\n  // Fires onComplete exactly once per completion, plus the opt-in sound/notification.\n  useEffect(() => {\n    if (status === 'completed' && !completedFiredRef.current) {\n      completedFiredRef.current = true;\n      onComplete?.();\n      if (playSoundOnComplete) {\n        try {\n          const audio = new Audio(soundUrl || DEFAULT_CHIME_DATA_URI);\n          audio.volume = soundVolume;\n          // play() can reject (autoplay policy) or, in some environments, return undefined\n          // instead of a Promise — never let a blocked/unsupported playback surface as an error.\n          audio.play()?.catch(() => {});\n        } catch {\n          // Audio unavailable/unsupported — fail silently.\n        }\n      }\n      if (notifyOnComplete && typeof Notification !== 'undefined' && Notification.permission === 'granted') {\n        try {\n          new Notification(notificationTitle ?? 'Timer done', {\n            body: notificationBody ?? (typeof label === 'string' ? label : undefined),\n          });\n        } catch {\n          // Some browsers (e.g. certain mobile Safari versions) throw constructing\n          // Notification outside specific contexts — fail silently, as documented.\n        }\n      }\n    }\n    if (status !== 'completed') completedFiredRef.current = false;\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [status]);\n\n  // durationSeconds prop change: reset (and optionally restart) at the new duration. Skips\n  // the initial mount, since autoStart/initial state already cover that.\n  useEffect(() => {\n    if (!mountedRef.current) {\n      mountedRef.current = true;\n      return;\n    }\n    const nextMs = durationSeconds * 1000;\n    totalMsRef.current = nextMs;\n    setRemaining(nextMs);\n    completedFiredRef.current = false;\n    bumpEpoch();\n    if (resetOnDurationChange) {\n      setStatus(autoStart ? 'running' : 'idle');\n    }\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [durationSeconds]);\n\n  // Running-ripple spawner: spawns a new ripple roughly every RIPPLE_SPAWN_INTERVAL_MS while\n  // running, each animating once (via CSS, `forwards`) and removing itself on `onAnimationEnd`.\n  // Pausing (or otherwise leaving `running`) only stops new spawns here — it doesn't touch\n  // `ripples` — so whichever ripples are already mid-flight keep animating to completion\n  // instead of vanishing mid-cycle.\n  useEffect(() => {\n    if (!showRunningRipples || status !== 'running' || prefersReducedMotion()) return undefined;\n    function spawnRipple() {\n      // Read now, not in the updater: two spawns before a render would both see the later id.\n      const rippleId = ++rippleIdRef.current;\n      setRipples((prev) => [...prev, rippleId]);\n    }\n    // A timer, so the cleanup cancels it too: StrictMode's second mount stacked a second ripple.\n    const first = setTimeout(spawnRipple, 0);\n    const id = setInterval(spawnRipple, RIPPLE_SPAWN_INTERVAL_MS);\n    return () => {\n      clearTimeout(first);\n      clearInterval(id);\n    };\n  }, [showRunningRipples, status]);\n\n  function handleRippleAnimationEnd(id: number) {\n    setRipples((prev) => prev.filter((existingId) => existingId !== id));\n  }\n\n  function requestNotificationPermissionIfNeeded() {\n    if (!notifyOnComplete || notifyPermissionRequestedRef.current) return;\n    if (typeof Notification === 'undefined' || Notification.permission !== 'default') return;\n    notifyPermissionRequestedRef.current = true;\n    Notification.requestPermission().catch(() => {});\n  }\n\n  function start() {\n    requestNotificationPermissionIfNeeded();\n    if (statusRef.current === 'completed' || statusRef.current === 'cancelled') {\n      setRemaining(totalMsRef.current);\n      completedFiredRef.current = false;\n    }\n    bumpEpoch();\n    setStatus('running');\n    onStart?.();\n  }\n\n  function pause() {\n    if (statusRef.current !== 'running') return;\n    setStatus('paused');\n    onPause?.();\n  }\n\n  function resume() {\n    if (statusRef.current !== 'paused') return;\n    bumpEpoch();\n    setStatus('running');\n    onResume?.();\n  }\n\n  function addTime(seconds: number) {\n    const capped = maxSeconds != null ? maxSeconds * 1000 : Infinity;\n    const next = Math.min(remainingRef.current + seconds * 1000, capped);\n    setRemaining(next);\n    bumpEpoch();\n    onAddTime?.(seconds, Math.round(next / 1000));\n    if (statusRef.current === 'completed' && next > 0) {\n      completedFiredRef.current = false;\n      setStatus('running');\n    }\n  }\n\n  function removeTime(seconds: number) {\n    const next = Math.max(0, remainingRef.current - seconds * 1000);\n    setRemaining(next);\n    bumpEpoch();\n    onRemoveTime?.(seconds, Math.round(next / 1000));\n    if (next <= 0 && statusRef.current === 'running') setStatus('completed');\n  }\n\n  function cancel() {\n    setRemaining(totalMsRef.current);\n    setStatus('cancelled');\n    onCancel?.();\n  }\n\n  function reset() {\n    setRemaining(totalMsRef.current);\n    completedFiredRef.current = false;\n    setStatus('idle');\n  }\n\n  useImperativeHandle(ref, () => ({\n    start,\n    pause,\n    resume,\n    addTime,\n    removeTime,\n    cancel,\n    reset,\n    getStatus: () => statusRef.current,\n    getRemainingMs: () => remainingRef.current,\n  }));\n\n  const radius = 50 - strokeWidth / 2;\n  const circumference = useMemo(() => 2 * Math.PI * radius, [radius]);\n  const clampedFraction = totalMsRef.current > 0 ? Math.min(1, remainingMs / totalMsRef.current) : 0;\n  const dashOffset = circumference * (1 - clampedFraction);\n  const urgentMs = Math.max(totalMsRef.current * urgentThreshold, urgentMinSeconds * 1000);\n  const isUrgent = status === 'running' && remainingMs <= urgentMs;\n  const isCritical = isUrgent && remainingMs <= Math.min(5000, urgentMs);\n  const isPaused = status === 'paused';\n\n  // The pause button doubles as a play/restart button whenever there's nothing to pause —\n  // idle (autoStart={false}), completed, or cancelled — rather than hiding itself, so there's\n  // always a way to (re)start the countdown from the built-in controls alone.\n  const showAddButton = showControls && !hideAddButton;\n  const showRemoveButton = showControls && !hideRemoveButton;\n  const showPauseButton = showControls && !hidePauseButton;\n  const pauseButtonIcon = status === 'running' ? faPause : faPlay;\n  const pauseButtonTitle =\n    status === 'running' ? 'Pause' : isPaused ? 'Resume' : status === 'idle' ? 'Start' : 'Restart';\n  const handlePauseButtonClick = status === 'running' ? pause : isPaused ? resume : start;\n  const showCancelButton = showControls && !hideCancelButton;\n\n  return (\n    <div\n      {...rest}\n      className={cx(\n        styles.timer,\n        pulseOnUrgent && isUrgent && styles.timerPulsing,\n        isCritical && styles.timerCritical,\n        className,\n        classNames?.root,\n      )}\n      style={\n        {\n          ...(size ? { '--timer-size': size } : null),\n          '--timer-stroke-width': `${strokeWidth}px`,\n          ...style,\n          ...slotStyles?.root,\n        } as React.CSSProperties\n      }\n    >\n      <div className={cx(styles.timerRingWrapper, classNames?.ringWrapper)} style={slotStyles?.ringWrapper}>\n        {ripples.length > 0 && (\n          <div className={cx(styles.timerRipples, classNames?.ripples)} style={slotStyles?.ripples}>\n            {ripples.map((id) => (\n              <span\n                key={id}\n                className={cx(styles.timerRipple, classNames?.ripple)}\n                style={slotStyles?.ripple}\n                onAnimationEnd={() => handleRippleAnimationEnd(id)}\n              />\n            ))}\n          </div>\n        )}\n        <svg className={cx(styles.timerRingSvg, classNames?.ringSvg)} viewBox='0 0 100 100' style={slotStyles?.ringSvg}>\n          <circle\n            className={cx(styles.timerRingTrack, classNames?.ringTrack)}\n            cx='50'\n            cy='50'\n            r={radius}\n            style={slotStyles?.ringTrack}\n          />\n          <circle\n            className={cx(\n              styles.timerRingProgress,\n              isUrgent && styles.timerRingProgressUrgent,\n              classNames?.ringProgress,\n            )}\n            cx='50'\n            cy='50'\n            r={radius}\n            strokeDasharray={circumference}\n            strokeDashoffset={dashOffset}\n            style={{\n              transition: isPaused ? 'none' : undefined,\n              transform: counterClockwise ? 'rotate(-90deg)' : undefined,\n              ...slotStyles?.ringProgress,\n            }}\n          />\n        </svg>\n        <div className={cx(styles.timerDisplay, classNames?.display)} style={slotStyles?.display}>\n          <span className={cx(styles.timerTime, classNames?.time)} style={slotStyles?.time}>\n            {formatTime(remainingMs)}\n          </span>\n          {label && (\n            <span className={cx(styles.timerLabel, classNames?.label)} style={slotStyles?.label}>\n              {label}\n            </span>\n          )}\n        </div>\n      </div>\n\n      {showControls && (\n        <div className={cx(styles.timerControls, classNames?.controls)} style={slotStyles?.controls}>\n          <div className={styles.timerControlsRow}>\n            {showRemoveButton && (\n              <ActionIcon\n                icon={faMinus}\n                title={`Remove ${adjustSeconds}s`}\n                onClick={() => removeTime(adjustSeconds)}\n                className={classNames?.removeButton}\n                style={slotStyles?.removeButton}\n              />\n            )}\n            {showPauseButton && (\n              <Button\n                size={ButtonSize.Small}\n                icon={pauseButtonIcon}\n                title={pauseButtonTitle}\n                onClick={handlePauseButtonClick}\n                className={classNames?.pauseButton}\n                style={slotStyles?.pauseButton}\n              />\n            )}\n            {showAddButton && (\n              <ActionIcon\n                icon={faPlus}\n                title={`Add ${adjustSeconds}s`}\n                onClick={() => addTime(adjustSeconds)}\n                className={classNames?.addButton}\n                style={slotStyles?.addButton}\n              />\n            )}\n          </div>\n          {showCancelButton && (\n            <ActionIcon\n              icon={faXmark}\n              title={text.cancel}\n              onClick={cancel}\n              className={cx(styles.timerCancelButton, classNames?.cancelButton)}\n              style={slotStyles?.cancelButton}\n            />\n          )}\n        </div>\n      )}\n    </div>\n  );\n}\n\nexport default Timer;\n",".virtualList {\n  overflow-y: auto;\n  box-sizing: border-box;\n  color: var(--text-color);\n  font-family: var(--default-font);\n\n  /* Disable the browser's own scroll anchoring — the component does its own\n     scroll-position correction when row heights change, and the two fighting\n     each other causes the thumb to desync while dragging. */\n  overflow-anchor: none;\n\n  /* Reserve the scrollbar gutter so rows don't re-wrap (and re-measure) when the\n     vertical scrollbar appears/disappears as content grows. */\n  scrollbar-gutter: stable;\n\n  /* Establish a stacking context so every z-index inside the list (sticky\n     header, toggle popover, drag clone) stays scoped to this component and\n     can't paint over outside content like a modal. */\n  isolation: isolate;\n\n  &-drag-clone {\n    box-sizing: border-box;\n    contain: layout paint style;\n    cursor: grabbing;\n\n    /* It tracks the finger via transform — never animate that. */\n    transition: none;\n  }\n\n  &-header {\n    display: grid;\n    position: sticky;\n\n    /* With `scrollParent` the header sticks to the page's scrollport, where a\n       fixed app bar may already sit; the list is given the offset from outside. */\n    top: var(--vl-header-top, 0);\n\n    /* Frozen layouts are exact pixels and may total more than the viewport, so\n       the header and the row canvas below both take that total and the list\n       scrolls sideways. `--vl-total` is only set once a column has been\n       resized; until then this resolves to 100% and nothing changes. */\n    width: max(100%, var(--vl-total, 0px));\n\n    /* Above focused rows (z-index 1) so a focused row scrolled under the header\n       never paints over it; the drag clone (z-index 3) still floats above both. */\n    z-index: 2;\n    background: var(--background-accent);\n    border-bottom: 1px solid var(--border-color);\n    box-sizing: border-box;\n\n    &-cell {\n      padding: 8px 12px;\n      font-weight: 600;\n      font-size: 13px;\n      color: var(--text-dark);\n      overflow: hidden;\n      text-overflow: ellipsis;\n      white-space: nowrap;\n      box-sizing: border-box;\n\n      /* Anchors the resize handle to the cell's trailing edge. */\n      position: relative;\n\n      /* The cell, not the button inside it, carries the header's tab stop, so\n         this is the ring a keyboard user follows. Inset because the cell clips\n         its overflow and an outward outline would be cut off. */\n      &:focus-visible {\n        outline: 1px solid var(--focus-border-color, var(--primary-color));\n        outline-offset: -1px;\n      }\n    }\n\n    /* Resize handle: a grab strip on the column's trailing edge, wider than the\n       line it sits on because a 1px target is unhittable. It stays inside the\n       cell rather than straddling the edge, since the cell clips its overflow\n       to keep a long header out of its neighbour. */\n    &-resize {\n      position: absolute;\n      top: 0;\n      bottom: 0;\n      right: 0;\n      width: 10px;\n      cursor: col-resize;\n      -webkit-tap-highlight-color: transparent;\n      touch-action: none;\n\n      /* The line itself, drawn on the trailing edge and revealed on hover. */\n      &::after {\n        content: '';\n        position: absolute;\n        top: 4px;\n        bottom: 4px;\n        right: 0;\n        width: 2px;\n        border-radius: 1px;\n        background: var(--primary-color);\n        opacity: 0;\n        transition: opacity 0.12s;\n      }\n\n      &:hover::after,\n      &:active::after {\n        opacity: 1;\n      }\n    }\n\n    /* The clickable part of a sortable header cell. Strips the button chrome and\n       inherits the cell's typography, so a sortable header looks identical to a\n       plain one until it's hovered. */\n    &-sort {\n      font: inherit;\n      color: inherit;\n      background: none;\n      border: none;\n      margin: 0;\n      padding: 0;\n      display: flex;\n      align-items: center;\n      gap: 6px;\n      min-width: 0;\n      max-width: 100%;\n      cursor: pointer;\n      -webkit-tap-highlight-color: transparent;\n\n      &:focus-visible {\n        outline: 1px solid var(--focus-border-color, var(--primary-color));\n        outline-offset: 2px;\n      }\n\n      /* The cell's own ellipsis can't apply once its content is a flex button,\n         so the truncation moves onto the label. */\n      &-label {\n        overflow: hidden;\n        text-overflow: ellipsis;\n        white-space: nowrap;\n      }\n\n      &-icon {\n        flex: none;\n        font-size: 11px;\n        opacity: 0.35;\n        transition:\n          opacity 0.15s,\n          color 0.15s;\n\n        &-active {\n          opacity: 1;\n          color: var(--primary-color);\n        }\n      }\n\n      &:hover &-icon {\n        opacity: 0.75;\n      }\n\n      &:hover &-icon-active {\n        opacity: 1;\n      }\n    }\n\n    &-toggle {\n      background: none;\n      border: none;\n      cursor: pointer;\n      -webkit-tap-highlight-color: transparent;\n      padding: 4px 8px;\n      color: var(--text-dark);\n      display: flex;\n      align-items: center;\n      border-radius: var(--default-border-radius);\n      transition: background 0.15s;\n\n      &:hover {\n        background: var(--background-accent-light);\n        color: var(--text-color);\n      }\n    }\n  }\n\n  /* The entries pad themselves so a hover highlight reaches the panel edge. */\n  &-toggle-panel {\n    padding: 4px 0;\n  }\n\n  &-toggle {\n    &-wrapper {\n      position: absolute;\n      right: 0;\n      top: 0;\n      bottom: 0;\n      display: flex;\n      align-items: center;\n      padding: 0 4px;\n    }\n\n    /* Chrome (background, shadow, radius, positioning) is provided by the\n       Popover panel this renders inside — keep only sizing here. */\n    &-popover {\n      display: flex;\n      flex-direction: column;\n      min-width: 160px;\n    }\n\n    &-item {\n      padding: 4px 12px;\n      white-space: nowrap;\n      transition: background 0.1s;\n      font-size: 14px;\n\n      /* `:focus` as well, because the panel takes the focus when the keyboard\n         opens it, and the checkbox itself is the focusable element. Not\n         `--background-accent-light`: it is the same colour as that panel in the\n         default theme. */\n      &:hover,\n      &:focus {\n        background: var(--background-accent);\n      }\n    }\n\n    /* Only rendered once a column has actually been resized, so it reads as an\n       undo for something the user just did rather than a permanent option. */\n    &-reset {\n      margin-top: 4px;\n      padding: 6px 12px;\n      border: none;\n      border-top: 1px solid var(--border-color);\n      background: none;\n      font: inherit;\n      font-size: 13px;\n      color: var(--text-color);\n      text-align: left;\n      white-space: nowrap;\n      cursor: pointer;\n      -webkit-tap-highlight-color: transparent;\n      transition: background 0.1s;\n\n      &:hover,\n      &:focus {\n        background: var(--background-accent);\n      }\n    }\n  }\n\n  /* Count badge on the column-toggle cog: how many columns the user has hidden.\n     Positioned at the cog's upper-right corner; non-interactive. */\n  &-column-chip {\n    position: absolute;\n    top: 0;\n    right: 0;\n    min-width: 16px;\n    height: 16px;\n    padding: 0 4px;\n    box-sizing: border-box;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    background: var(--primary-color);\n    color: var(--text-on-primary);\n    font-size: 10px;\n    font-weight: 600;\n    line-height: 1;\n    border-radius: 8px;\n    pointer-events: none;\n  }\n\n  &-select-cell {\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    padding: 0 4px;\n    box-sizing: border-box;\n\n    /* Only the header's select-all cell is focusable; the ones in rows are not. */\n    &:focus-visible {\n      outline: 1px solid var(--focus-border-color, var(--primary-color));\n      outline-offset: -1px;\n    }\n  }\n\n  &-body {\n    position: relative;\n    box-sizing: border-box;\n\n    /* Rows are absolute with left/right 0, so they inherit this width and stay\n       aligned with the header when the table is wider than the viewport. */\n    width: max(100%, var(--vl-total, 0px));\n  }\n\n  /* The list itself holds the tab stop, so the active row cannot use a\n     `:focus-visible` of its own. Ringing the list instead would mark the whole\n     table rather than the row the arrow keys are on, so the list drops its own\n     outline and lends it to that row. Scoped to `:focus-visible`, a mouse user\n     never sees it. */\n  &:focus-visible {\n    outline: none;\n\n    & .virtualList-row-active {\n      outline: 2px solid var(--focus-border-color, var(--primary-color));\n      outline-offset: -2px;\n    }\n  }\n\n  /* A resize drag travels over the rows, and their hover highlight following the\n     pointer is noise. Inert rather than a `:hover` override, which would have to\n     out-specify the selected-row rule to work. The drag itself is unaffected:\n     pointer capture bypasses hit testing. */\n  &-resizing &-body {\n    pointer-events: none;\n  }\n\n  &-row {\n    border-bottom: 1px solid var(--border-color);\n    box-sizing: border-box;\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n    transition: background 0.1s;\n\n    /* Isolate each row's layout/style so measuring or restyling one row can't force\n       the whole list to re-layout — keeps fast scrolling smooth. */\n    contain: layout style;\n\n    &-no-divider {\n      border-bottom: none;\n    }\n\n    &:hover {\n      background: var(--background-accent-light);\n    }\n\n    /* Opt out of the hover highlight (e.g. when each row is a Card that handles\n       its own hover). Declared before the `-selected` rule below, so a selected\n       row's highlight still wins on hover. */\n    &-no-hover:hover {\n      background: transparent;\n    }\n\n    &-selected {\n      background: var(--primary-lighter);\n\n      &:hover {\n        background: var(--primary-lighter);\n      }\n    }\n\n    &-draggable {\n      cursor: grab;\n\n      /* Stop the mobile long-press from selecting text or showing the callout. */\n      user-select: none;\n      -webkit-touch-callout: none;\n\n      &:active {\n        cursor: grabbing;\n      }\n    }\n\n    &-dragging {\n      opacity: 0.4;\n    }\n\n    /* Tree-reorder `inside` drop: the dragged node becomes a child of this row,\n       so highlight the whole row instead of drawing a between-rows line. */\n    &-drop-inside {\n      background: var(--primary-lighter);\n      box-shadow: inset 0 0 0 2px var(--primary-color);\n    }\n  }\n\n  /* Tree-reorder `before`/`after` drop line. Positioned inside the (relatively\n     positioned absolute) row; `left` is set inline to the target's indent so it\n     starts where the dropped content will. */\n  &-drop-line {\n    position: absolute;\n    right: 0;\n    height: 2px;\n    background: var(--primary-color);\n    pointer-events: none;\n    z-index: 1;\n\n    /* A small knob at the line's start, the usual tree drop-indicator look. */\n    &::before {\n      content: '';\n      position: absolute;\n      left: 0;\n      top: 50%;\n      width: 6px;\n      height: 6px;\n      border-radius: 50%;\n      background: var(--primary-color);\n      transform: translate(-50%, -50%);\n    }\n\n    &-before {\n      top: -1px;\n    }\n\n    &-after {\n      bottom: -1px;\n    }\n  }\n\n  /* While a row is being dragged, suppress hover backgrounds so the pointer\n     moving across rows doesn't light up the wrong one, and stop text selection.\n     `touch-action: none` stops the touch drag from also scrolling the list\n     (only active once the long-press has armed the drag). */\n  &-reordering {\n    touch-action: none;\n\n    .virtualList-row {\n      user-select: none;\n\n      &:hover {\n        background: transparent;\n      }\n\n      &.virtualList-row-selected:hover {\n        background: var(--primary-lighter);\n      }\n\n      &.virtualList-row-dragging {\n        background: transparent;\n      }\n    }\n  }\n\n  &-cell {\n    padding: 8px 12px;\n    overflow: hidden;\n    text-overflow: ellipsis;\n    font-size: 14px;\n    box-sizing: border-box;\n    display: flex;\n    align-items: center;\n    min-width: 0;\n\n    /* `fit` columns are content-sized — keep their cells on a single line so the\n       content can't wrap, and so the width measurement reads the true intrinsic\n       width. (Other cells are free to wrap for variable-height rows.) */\n    &[data-fit-key] {\n      white-space: nowrap;\n    }\n  }\n\n  &-loading {\n    &-full {\n      display: flex;\n      justify-content: center;\n      align-items: center;\n      height: 100%;\n    }\n\n    &-indicator {\n      display: flex;\n      justify-content: center;\n      align-items: center;\n      padding: 8px 0;\n    }\n  }\n\n  /* Must stay after the declarations it undoes: both selectors are one class, so\n     only source order separates them. `overflow` cannot be split per axis here\n     either, because a container with `overflow-x` and `overflow-y: visible`\n     computes the visible one to `auto` and hands the list back its own vertical\n     scrollbar. So a frozen layout wider than the list overflows the page\n     sideways instead of scrolling inside it. */\n  &-windowed {\n    height: auto;\n    overflow: visible;\n    scrollbar-gutter: auto;\n\n    .virtualList-loading-full {\n      height: auto;\n      padding: 32px 0;\n    }\n  }\n}\n","import type { ColumnWidthSpec, SortValue, VirtualListColumn, VirtualListSort } from './virtualList.types';\n\n// ─── Layout / windowing math ──────────────────────────────────────────────────\n\nexport function buildOffsets(\n  keys: (string | number)[],\n  heightsMap: Map<string | number, number>,\n  estimatedRowHeight: number,\n  rowGap = 0,\n): { offsets: number[]; totalHeight: number } {\n  const itemCount = keys.length;\n  const offsets = new Array<number>(itemCount + 1);\n  offsets[0] = 0;\n  for (let i = 0; i < itemCount; i++) {\n    const gap = i < itemCount - 1 ? rowGap : 0;\n    // Heights are cached by item key, not index, so a measured row keeps its\n    // height across reorders/filters instead of inheriting its neighbour's.\n    offsets[i + 1] = offsets[i] + (heightsMap.get(keys[i]) ?? estimatedRowHeight) + gap;\n  }\n  return { offsets, totalHeight: offsets[itemCount] };\n}\n\n/** Index of the row that sits at `scrollTop` — i.e. the first row whose bottom edge is below it. */\nexport function findStartIndex(offsets: number[], scrollTop: number, itemCount: number): number {\n  if (itemCount <= 0) return 0;\n  let lo = 0;\n  let hi = itemCount - 1;\n  while (lo < hi) {\n    const mid = (lo + hi) >> 1;\n    if (offsets[mid + 1] <= scrollTop) lo = mid + 1;\n    else hi = mid;\n  }\n  return lo;\n}\n\nexport function getVisibleRange(\n  offsets: number[],\n  scrollTop: number,\n  containerHeight: number,\n  overscan: number,\n  itemCount: number,\n): { startIndex: number; endIndex: number } {\n  if (itemCount === 0) return { startIndex: 0, endIndex: -1 };\n\n  const lo = findStartIndex(offsets, scrollTop, itemCount);\n\n  let end = lo;\n  while (end < itemCount && offsets[end] < scrollTop + containerHeight) end++;\n\n  return {\n    startIndex: Math.max(0, lo - overscan),\n    endIndex: Math.min(itemCount - 1, end + overscan - 1),\n  };\n}\n\n// ─── Reorder / tree-drop math ─────────────────────────────────────────────────\n\n/**\n * Decides where a dragged row should move to, applying midpoint hysteresis: a\n * move only happens once the pointer has crossed the centre of the row it is\n * over (downwards when moving down, upwards when moving up). Returns the target\n * index, or null if no move should occur. Pure for easy unit testing.\n */\nexport function resolveReorderTarget(\n  fromIndex: number,\n  overIndex: number,\n  pointerY: number,\n  rowTop: number,\n  rowHeight: number,\n): number | null {\n  if (fromIndex === -1 || fromIndex === overIndex) return null;\n  const midpoint = rowTop + rowHeight / 2;\n  const movingDown = overIndex > fromIndex;\n  if (movingDown ? pointerY >= midpoint : pointerY <= midpoint) return overIndex;\n  return null;\n}\n\n/**\n * Decides where a tree-reorder drop should land relative to the row under the\n * pointer. Group rows expose three zones (top third → `before`, middle →\n * `inside`, bottom third → `after`); leaf rows split in half (`before`/`after`)\n * since they can't receive children. Pure for easy unit testing.\n */\nexport function resolveTreeDropTarget(\n  pointerY: number,\n  rowTop: number,\n  rowHeight: number,\n  isGroup: boolean,\n): 'before' | 'inside' | 'after' {\n  if (rowHeight <= 0) return 'before';\n  const rel = (pointerY - rowTop) / rowHeight;\n  if (isGroup) {\n    if (rel < 1 / 3) return 'before';\n    if (rel > 2 / 3) return 'after';\n    return 'inside';\n  }\n  return rel < 0.5 ? 'before' : 'after';\n}\n\n/**\n * Index of the last descendant of the node at `index` in a depth-tagged flat\n * list (a contiguous run of following rows with a greater depth). Returns\n * `index` itself when the node has no children. A drop is forbidden anywhere in\n * `[index, lastDescendantIndex]` — that's the node and its own subtree.\n */\nexport function lastDescendantIndex(index: number, depths: number[]): number {\n  const d = depths[index];\n  let i = index + 1;\n  while (i < depths.length && depths[i] > d) i++;\n  return i - 1;\n}\n\n// ─── Column template ──────────────────────────────────────────────────────────\n\nexport function buildColumnTemplate(\n  columns: { key: string; width: ColumnWidthSpec }[],\n  fitWidths?: Record<string, number>,\n): string {\n  if (columns.length === 0) return '1fr';\n  return columns\n    .map((col) => {\n      switch (col.width.type) {\n        case 'fixed':\n          return `${col.width.px}px`;\n        case 'flex':\n          return `${col.width.weight ?? 1}fr`;\n        // A `fit` track is content-sized, so it resolves independently in each\n        // grid (the header and every row are separate grids) and would drift\n        // out of alignment. Once we've measured the column's natural width\n        // across all those grids, pin it to that exact px so every grid uses an\n        // identical track. Until then, fall back to content-sizing.\n        case 'fit': {\n          const w = fitWidths?.[col.key];\n          return w != null ? `${w}px` : 'fit-content(100%)';\n        }\n      }\n    })\n    .join(' ');\n}\n\n// ─── Row focus ────────────────────────────────────────────────────────────────\n\n/**\n * What counts as a control inside a row, and so what gets held out of the tab\n * order while its row is not the one being used.\n *\n * `[tabindex=\"-1\"]` is deliberately excluded. The row's own select checkbox sits\n * there and is reached with Space, and an element the consumer has already taken\n * out of the tab order stays out.\n */\nexport const ROW_FOCUSABLE_SELECTOR =\n  'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n\n/** Marks an element parked at `tabindex=\"-1\"` by the list rather than by the consumer. */\nexport const ROW_PARKED_ATTRIBUTE = 'data-vl-parked';\n\n/**\n * What `F2` steps into: the controls of a row, including the ones parked out of\n * the tab order. Without the second half it would find nothing at all in a row\n * whose controls are custom elements carrying a `tabindex`, because parking them\n * is exactly what takes them out of the selector above.\n */\nexport const ROW_ENTER_SELECTOR = `${ROW_FOCUSABLE_SELECTOR}, [${ROW_PARKED_ATTRIBUTE}]`;\n\n// ─── Column width measurement ─────────────────────────────────────────────────\n\n/**\n * The content width of one cell, in px. `scrollWidth` reports the true content\n * width even when the cell is pinned narrower than its content, which is what\n * lets a column recover from having been measured while it was still empty.\n *\n * Two corrections on top of it, both only resolved when they can apply, so the\n * hot scroll path stays cheap for a plain in-flow cell:\n *\n * - `scrollWidth` includes the left padding but drops the right one once the\n *   content overflows, so add it back.\n * - A cell whose only children are FontAwesome icons reports just its padding:\n *   their boxes overflow via `overflow: visible`, carry no intrinsic width, and\n *   so never count towards `scrollWidth` (right-aligned, they even overflow\n *   into negative offsets). Only then walk the descendants for their true span.\n *   Once a real width pins, the cell is no longer collapsed and this stops.\n */\nexport function measureCellWidth(el: HTMLElement): number {\n  const overflowing = el.scrollWidth > el.clientWidth;\n  let width = el.scrollWidth;\n  if (!overflowing && !el.firstElementChild) return width;\n\n  const cs = getComputedStyle(el);\n  const padLeft = parseFloat(cs.paddingLeft) || 0;\n  const padRight = parseFloat(cs.paddingRight) || 0;\n  if (overflowing) width += padRight;\n\n  if (el.firstElementChild && !overflowing && el.scrollWidth <= padLeft + padRight + 1) {\n    let minLeft = Infinity;\n    let maxRight = -Infinity;\n    el.querySelectorAll('*').forEach((d) => {\n      const r = d.getBoundingClientRect();\n      if (r.left < minLeft) minLeft = r.left;\n      if (r.right > maxRight) maxRight = r.right;\n    });\n    if (maxRight > minLeft) {\n      const span = Math.ceil(maxRight - minLeft) + padLeft + padRight;\n      if (span > width) width = span;\n    }\n  }\n  return width;\n}\n\n/** Attribute selector for one column's cells. Column keys are arbitrary strings. */\nexport function columnCellSelector(key: string): string {\n  return `[data-col-key=\"${key.replace(/[\"\\\\]/g, '\\\\$&')}\"]`;\n}\n\n// ─── Column resizing ──────────────────────────────────────────────────────────\n\n/** Floor for a user-resized column, unless the column raises it with `minWidth`. */\nexport const MIN_COLUMN_WIDTH = 48;\n\n/**\n * Width given to a column that gains one while the layout is already frozen,\n * i.e. one revealed from the gear menu after the first resize. Every visible\n * column has to be an exact px track for the total width (and so the horizontal\n * scroll range) to be exact, and there is nothing measured to go on yet.\n */\nexport const DEFAULT_FROZEN_WIDTH = 160;\n\n/**\n * Custom property carrying one column's width. Column keys are arbitrary\n * strings, so anything a custom property can't contain is folded to `_`; two\n * keys that differ only in those characters would share a track.\n */\nexport function columnWidthVar(key: string): string {\n  return `--vl-col-${key.replace(/[^a-zA-Z0-9_-]/g, '_')}`;\n}\n\n/** Px width for a column the user hasn't sized, at the moment the layout freezes. */\nexport function resolveFrozenWidth<T>(col: VirtualListColumn<T>): number {\n  if (col.width.type === 'fixed') return col.width.px;\n  if (col.width.type === 'fit') return DEFAULT_FROZEN_WIDTH;\n  return DEFAULT_FROZEN_WIDTH * (col.width.weight ?? 1);\n}\n\n/**\n * Grid template for a frozen layout: every column reads its own custom property,\n * so a drag can rewrite one width with a single DOM write and have the header\n * and every row follow in the same layout pass, without React rendering.\n */\nexport function buildFrozenTemplate<T>(columns: VirtualListColumn<T>[]): string {\n  if (columns.length === 0) return '1fr';\n  return columns.map((col) => `var(${columnWidthVar(col.key)}, ${DEFAULT_FROZEN_WIDTH}px)`).join(' ');\n}\n\n// ─── Column-visibility persistence ────────────────────────────────────────────\n\nexport const COLUMN_STORAGE_PREFIX = 'ahroweui:virtuallist:columns:';\n\n// We persist both the visible keys and the full set of columns that existed at\n// save time. That lets us tell \"the user hid this column\" from \"this column\n// didn't exist yet\" when columns are added later — the former stays hidden, the\n// latter falls back to its defaultHidden value.\nexport interface PersistedColumns {\n  visible: string[];\n  known: string[];\n  /** User-resized column widths in px. Absent in entries saved before resizing existed. */\n  widths?: Record<string, number>;\n}\n\nexport function loadPersistedColumns(persistKey: string | undefined): PersistedColumns | null {\n  if (!persistKey || typeof window === 'undefined') return null;\n  try {\n    const raw = window.localStorage.getItem(COLUMN_STORAGE_PREFIX + persistKey);\n    if (!raw) return null;\n    const parsed = JSON.parse(raw);\n    if (parsed && Array.isArray(parsed.visible) && Array.isArray(parsed.known)) {\n      const widths: Record<string, number> = {};\n      if (parsed.widths && typeof parsed.widths === 'object')\n        for (const [key, value] of Object.entries(parsed.widths))\n          if (typeof value === 'number' && Number.isFinite(value) && value > 0) widths[key] = value;\n      return {\n        visible: parsed.visible.filter((k: unknown): k is string => typeof k === 'string'),\n        known: parsed.known.filter((k: unknown): k is string => typeof k === 'string'),\n        widths,\n      };\n    }\n  } catch {\n    // Corrupt/unavailable storage — fall back to defaults.\n  }\n  return null;\n}\n\n/** Whether a column should start visible, honouring saved settings then `defaultHidden`. */\nexport function isColumnInitiallyVisible<T>(col: VirtualListColumn<T>, persisted: PersistedColumns | null): boolean {\n  // A locked column is always visible and can't be hidden — ignore saved state.\n  if (col.lockVisible) return true;\n  if (persisted && persisted.known.includes(col.key)) return persisted.visible.includes(col.key);\n  return !col.defaultHidden;\n}\n\n// ─── Sorting ──────────────────────────────────────────────────────────────────\n\n// `numeric` so \"Order 10\" lands after \"Order 2\" instead of before it, and\n// `sensitivity: 'base'` so case and accents don't split otherwise-equal values.\n// Built lazily and reused per locale: constructing a Collator is expensive relative\n// to the comparisons it then runs.\nconst collators = new Map<string, Intl.Collator>();\nfunction getCollator(locale: string | undefined): Intl.Collator {\n  const key = locale ?? '';\n  let collator = collators.get(key);\n  if (!collator) {\n    collator = new Intl.Collator(locale, { numeric: true, sensitivity: 'base' });\n    collators.set(key, collator);\n  }\n  return collator;\n}\n\n/** Values with no order of their own, parked at the end of the list either way. */\nexport function isEmptySortValue(value: SortValue): boolean {\n  return value == null || (typeof value === 'number' && Number.isNaN(value));\n}\n\n/**\n * Ascending comparison of two non-empty `sortValue` results. Dates and booleans\n * are normalised to numbers; anything with a string on either side goes through\n * the collator, which is also what makes a mixed-type column produce a stable\n * (if arbitrary) order rather than a random one.\n */\nexport function compareSortValues(a: SortValue, b: SortValue, locale?: string): number {\n  const na = a instanceof Date ? a.getTime() : typeof a === 'boolean' ? Number(a) : a;\n  const nb = b instanceof Date ? b.getTime() : typeof b === 'boolean' ? Number(b) : b;\n  if (typeof na === 'string' || typeof nb === 'string') return getCollator(locale).compare(String(na), String(nb));\n  return (na as number) < (nb as number) ? -1 : (na as number) > (nb as number) ? 1 : 0;\n}\n\n/** Whether a header click on this column should do anything at all. */\nexport function isColumnSortable<T>(col: VirtualListColumn<T>): boolean {\n  return col.sortValue != null || col.compare != null || col.sortable === true;\n}\n\n/** The asc → desc → unsorted cycle a header click steps through. */\nexport function nextSort(current: VirtualListSort | null, key: string): VirtualListSort | null {\n  if (!current || current.key !== key) return { key, direction: 'asc' };\n  return current.direction === 'asc' ? { key, direction: 'desc' } : null;\n}\n\n/**\n * Returns `items` reordered by `sort`, or the same array reference when nothing\n * applies: no sort, no such column, or a `sortable` column that only reports\n * (its data is sorted by the consumer). Callers rely on that identity to tell\n * \"the list sorted this\" from \"the list left it alone\".\n */\nexport function sortItems<T>(\n  items: T[],\n  sort: VirtualListSort | null | undefined,\n  columns: VirtualListColumn<T>[] | undefined,\n  locale?: string,\n): T[] {\n  if (!sort || !columns) return items;\n  const col = columns.find((c) => c.key === sort.key);\n  if (!col || (col.compare == null && col.sortValue == null)) return items;\n\n  const dir = sort.direction === 'asc' ? 1 : -1;\n  const { compare, sortValue } = col;\n  // Decorated with the source index so equal values keep their original order.\n  // Array.prototype.sort is stable per spec, but the tiebreaker also stops a\n  // `compare` that returns 0 for distinct rows from reshuffling them.\n  const decorated = items.map((item, index) => ({ item, index }));\n  decorated.sort((a, b) => {\n    let result: number;\n    if (compare) {\n      result = dir * compare(a.item, b.item);\n    } else {\n      const av = sortValue!(a.item);\n      const bv = sortValue!(b.item);\n      const aEmpty = isEmptySortValue(av);\n      const bEmpty = isEmptySortValue(bv);\n      // Deliberately not multiplied by `dir`: an empty cell belongs at the\n      // bottom of the table whichever way the column is pointing.\n      if (aEmpty || bEmpty) result = aEmpty === bEmpty ? 0 : aEmpty ? 1 : -1;\n      else result = dir * compareSortValues(av, bv, locale);\n    }\n    return result !== 0 ? result : a.index - b.index;\n  });\n  return decorated.map((d) => d.item);\n}\n","import React, { useRef, useLayoutEffect } from 'react';\nimport cx from 'classnames';\n\nimport styles from './virtualList.module.pcss';\nimport { ROW_FOCUSABLE_SELECTOR, ROW_PARKED_ATTRIBUTE } from './virtualList.utils';\nimport type { TreeDropPosition } from './virtualList.types';\n\nexport interface VirtualRowProps {\n  index: number;\n  /** Referenced by the list's `aria-activedescendant` while this row is active. */\n  id?: string;\n  top: number;\n  /** Padding applied to the row (already normalised to a CSS string). */\n  padding?: string;\n  columnTemplate: string | undefined;\n  isSelected: boolean;\n  /** The row the arrow keys are on. Distinct from selection. */\n  isActive?: boolean;\n  /**\n   * Hold this row's own controls out of the tab order unless it is the row being\n   * used. Off when the list leaves the keyboard to its rows.\n   */\n  manageTabStops?: boolean;\n  /** This is the row being used, so its controls are reachable with Tab. */\n  isEntered?: boolean;\n  activeClassName?: string;\n  activeStyle?: React.CSSProperties;\n  showDivider: boolean;\n  hover: boolean;\n  onClick: () => void;\n  observer: ResizeObserver | null;\n  ariaRowIndex?: number;\n  ariaPosInSet?: number;\n  ariaSetSize?: number;\n  rowRole: string;\n  reorderable?: boolean;\n  /**\n   * Native mouse DnD is active for the list. Distinct from `draggable`, which in\n   * `dragHandle` mode is only set on the row the pointer has armed.\n   */\n  mouseDragEnabled?: boolean;\n  draggable?: boolean;\n  isDragging?: boolean;\n  reserveToggleGutter?: boolean;\n  onMouseDown?: (e: React.MouseEvent<HTMLDivElement>) => void;\n  onMouseOver?: (e: React.MouseEvent<HTMLDivElement>) => void;\n  onMouseLeave?: (e: React.MouseEvent<HTMLDivElement>) => void;\n  onDragStart?: (e: React.DragEvent<HTMLDivElement>) => void;\n  onDragOver?: (e: React.DragEvent<HTMLDivElement>) => void;\n  onDragEnd?: () => void;\n  onDrop?: (e: React.DragEvent<HTMLDivElement>) => void;\n  onTouchStart?: (e: React.TouchEvent<HTMLDivElement>) => void;\n  dropPosition?: TreeDropPosition;\n  dropIndentPx?: number;\n  dropIndicatorClassName?: string;\n  dropIndicatorStyle?: React.CSSProperties;\n  rowClassName?: string;\n  rowStyle?: React.CSSProperties;\n  children: React.ReactNode;\n}\n\n/**\n * A single virtualized row: absolutely positioned at its `top`, optionally a\n * grid (table mode), registering itself with the shared ResizeObserver and\n * carrying all the drag / drop-indicator wiring the parent assigns it.\n */\nfunction VirtualRow({\n  index,\n  id,\n  top,\n  padding,\n  columnTemplate,\n  isSelected,\n  isActive,\n  manageTabStops,\n  isEntered,\n  activeClassName,\n  activeStyle,\n  showDivider,\n  hover,\n  onClick,\n  observer,\n  ariaRowIndex,\n  ariaPosInSet,\n  ariaSetSize,\n  rowRole,\n  reorderable,\n  mouseDragEnabled,\n  draggable,\n  isDragging,\n  reserveToggleGutter,\n  onMouseDown,\n  onMouseOver,\n  onMouseLeave,\n  onDragStart,\n  onDragOver,\n  onDragEnd,\n  onDrop,\n  onTouchStart,\n  dropPosition,\n  dropIndentPx,\n  dropIndicatorClassName,\n  dropIndicatorStyle,\n  rowClassName,\n  rowStyle,\n  children,\n}: VirtualRowProps) {\n  const ref = useRef<HTMLDivElement>(null);\n\n  // A single observer is shared across all rows (created by the parent); each\n  // row just registers/unregisters its element. The parent reads the row index\n  // back from the `data-index` attribute when the observer fires.\n  useLayoutEffect(() => {\n    const el = ref.current;\n    if (!el || !observer) return;\n    observer.observe(el);\n    return () => observer.unobserve(el);\n  }, [observer]);\n\n  // One row at a time carries tab stops. Without this every control in every\n  // rendered row is one, the set changes as the list scrolls, and tabbing\n  // through a windowed list leaves it at an arbitrary point: the next row does\n  // not exist in the DOM yet when the browser looks for the next stop.\n  //\n  // Runs on every commit of the row rather than on a dependency list, because\n  // the cells are the consumer's and can change without anything here changing.\n  // The selector only matches what is in the tab order to begin with, and an\n  // attribute write invalidates no layout, so a scroll frame pays a query per\n  // row and nothing else.\n  useLayoutEffect(() => {\n    const el = ref.current;\n    if (!el || !manageTabStops || isEntered) return;\n    const restore: [HTMLElement, string | null][] = [];\n    el.querySelectorAll<HTMLElement>(ROW_FOCUSABLE_SELECTOR).forEach((node) => {\n      restore.push([node, node.getAttribute('tabindex')]);\n      node.setAttribute('tabindex', '-1');\n      node.setAttribute(ROW_PARKED_ATTRIBUTE, '');\n    });\n    return () => {\n      for (const [node, previous] of restore) {\n        if (previous === null) node.removeAttribute('tabindex');\n        else node.setAttribute('tabindex', previous);\n        node.removeAttribute(ROW_PARKED_ATTRIBUTE);\n      }\n    };\n  });\n\n  return (\n    <div\n      ref={ref}\n      id={id}\n      data-index={index}\n      className={cx(\n        styles.virtualListRow,\n        isSelected && styles.virtualListRowSelected,\n        isActive && styles.virtualListRowActive,\n        isActive && activeClassName,\n        !showDivider && styles.virtualListRowNoDivider,\n        !hover && styles.virtualListRowNoHover,\n        reorderable && styles.virtualListRowDraggable,\n        isDragging && styles.virtualListRowDragging,\n        dropPosition === 'inside' && styles.virtualListRowDropInside,\n        rowClassName,\n      )}\n      style={{\n        position: 'absolute',\n        top: 0,\n        left: 0,\n        right: 0,\n        transform: `translateY(${top}px)`,\n        ...(padding != null ? { padding } : {}),\n        ...(columnTemplate ? { display: 'grid', gridTemplateColumns: columnTemplate } : {}),\n        // Match the header's reserved gutter for the column-toggle cog so row\n        // cells line up with their header cells instead of drifting under it.\n        // Applied after `padding` so the alignment gutter always wins on the right.\n        ...(reserveToggleGutter ? { paddingRight: 32 } : {}),\n        ...rowStyle,\n        ...(isActive ? activeStyle : undefined),\n      }}\n      onClick={onClick}\n      draggable={draggable}\n      onMouseDown={onMouseDown}\n      onMouseOver={onMouseOver}\n      onMouseLeave={onMouseLeave}\n      onDragStart={onDragStart}\n      onDragOver={onDragOver}\n      onDragEnd={onDragEnd}\n      onDrop={onDrop ?? (mouseDragEnabled ? (e) => e.preventDefault() : undefined)}\n      onTouchStart={onTouchStart}\n      onContextMenu={mouseDragEnabled ? (e) => e.preventDefault() : undefined}\n      aria-selected={isSelected}\n      aria-rowindex={ariaRowIndex}\n      aria-posinset={ariaPosInSet}\n      aria-setsize={ariaSetSize}\n      role={rowRole}\n    >\n      {(dropPosition === 'before' || dropPosition === 'after') && (\n        <div\n          data-drop-line={dropPosition}\n          aria-hidden\n          className={cx(\n            styles.virtualListDropLine,\n            dropPosition === 'before' ? styles.virtualListDropLineBefore : styles.virtualListDropLineAfter,\n            dropIndicatorClassName,\n          )}\n          style={{ left: dropIndentPx ?? 0, ...dropIndicatorStyle }}\n        />\n      )}\n      {children}\n    </div>\n  );\n}\n\nexport default VirtualRow;\n","import React, { useRef, useState } from 'react';\n\ninterface StationProps {\n  tabIndex: number;\n  ref: (el: HTMLElement | null) => void;\n  onFocus: () => void;\n}\n\ninterface UseHeaderNavigationResult {\n  getStationProps: (index: number) => StationProps;\n  /** True when the key moved the focus, so the caller stops handling it. */\n  handleNavigationKey: (event: React.KeyboardEvent, index: number) => boolean;\n}\n\n/**\n * Roving tab stop across the header: the select-all cell, every column header,\n * and the column menu are stations, and arrow keys move between them.\n *\n * Without it each of those is its own tab stop, so a table with eight sortable,\n * resizable columns takes seventeen presses of Tab before the data. The stops\n * also belong together: they all act on the same header, which is exactly the\n * case the roving pattern exists for.\n */\nexport function useHeaderNavigation(stationCount: number): UseHeaderNavigationResult {\n  const [activeIndex, setActiveIndex] = useState(0);\n  const stationsRef = useRef<(HTMLElement | null)[]>([]);\n\n  // Hiding a column can shrink the header past the active station. Clamping\n  // during render rather than in an effect keeps the header from painting a\n  // single frame with no tab stop at all.\n  const active = Math.min(activeIndex, Math.max(0, stationCount - 1));\n  if (active !== activeIndex) setActiveIndex(active);\n\n  function focusStation(index: number) {\n    const next = Math.max(0, Math.min(index, stationCount - 1));\n    setActiveIndex(next);\n    stationsRef.current[next]?.focus();\n  }\n\n  function getStationProps(index: number): StationProps {\n    return {\n      tabIndex: index === active ? 0 : -1,\n      ref: (el: HTMLElement | null) => {\n        stationsRef.current[index] = el;\n      },\n      // Also fires for a click, which keeps the tab stop where the user last\n      // was rather than snapping back to the first column.\n      onFocus: () => setActiveIndex(index),\n    };\n  }\n\n  function handleNavigationKey(event: React.KeyboardEvent, index: number): boolean {\n    // Alt is the resize modifier, so those combinations belong to the station.\n    if (event.altKey || event.ctrlKey || event.metaKey) return false;\n    switch (event.key) {\n      case 'ArrowRight':\n        focusStation(index + 1);\n        break;\n      case 'ArrowLeft':\n        focusStation(index - 1);\n        break;\n      case 'Home':\n        focusStation(0);\n        break;\n      case 'End':\n        focusStation(stationCount - 1);\n        break;\n      default:\n        return false;\n    }\n    event.preventDefault();\n    event.stopPropagation();\n    return true;\n  }\n\n  return { getStationProps, handleNavigationKey };\n}\n","import React from 'react';\nimport cx from 'classnames';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faGear, faSort, faSortDown, faSortUp } from '@fortawesome/free-solid-svg-icons';\n\nimport styles from './virtualList.module.pcss';\nimport Checkbox from '../checkbox';\nimport Popover from '../popover';\nimport { PopoverAlign, PopoverPlacement } from '../popover/popover.types';\nimport { isColumnSortable } from './virtualList.utils';\nimport { useHeaderNavigation } from './useHeaderNavigation';\nimport type { UseColumnsResult } from './useColumns';\nimport type { UseColumnResizeResult } from './useColumnResize';\nimport type { SlotClassNames, SlotStyles } from '../types/slots.types';\nimport type { VirtualListColumn, VirtualListLabels, VirtualListSlots, VirtualListSort } from './virtualList.types';\n\n/** Width change of one Alt+arrow press, in px. Alt+Shift steps by one instead. */\nconst KEYBOARD_RESIZE_STEP = 10;\n\n/** FontAwesome's own style type, which carries the `--fa-font-*` custom properties. */\ntype FaStyle = React.CSSProperties & Record<`--fa-font-${string}`, string>;\n\ninterface VirtualListHeaderProps<T> {\n  headerRef: React.RefObject<HTMLDivElement | null>;\n  /** Every column, including hidden ones: the toggle menu lists them all. */\n  columns: VirtualListColumn<T>[];\n  cols: UseColumnsResult<T>;\n  resize: UseColumnResizeResult<T>;\n  sort: VirtualListSort | null;\n  onSort: (key: string) => void;\n  multiSelect: boolean;\n  allSelected: boolean;\n  someSelected: boolean;\n  onSelectAll: () => void;\n  labels: Required<VirtualListLabels>;\n  classNames?: SlotClassNames<VirtualListSlots>;\n  styles?: SlotStyles<VirtualListSlots>;\n}\n\n/**\n * The table-mode header row: one cell per visible column, each optionally a sort\n * button and a resize handle, plus the column-visibility menu pinned to the\n * right. It is a grid with the same template as every row, which is what keeps\n * the columns aligned across the separate grids.\n *\n * The whole header is a single tab stop (see `useHeaderNavigation`). Focus lands\n * on a header cell rather than on the button inside it, so every key a column\n * understands is handled in one place: Enter or Space sorts, Alt plus the arrow\n * keys resizes, Alt+Home and Alt+End take the column to its minimum or to its\n * content width.\n */\nfunction VirtualListHeader<T>({\n  headerRef,\n  columns,\n  cols,\n  resize,\n  sort,\n  onSort,\n  multiSelect,\n  allSelected,\n  someSelected,\n  onSelectAll,\n  labels,\n  classNames,\n  styles: slotStyles,\n}: VirtualListHeaderProps<T>) {\n  // Station order is the reading order: the select-all box, the columns, the menu.\n  const columnOffset = multiSelect ? 1 : 0;\n  const stationCount = columnOffset + cols.visibleColumns.length + (cols.shouldShowToggle ? 1 : 0);\n  const nav = useHeaderNavigation(stationCount);\n\n  return (\n    <div\n      ref={headerRef}\n      className={cx(styles.virtualListHeader, classNames?.header)}\n      style={{\n        gridTemplateColumns: cols.columnTemplate,\n        ...(cols.shouldShowToggle ? { paddingRight: 32 } : {}),\n        ...slotStyles?.header,\n      }}\n      role='row'\n      aria-rowindex={1}\n    >\n      {multiSelect && (\n        <div\n          {...nav.getStationProps(0)}\n          className={cx(styles.virtualListSelectCell, classNames?.selectCell)}\n          style={slotStyles?.selectCell}\n          role='columnheader'\n          onKeyDown={(event) => {\n            if (nav.handleNavigationKey(event, 0)) return;\n            if (event.target !== event.currentTarget) return;\n            if (event.key !== 'Enter' && event.key !== ' ') return;\n            event.preventDefault();\n            onSelectAll();\n          }}\n        >\n          <Checkbox\n            selected={allSelected}\n            indeterminate={someSelected}\n            onToggle={onSelectAll}\n            aria-label={labels.selectAllRows}\n            tabIndex={-1}\n          />\n        </div>\n      )}\n\n      {cols.visibleColumns.map((col, columnIndex) => {\n        const sortable = isColumnSortable(col);\n        const direction = sort && sort.key === col.key ? sort.direction : null;\n        const handleProps = resize.getHandleProps(col);\n        const station = columnOffset + columnIndex;\n        return (\n          <div\n            key={col.key}\n            {...nav.getStationProps(station)}\n            onKeyDown={(event) => {\n              if (nav.handleNavigationKey(event, station)) return;\n              if (event.altKey && resize.isResizable(col)) {\n                const step = event.shiftKey ? 1 : KEYBOARD_RESIZE_STEP;\n                if (event.key === 'ArrowRight') resize.resizeBy(col.key, step);\n                else if (event.key === 'ArrowLeft') resize.resizeBy(col.key, -step);\n                else if (event.key === 'Home') resize.resizeToMin(col.key);\n                else if (event.key === 'End') resize.autoFit(col.key);\n                else return;\n                event.preventDefault();\n                return;\n              }\n              // Only when the cell itself holds the focus: after a mouse click\n              // the focus sits on the sort button, which activates on its own.\n              if (!sortable || event.target !== event.currentTarget) return;\n              if (event.key !== 'Enter' && event.key !== ' ') return;\n              event.preventDefault();\n              onSort(col.key);\n            }}\n            className={cx(styles.virtualListHeaderCell, classNames?.headerCell)}\n            style={slotStyles?.headerCell}\n            role='columnheader'\n            data-col-key={col.key}\n            aria-sort={\n              sortable ? (direction === 'asc' ? 'ascending' : direction === 'desc' ? 'descending' : 'none') : undefined\n            }\n            data-fit-key={col.width.type === 'fit' ? col.key : undefined}\n          >\n            {sortable ? (\n              <button\n                type='button'\n                className={cx(styles.virtualListHeaderSort, classNames?.headerSort)}\n                style={slotStyles?.headerSort}\n                onClick={() => onSort(col.key)}\n                tabIndex={-1}\n              >\n                <span className={styles.virtualListHeaderSortLabel}>{col.label}</span>\n                {/* Rendered in every state, inactive included: the header cell is\n                    measured for `fit` columns and those pins only ever grow, so an\n                    icon that appears on click would widen the column for good. */}\n                <FontAwesomeIcon\n                  className={cx(\n                    styles.virtualListHeaderSortIcon,\n                    direction && styles.virtualListHeaderSortIconActive,\n                    classNames?.sortIcon,\n                  )}\n                  style={slotStyles?.sortIcon as FaStyle}\n                  icon={direction === 'asc' ? faSortUp : direction === 'desc' ? faSortDown : faSort}\n                />\n              </button>\n            ) : (\n              col.label\n            )}\n            {handleProps && (\n              <div\n                {...handleProps}\n                className={cx(styles.virtualListHeaderResize, classNames?.resizeHandle)}\n                style={slotStyles?.resizeHandle}\n              />\n            )}\n          </div>\n        );\n      })}\n\n      {cols.shouldShowToggle && (\n        <ColumnToggle\n          station={stationCount - 1}\n          nav={nav}\n          columns={columns}\n          cols={cols}\n          resize={resize}\n          labels={labels}\n          classNames={classNames}\n          styles={slotStyles}\n        />\n      )}\n    </div>\n  );\n}\n\ntype ColumnToggleProps<T> = Pick<\n  VirtualListHeaderProps<T>,\n  'columns' | 'cols' | 'resize' | 'labels' | 'classNames' | 'styles'\n> & {\n  station: number;\n  nav: ReturnType<typeof useHeaderNavigation>;\n};\n\n/**\n * The gear at the header's trailing edge: one checkbox per column, the reset for\n * user-set widths, and a badge counting the columns the user has hidden.\n */\nfunction ColumnToggle<T>({\n  station,\n  nav,\n  columns,\n  cols,\n  resize,\n  labels,\n  classNames,\n  styles: slotStyles,\n}: ColumnToggleProps<T>) {\n  return (\n    <div className={styles.virtualListToggleWrapper}>\n      <Popover\n        isOpen={cols.toggleOpen}\n        onOpenChange={cols.setToggleOpen}\n        placement={PopoverPlacement.Bottom}\n        align={PopoverAlign.End}\n        constrainWidth={false}\n        classNames={{ panel: styles.virtualListTogglePanel }}\n        content={\n          <div\n            className={cx(styles.virtualListTogglePopover, classNames?.togglePopover)}\n            style={slotStyles?.togglePopover}\n          >\n            {columns\n              .filter((col) => !col.hideFromToggle)\n              .map((col) => (\n                <Checkbox\n                  key={col.key}\n                  className={cx(styles.virtualListToggleItem, classNames?.toggleItem)}\n                  style={slotStyles?.toggleItem}\n                  selected={cols.effectiveVisible.has(col.key)}\n                  onToggle={() => cols.handleToggleColumn(col.key)}\n                  disabled={col.lockVisible}\n                  cursorDefault={false}\n                >\n                  {col.toggleLabel ?? col.label}\n                </Checkbox>\n              ))}\n            {resize.isFrozen && (\n              <button\n                type='button'\n                className={cx(styles.virtualListToggleReset, classNames?.toggleReset)}\n                style={slotStyles?.toggleReset}\n                onClick={() => {\n                  resize.reset();\n                  cols.setToggleOpen(false);\n                }}\n              >\n                {labels.resetColumnWidths}\n              </button>\n            )}\n          </div>\n        }\n      >\n        <button\n          {...nav.getStationProps(station)}\n          type='button'\n          className={cx(styles.virtualListHeaderToggle, classNames?.headerToggle)}\n          style={slotStyles?.headerToggle}\n          aria-label={labels.toggleColumns}\n          // Enter and Space are the button's own; only the arrows are ours.\n          onKeyDown={(event) => nav.handleNavigationKey(event, station)}\n        >\n          <FontAwesomeIcon icon={faGear} style={slotStyles?.headerToggle as FaStyle} />\n        </button>\n      </Popover>\n      {cols.manuallyHiddenCount > 0 && (\n        <span\n          className={cx(styles.virtualListColumnChip, classNames?.columnChip)}\n          style={slotStyles?.columnChip}\n          data-column-hidden-count={cols.manuallyHiddenCount}\n          aria-label={labels.columnsHidden(cols.manuallyHiddenCount)}\n        >\n          {cols.manuallyHiddenCount}\n        </span>\n      )}\n    </div>\n  );\n}\n\nexport default VirtualListHeader;\n","import type { VirtualListScrollParent } from './virtualList.types';\n\n/**\n * Whatever carries the list's scroll, and how the list is measured against it.\n *\n * Everything here works in *list space*: offset 0 is the top of the first row,\n * which is the root's `scrollTop` when the list scrolls itself and its distance\n * above the scrollport's top edge when it doesn't. One coordinate system, so the\n * windowing, anchoring and jump math never branch.\n */\n\nexport type Scroller = HTMLElement | Window;\n\nexport function isWindowScroller(scroller: Scroller): scroller is Window {\n  return !('nodeType' in scroller);\n}\n\n/** `'nearest'` is resolved by the caller, which caches the ancestor walk. */\nexport function resolveScroller(\n  scrollParent: Exclude<VirtualListScrollParent, 'nearest'> | undefined,\n): Scroller | null {\n  if (scrollParent == null || typeof window === 'undefined') return null;\n  if (scrollParent === 'window') return window;\n  // Resolved on every commit, so a ref still empty on the first render costs one\n  // frame against the page rather than rendering nothing.\n  if (typeof scrollParent === 'function') return scrollParent() ?? window;\n  return scrollParent;\n}\n\n/** In viewport coordinates, which is what the rects below are compared against. */\nexport function scrollportBounds(scroller: Scroller): { top: number; height: number } {\n  if (isWindowScroller(scroller)) return { top: 0, height: scroller.innerHeight };\n  // `clientTop` is the border, which `getBoundingClientRect` includes and\n  // `clientHeight` doesn't: without it a bordered scroll parent is off by it.\n  return { top: scroller.getBoundingClientRect().top + scroller.clientTop, height: scroller.clientHeight };\n}\n\nexport interface Viewport {\n  /** List-space scroll position, unclamped: negative while the list starts below\n   *  the scrollport, which is what makes a target reachable from either side. */\n  rel: number;\n  /** The slice of the scrollport the list occupies now. */\n  visibleHeight: number;\n  /** The whole scrollport, which is what a scroll target is aimed within. */\n  viewportHeight: number;\n  /** From the list's last row to the end of the scrollport. The browser clamps\n   *  `scrollTop`, so a list that scrolls itself never reports this negative;\n   *  riding a scrollport, it does, for the whole rest of the page. */\n  distanceToEnd: number;\n  width: number;\n}\n\nexport function measureViewport(root: HTMLElement, scroller: Scroller | null): Viewport {\n  const width = root.clientWidth;\n  if (!scroller) {\n    const height = root.clientHeight;\n    return {\n      rel: root.scrollTop,\n      visibleHeight: height,\n      viewportHeight: height,\n      distanceToEnd: root.scrollHeight - root.scrollTop - height,\n      width,\n    };\n  }\n  const rect = root.getBoundingClientRect();\n  const { top, height } = scrollportBounds(scroller);\n  const bottom = top + height;\n  return {\n    rel: top - rect.top,\n    visibleHeight: Math.max(0, Math.min(bottom, rect.bottom) - Math.max(top, rect.top)),\n    viewportHeight: height,\n    distanceToEnd: rect.bottom - bottom,\n    width,\n  };\n}\n","import React, { useState, useRef, useEffect, useLayoutEffect, useMemo, useCallback } from 'react';\n\nimport { buildOffsets, findStartIndex, getVisibleRange } from './virtualList.utils';\nimport { getScrollParent } from '../utils/scrollAncestors';\nimport { isWindowScroller, measureViewport, resolveScroller, scrollportBounds } from './scroller';\nimport type { Scroller, Viewport } from './scroller';\nimport type { VirtualListHandle, VirtualListScrollOptions, VirtualListScrollParent } from './virtualList.types';\n\n/**\n * Resolve a `rowGap` value to pixels for the offset math. Numbers and plain\n * `px`/numeric strings parse directly (no DOM, SSR/test-safe); any other CSS\n * length (`em`, `rem`, `%`, `var(...)`) is resolved via a hidden probe appended\n * to `contextEl` so it inherits the list's font-size and custom properties.\n */\nfunction resolveCssLength(value: number | string, contextEl: HTMLElement | null): number {\n  if (typeof value === 'number') return value;\n  const trimmed = value.trim();\n  if (trimmed === '') return 0;\n  const asNumber = Number(trimmed);\n  if (!Number.isNaN(asNumber)) return asNumber; // \"8\" → 8\n  const pxMatch = /^(-?\\d*\\.?\\d+)px$/.exec(trimmed);\n  if (pxMatch) return parseFloat(pxMatch[1]); // \"8px\" → 8\n  if (typeof document === 'undefined' || !contextEl) return 0;\n  const probe = document.createElement('div');\n  probe.style.cssText = `position:absolute;visibility:hidden;height:${trimmed};`;\n  contextEl.appendChild(probe);\n  const px = probe.getBoundingClientRect().height;\n  contextEl.removeChild(probe);\n  return Number.isFinite(px) ? px : 0;\n}\n\n/** No viewport holds this many rows at once, so rendering them all is a bug. */\nconst NOT_VIRTUALIZING_ROWS = 100;\n\ninterface UseVirtualWindowArgs<T> {\n  items: T[];\n  /** Stable key per row (already derived by the caller). */\n  keys: (string | number)[];\n  getItemKey?: (item: T, index: number) => string | number;\n  estimatedRowHeight: number;\n  rowGap: number | string;\n  overscan: number;\n  onLoadMore?: () => Promise<void>;\n  loadMoreThreshold: number;\n  /** Initial/filter load in progress — the body is replaced by a spinner. */\n  isLoading: boolean;\n  scrollParent?: VirtualListScrollParent;\n}\n\n/** Where the list is on screen, which is not its own box once it rides a scrollport. */\nexport interface ListViewportRect {\n  top: number;\n  bottom: number;\n  left: number;\n  right: number;\n}\n\n/** The list-space slice currently on screen, read live for keyboard navigation. */\nexport interface VisibleSlice {\n  top: number;\n  height: number;\n}\n\ninterface UseVirtualWindowResult {\n  rootRef: React.RefObject<HTMLDivElement | null>;\n  headerRef: React.RefObject<HTMLDivElement | null>;\n  /** Shared by every row to measure its height (read back via `data-index`). */\n  rowObserver: ResizeObserver | null;\n  /** Latest index → key mapping, for async consumers (e.g. the drag hook). */\n  keysRef: React.RefObject<(string | number)[]>;\n  offsets: number[];\n  totalHeight: number;\n  /** The list's visible slice, which is what decides which rows are rendered. */\n  containerHeight: number;\n  /** The whole scrollport, which is what a scroll target is aimed within. */\n  viewportHeight: number;\n  containerWidth: number;\n  startIndex: number;\n  endIndex: number;\n  showLoadMoreSpinner: boolean;\n  isWindowed: boolean;\n  getVisibleSlice: () => VisibleSlice;\n  getVisibleRect: () => ListViewportRect | null;\n  scrollBy: (dy: number) => void;\n  /** The scroll-related imperative methods, for the component to compose into\n   *  the forwarded handle alongside column controls. */\n  scrollApi: Omit<VirtualListHandle, 'recalculateColumns' | 'resetColumnWidths'>;\n}\n\n/**\n * The generic virtualization engine: viewport measurement, per-row height\n * caching, offset/visible-range computation, scroll coalescing, scroll\n * anchoring on height changes, infinite-load triggering, and the imperative\n * scroll handle. Mode-agnostic — knows nothing about columns, selection or\n * dragging.\n */\n/**\n * Loop guard for a jump re-aiming at its target. It normally ends because the\n * target stops moving, long before this; the cap is only there so a target that\n * can never be reached, the last row asked for with `align: 'start'`, can't\n * re-aim forever.\n */\nconst MAX_JUMP_CORRECTIONS = 20;\n\nexport function useVirtualWindow<T>({\n  items,\n  keys,\n  getItemKey,\n  estimatedRowHeight,\n  rowGap,\n  overscan,\n  onLoadMore,\n  loadMoreThreshold,\n  isLoading,\n  scrollParent,\n}: UseVirtualWindowArgs<T>): UseVirtualWindowResult {\n  const [scrollTop, setScrollTop] = useState(0);\n  const [containerHeight, setContainerHeight] = useState(0);\n  const [viewportHeight, setViewportHeight] = useState(0);\n  const [containerWidth, setContainerWidth] = useState(0);\n  // rowGap may be any CSS length; resolve it to px for the offset math. Seed with\n  // the synchronous fast path (number / px), then refine before paint for units\n  // that need the DOM (em/rem/%/var).\n  const [resolvedRowGap, setResolvedRowGap] = useState<number>(() => resolveCssLength(rowGap, null));\n  const [heightsMap, setHeightsMap] = useState<Map<string | number, number>>(new Map());\n  const [showLoadMoreSpinner, setShowLoadMoreSpinner] = useState(false);\n  const isLoadingMoreRef = useRef(false);\n\n  const rootRef = useRef<HTMLDivElement>(null);\n  const headerRef = useRef<HTMLDivElement>(null);\n  const prevOffsetsRef = useRef<number[]>([]);\n  const scrollRafRef = useRef<number | null>(null);\n  const keysRef = useRef<(string | number)[]>([]);\n\n  const isWindowed = scrollParent != null;\n  // The ref is what every callback reads, so none of them has to be rebuilt when\n  // the scroller resolves; the state exists only to re-run the listener effects.\n  const scrollerRef = useRef<Scroller | null>(null);\n  const nearestRef = useRef<Scroller | null>(null);\n  const [scroller, setScroller] = useState<Scroller | null>(null);\n  // No dependency list, and declared before every effect that measures: a\n  // getter's identity changes each render and the element it returns can appear\n  // at any commit. The setState cannot chain, because what it stores is stable\n  // across commits and it bails out on an equal one.\n  // oxlint-disable-next-line react-hooks/exhaustive-deps\n  useLayoutEffect(() => {\n    let next: Scroller | null;\n    if (scrollParent === 'nearest') {\n      // Walking the ancestors reads computed style and so forces a style recalc:\n      // resolve once, and again only if what it found has left the DOM.\n      const cached = nearestRef.current;\n      if (!cached || (!isWindowScroller(cached) && !cached.isConnected))\n        nearestRef.current = rootRef.current ? (getScrollParent(rootRef.current) ?? window) : null;\n      next = nearestRef.current;\n    } else {\n      next = resolveScroller(scrollParent);\n    }\n    scrollerRef.current = next;\n    setScroller((prev) => (prev === next ? prev : next));\n  });\n\n  // One ResizeObserver shared by every row, created once via a lazy state\n  // initializer so rows mounting in the same commit can register in their\n  // layout effect. Batching all entries into a single setState avoids a\n  // re-render per row when many rows enter the viewport at once (the main\n  // source of scroll jank). The row index is read back from `data-index`.\n  const [rowObserver] = useState<ResizeObserver | null>(() => {\n    if (typeof ResizeObserver === 'undefined') return null;\n    return new ResizeObserver((entries) => {\n      setHeightsMap((prev) => {\n        let next: Map<string | number, number> | null = null;\n        for (const entry of entries) {\n          const el = entry.target as HTMLElement;\n          const idx = Number(el.dataset.index);\n          if (Number.isNaN(idx)) continue;\n          // Resolve the row's stable key so the cache survives reorders/filters.\n          const key = keysRef.current[idx];\n          if (key === undefined) continue;\n          // Prefer the size the observer already computed — reading offsetHeight\n          // here would force a synchronous reflow on every measurement batch.\n          const box = entry.borderBoxSize?.[0];\n          const h = box ? Math.round(box.blockSize) : el.offsetHeight;\n          if (prev.get(key) === h) continue;\n          if (!next) next = new Map(prev);\n          next.set(key, h);\n        }\n        return next ?? prev;\n      });\n    });\n  });\n  useEffect(() => () => rowObserver?.disconnect(), [rowObserver]);\n\n  const applyViewport = useCallback((v: Viewport) => {\n    const top = Math.max(0, v.rel);\n    setScrollTop((prev) => (prev === top ? prev : top));\n    setContainerHeight((prev) => (prev === v.visibleHeight ? prev : v.visibleHeight));\n    setViewportHeight((prev) => (prev === v.viewportHeight ? prev : v.viewportHeight));\n    setContainerWidth((prev) => (prev === v.width ? prev : v.width));\n  }, []);\n\n  const syncViewport = useCallback(() => {\n    const el = rootRef.current;\n    if (!el) return;\n    applyViewport(measureViewport(el, scrollerRef.current));\n  }, [applyViewport]);\n\n  const getVisibleSlice = useCallback((): VisibleSlice => {\n    const el = rootRef.current;\n    if (!el) return { top: 0, height: 0 };\n    const v = measureViewport(el, scrollerRef.current);\n    return { top: Math.max(0, v.rel), height: v.visibleHeight };\n  }, []);\n\n  const getVisibleRect = useCallback((): ListViewportRect | null => {\n    const el = rootRef.current;\n    if (!el) return null;\n    const rect = el.getBoundingClientRect();\n    const scrollerNow = scrollerRef.current;\n    if (!scrollerNow) return { top: rect.top, bottom: rect.bottom, left: rect.left, right: rect.right };\n    const { top, height } = scrollportBounds(scrollerNow);\n    return {\n      top: Math.max(rect.top, top),\n      bottom: Math.min(rect.bottom, top + height),\n      left: rect.left,\n      right: rect.right,\n    };\n  }, []);\n\n  const scrollBy = useCallback((dy: number) => {\n    const el = rootRef.current;\n    if (!el) return;\n    const scrollerNow = scrollerRef.current;\n    if (!scrollerNow) el.scrollTop += dy;\n    else if (isWindowScroller(scrollerNow)) scrollerNow.scrollBy(0, dy);\n    else scrollerNow.scrollTop += dy;\n  }, []);\n\n  useLayoutEffect(() => {\n    const el = rootRef.current;\n    if (!el) return;\n    // Measure synchronously before the first paint so the initial render fills\n    // the viewport, instead of painting a few overscan rows and re-rendering.\n    syncViewport();\n    const observer = typeof ResizeObserver === 'undefined' ? null : new ResizeObserver(() => syncViewport());\n    // Track width too: `fit` columns are measured against this box and capped at\n    // its width, so when the container settles to its real width after a route\n    // transition / async layout, that change has to re-trigger the measure —\n    // otherwise an early measure against a 0/tiny width gets pinned and sticks.\n    observer?.observe(el);\n    if (!scroller) return () => observer?.disconnect();\n    // Where the list sits inside its scrollport is half of every measurement,\n    // and an element that merely *moves* fires nothing. The scrollport's own\n    // content box catches the common cause, something above the list growing;\n    // `remeasure()` is there for the rest.\n    observer?.observe(isWindowScroller(scroller) ? document.documentElement : scroller);\n    window.addEventListener('resize', syncViewport);\n    return () => {\n      observer?.disconnect();\n      window.removeEventListener('resize', syncViewport);\n    };\n  }, [scroller, syncViewport]);\n\n  // Read through a ref so the trigger below stays stable even when the consumer\n  // passes a fresh `onLoadMore` closure on every render.\n  const onLoadMoreRef = useRef(onLoadMore);\n  useLayoutEffect(() => {\n    onLoadMoreRef.current = onLoadMore;\n  });\n\n  /** Start a load unless one is already in flight (and drive the bottom spinner). */\n  const triggerLoadMore = useCallback(() => {\n    const load = onLoadMoreRef.current;\n    if (!load || isLoadingMoreRef.current) return;\n    isLoadingMoreRef.current = true;\n    setShowLoadMoreSpinner(true);\n    load().finally(() => {\n      isLoadingMoreRef.current = false;\n      setShowLoadMoreSpinner(false);\n    });\n  }, []);\n\n  // One ask per batch, the same probe the fill check below uses. The end of the\n  // list staying in view keeps qualifying, and in `scrollParent` mode it does so\n  // for the whole rest of the page.\n  const loadMoreProbeRef = useRef(-1);\n  const handleScroll = useCallback(() => {\n    const el = rootRef.current;\n    if (!el) return;\n    // Only the load-more check needs its answer now; the render measures again in\n    // the frame, so a scroll-anchoring correction landing in between is not undone\n    // by a reading taken before it.\n    const v = measureViewport(el, scrollerRef.current);\n    // Coalesce scroll-driven re-renders to at most one per animation frame.\n    // Scroll events can fire several times per frame (especially while dragging\n    // the scrollbar thumb); rendering the visible window once per frame keeps\n    // the content in step with the cursor instead of lagging behind.\n    if (scrollRafRef.current == null) {\n      scrollRafRef.current = requestAnimationFrame(() => {\n        scrollRafRef.current = null;\n        syncViewport();\n      });\n    }\n\n    if (v.distanceToEnd <= loadMoreThreshold && loadMoreProbeRef.current !== keysRef.current.length) {\n      loadMoreProbeRef.current = keysRef.current.length;\n      triggerLoadMore();\n    }\n  }, [syncViewport, loadMoreThreshold, triggerLoadMore]);\n\n  // Imperative rather than an `onScroll` prop, because with `scrollParent` the\n  // events happen on an element the list does not render. Doing it the same way\n  // in both modes also leaves the root's `onScroll` free for the consumer.\n  useEffect(() => {\n    const target: EventTarget | null = scroller ?? rootRef.current;\n    if (!target) return;\n    target.addEventListener('scroll', handleScroll, { passive: true });\n    return () => target.removeEventListener('scroll', handleScroll);\n  }, [scroller, handleScroll]);\n\n  useEffect(\n    () => () => {\n      if (scrollRafRef.current != null) cancelAnimationFrame(scrollRafRef.current);\n    },\n    [],\n  );\n\n  useLayoutEffect(() => {\n    keysRef.current = keys;\n  });\n\n  // Resolve the gap to px before paint, re-running when the value or the\n  // container's layout changes (a theme/font change that alters em/var is caught\n  // by the container ResizeObserver feeding containerWidth/Height).\n  useLayoutEffect(() => {\n    const px = resolveCssLength(rowGap, rootRef.current);\n    setResolvedRowGap((prev) => (prev === px ? prev : px));\n  }, [rowGap, containerWidth, containerHeight]);\n\n  // What an unmeasured row is assumed to be. `estimatedRowHeight` is only the\n  // seed: once rows have been measured, their average is a far better guess, and\n  // it is the guess every row between here and a distant jump target is made of.\n  // With a fixed estimate the error compounds over that distance (40 assumed\n  // against 35 real puts row 75 at row 86), and no amount of re-aiming fixes it,\n  // because the rows that are wrong are precisely the ones never rendered.\n  const assumedRowHeight = useMemo(() => {\n    if (heightsMap.size === 0) return estimatedRowHeight;\n    let total = 0;\n    for (const height of heightsMap.values()) total += height;\n    return total / heightsMap.size;\n  }, [heightsMap, estimatedRowHeight]);\n\n  const { offsets, totalHeight } = useMemo(\n    () => buildOffsets(keys, heightsMap, assumedRowHeight, resolvedRowGap),\n    [keys, heightsMap, assumedRowHeight, resolvedRowGap],\n  );\n\n  // The scroll trigger above can only fire on a list that actually scrolls. When\n  // a batch is smaller than the viewport there is no overflow, so no scroll event\n  // ever happens and lazy loading dead-ends after the first batch. Keep asking\n  // for more until the content overflows; the consumer ends the loop by appending\n  // nothing (or by dropping `onLoadMore` once its source is exhausted).\n  //\n  // Each (row count, viewport height) pair is probed at most once, so rows\n  // settling to their measured heights can't re-ask a source that already ran\n  // dry, and progress depends entirely on the consumer appending rows.\n  //\n  // The bar to clear is the whole scrollport, not the slice the list occupies\n  // now: a windowed list scrolling in from the bottom of a page would otherwise\n  // stop filling at whatever happened to show.\n  const fillProbeRef = useRef('');\n  const fillHeight = isWindowed ? viewportHeight : containerHeight;\n  useEffect(() => {\n    if (!onLoadMore || isLoading || showLoadMoreSpinner) return;\n    // 0 while the list is unmeasured or `display: none` — nothing to fill yet.\n    if (fillHeight === 0) return;\n    // The sticky header eats into the scroll area, so it isn't usable space.\n    const headerH = headerRef.current?.offsetHeight ?? 0;\n    if (totalHeight >= fillHeight - headerH) return; // already scrollable\n    const probe = `${items.length}:${fillHeight}`;\n    if (fillProbeRef.current === probe) return;\n    fillProbeRef.current = probe;\n    triggerLoadMore();\n  }, [onLoadMore, isLoading, showLoadMoreSpinner, items.length, totalHeight, fillHeight, triggerLoadMore]);\n\n  /** Put list-space offset `top` at the top of the scrollport, whoever owns it. */\n  const scrollToOffset = useCallback((top: number, behavior?: ScrollBehavior) => {\n    const el = rootRef.current;\n    if (!el) return;\n    const scrollerNow = scrollerRef.current;\n    if (!scrollerNow) {\n      const clamped = Math.max(0, top);\n      // Assignment rather than `scrollTo` when no behaviour was asked for: it is\n      // what the anchoring correction needs, and the two are the same for `auto`.\n      if (behavior && typeof el.scrollTo === 'function') el.scrollTo({ top: clamped, behavior });\n      else el.scrollTop = clamped;\n      return;\n    }\n    // List space is relative, so the move is a delta on where the scrollport is\n    // now, and `rel` is unclamped here: a list still below it has to be reachable\n    // from above as well as from within.\n    const { rel } = measureViewport(el, scrollerNow);\n    if (isWindowScroller(scrollerNow)) {\n      scrollerNow.scrollTo({ top: Math.max(0, scrollerNow.scrollY + top - rel), behavior: behavior ?? 'auto' });\n      return;\n    }\n    const target = Math.max(0, scrollerNow.scrollTop + top - rel);\n    if (behavior && typeof scrollerNow.scrollTo === 'function') scrollerNow.scrollTo({ top: target, behavior });\n    else scrollerNow.scrollTop = target;\n  }, []);\n\n  useLayoutEffect(() => {\n    const el = rootRef.current;\n    const prevOffsets = prevOffsetsRef.current;\n    prevOffsetsRef.current = offsets;\n\n    // Nothing to anchor against on the first pass or when pinned at the top.\n    if (!el || prevOffsets.length === 0) return;\n    // A jump in flight is aiming at a row; anchoring would hold the row it\n    // happens to be passing instead, and the two would fight to a standstill.\n    if (pendingJumpRef.current) return;\n    const curScrollTop = getVisibleSlice().top;\n    if (curScrollTop <= 0) return;\n\n    // Scroll anchoring: pin the row at the top of the viewport. Find it under\n    // the *previous* offsets, then re-derive scrollTop from its *new* offset so\n    // it stays visually fixed when rows above it are measured or resized. This\n    // is more robust than summing per-row height deltas — it can't drift, and\n    // one correction covers any number of simultaneous changes above the fold.\n    const prevCount = prevOffsets.length - 1;\n    const anchor = findStartIndex(prevOffsets, curScrollTop, prevCount);\n    if (anchor >= items.length) return;\n    const within = curScrollTop - prevOffsets[anchor];\n    const target = offsets[anchor] + within;\n    if (Math.abs(target - curScrollTop) > 0.5) {\n      scrollToOffset(target);\n      setScrollTop(target);\n    }\n  }, [offsets, items.length, getVisibleSlice, scrollToOffset]);\n\n  /** Scroll position that puts row `index` where `align` asks for, or null if there is none. */\n  const computeScrollTop = useCallback(\n    (index: number, options?: VirtualListScrollOptions): number | null => {\n      const el = rootRef.current;\n      if (!el || items.length === 0) return null;\n      const i = Math.max(0, Math.min(index, items.length - 1));\n      const rowTop = offsets[i] ?? 0;\n      const rowHeight = (offsets[i + 1] ?? rowTop) - rowTop;\n      // Row offsets are measured from the top of the body, which itself starts\n      // below the sticky header: the header is in flow, so scrolling by exactly\n      // `rowTop` already puts the row flush under it. Subtracting the header\n      // height on top of that, as this once did, left the row one header lower,\n      // which in a table whose rows are about as tall as its header reads as\n      // landing a row short.\n      const headerHeight = headerRef.current?.offsetHeight ?? 0;\n      // The whole scrollport, not the slice shown now: the jump is aimed at where\n      // the row lands after the scroll, by which point the list covers it.\n      const usable = Math.max(0, measureViewport(el, scrollerRef.current).viewportHeight - headerHeight);\n      const align = options?.align ?? 'start';\n      let top = rowTop;\n      if (align === 'center') top -= (usable - rowHeight) / 2;\n      else if (align === 'end') top -= usable - rowHeight;\n      return top;\n    },\n    [items.length, offsets],\n  );\n\n  // A jump lands on the wrong row while the rows between here and there have\n  // never been rendered: their offsets are `estimatedRowHeight` guesses, and the\n  // error compounds over the distance (40px guessed against 35px real puts row\n  // 75 at row 86). Scrolling there renders those rows, measuring corrects the\n  // offsets, and the effect below re-aims at the target until it stops moving.\n  // This is why the same jump is accurate once the list has been scrolled\n  // through: by then every row in between carries a measured height.\n  const pendingJumpRef = useRef<{\n    index: number;\n    options?: VirtualListScrollOptions;\n    lastTarget: number;\n    attempts: number;\n    items: T[];\n  } | null>(null);\n\n  // Deliberately without a dependency list: every commit is a chance that new\n  // measurements have moved the target, including the ones that come from the\n  // scroll position itself changing mid-animation.\n  useLayoutEffect(() => {\n    const pending = pendingJumpRef.current;\n    const el = rootRef.current;\n    if (!pending) return;\n    const target = pending.items === items && el ? computeScrollTop(pending.index, pending.options) : null;\n    if (target == null) {\n      pendingJumpRef.current = null; // new data supersedes an in-flight jump\n      return;\n    }\n    const isStable = Math.abs(target - pending.lastTarget) <= 1;\n    // Only once it has both stopped moving and been reached: with a smooth\n    // behaviour the target can sit still for several frames while the animation\n    // is still on its way there, and giving up then would leave it short.\n    if ((isStable && Math.abs(getVisibleSlice().top - target) <= 1) || pending.attempts >= MAX_JUMP_CORRECTIONS) {\n      pendingJumpRef.current = null;\n      return;\n    }\n    if (isStable) return;\n    pending.lastTarget = target;\n    pending.attempts += 1;\n    scrollToOffset(target, pending.options?.behavior);\n  });\n\n  // Any input of the user's own outranks a jump still in flight: being pulled\n  // back to a row you just scrolled away from is worse than not arriving.\n  useEffect(() => {\n    const el = rootRef.current;\n    if (!el) return;\n    const cancel = () => {\n      pendingJumpRef.current = null;\n    };\n    // The wheel and the finger belong to whatever scrolls; the keys belong to\n    // the list, and hijacking them page-wide would cancel on any typing.\n    const pointerTarget: EventTarget = scroller ?? el;\n    pointerTarget.addEventListener('wheel', cancel, { passive: true });\n    pointerTarget.addEventListener('touchstart', cancel, { passive: true });\n    el.addEventListener('keydown', cancel);\n    return () => {\n      pointerTarget.removeEventListener('wheel', cancel);\n      pointerTarget.removeEventListener('touchstart', cancel);\n      el.removeEventListener('keydown', cancel);\n    };\n  }, [scroller]);\n\n  const scrollApi = useMemo<Omit<VirtualListHandle, 'recalculateColumns' | 'resetColumnWidths'>>(() => {\n    function scrollToIndex(index: number, options?: VirtualListScrollOptions) {\n      const target = computeScrollTop(index, options);\n      if (target == null) return;\n      pendingJumpRef.current = { index, options, lastTarget: target, attempts: 0, items };\n      scrollToOffset(target, options?.behavior);\n    }\n    return {\n      scrollToIndex,\n      scrollToKey(key, options) {\n        const i = items.findIndex((item, idx) => (getItemKey ? getItemKey(item, idx) : idx) === key);\n        if (i >= 0) scrollToIndex(i, options);\n      },\n      scrollToTop(options) {\n        pendingJumpRef.current = null;\n        scrollToOffset(0, options?.behavior);\n      },\n      getScrollElement: () => {\n        const scrollerNow = scrollerRef.current;\n        if (!scrollerNow) return rootRef.current;\n        return isWindowScroller(scrollerNow) ? null : scrollerNow;\n      },\n      remeasure: syncViewport,\n    };\n  }, [items, getItemKey, computeScrollTop, scrollToOffset, syncViewport]);\n\n  const { startIndex, endIndex } = useMemo(\n    () => getVisibleRange(offsets, scrollTop, containerHeight, overscan, items.length),\n    [offsets, scrollTop, containerHeight, overscan, items.length],\n  );\n\n  // Virtualizing nothing fails silently: the page is simply slow, which points\n  // nowhere near the cause. That cause is almost always a viewport as tall as its\n  // content, which is what a percentage height amounts to inside a parent with no\n  // definite height of its own.\n  const warnedRef = useRef(false);\n  useEffect(() => {\n    if (warnedRef.current || containerHeight === 0) return;\n    if (items.length < NOT_VIRTUALIZING_ROWS || endIndex - startIndex + 1 < items.length) return;\n    warnedRef.current = true;\n    console.warn(\n      `VirtualList: rendering all ${items.length} rows, so nothing is being virtualized. Its viewport is as tall as its content. Give the list a definite height, or pass scrollParent to have it ride a scrollport it does not own.`,\n    );\n  }, [containerHeight, items.length, startIndex, endIndex]);\n\n  return {\n    rootRef,\n    headerRef,\n    rowObserver,\n    keysRef,\n    offsets,\n    totalHeight,\n    containerHeight,\n    viewportHeight,\n    containerWidth,\n    startIndex,\n    endIndex,\n    showLoadMoreSpinner,\n    isWindowed,\n    getVisibleSlice,\n    getVisibleRect,\n    scrollBy,\n    scrollApi,\n  };\n}\n","import { useState, useMemo } from 'react';\n\ninterface UseSelectionArgs<T> {\n  items: T[];\n  getKey: (item: T, index: number) => string | number;\n  multiSelect: boolean;\n  /** Controlled selected keys. When provided, internal state is ignored. */\n  selectedKeys?: Set<string | number>;\n  onSelectionChange?: (keys: Set<string | number>) => void;\n}\n\ninterface UseSelectionResult {\n  effectiveSelectedKeys: Set<string | number>;\n  /** Toggle one row's membership in the selection. */\n  handleRowSelect: (key: string | number) => void;\n  /** Select all rows, or clear if all are already selected. */\n  handleSelectAll: () => void;\n  allSelected: boolean;\n  someSelected: boolean;\n}\n\n/**\n * Multi-row selection, controlled or uncontrolled. `allSelected`/`someSelected`\n * (for the header checkbox) are O(n) scans, so they're only computed when\n * `multiSelect` is on.\n */\nexport function useSelection<T>({\n  items,\n  getKey,\n  multiSelect,\n  selectedKeys,\n  onSelectionChange,\n}: UseSelectionArgs<T>): UseSelectionResult {\n  const isControlled = selectedKeys !== undefined;\n  const [internalSelectedKeys, setInternalSelectedKeys] = useState<Set<string | number>>(new Set());\n  const effectiveSelectedKeys = isControlled ? selectedKeys! : internalSelectedKeys;\n\n  function applySelection(next: Set<string | number>) {\n    if (!isControlled) setInternalSelectedKeys(next);\n    onSelectionChange?.(next);\n  }\n\n  function handleRowSelect(key: string | number) {\n    const next = new Set(effectiveSelectedKeys);\n    if (next.has(key)) next.delete(key);\n    else next.add(key);\n    applySelection(next);\n  }\n\n  function handleSelectAll() {\n    const allKeys = items.map((item, i) => getKey(item, i));\n    const allChosen = allKeys.every((k) => effectiveSelectedKeys.has(k));\n    applySelection(allChosen ? new Set() : new Set(allKeys));\n  }\n\n  // O(n) header-checkbox state. The component re-renders every scroll frame\n  // (scrollTop is state), but these depend only on the items and the selection —\n  // so memoise to keep two full-list scans off the scroll path.\n  const { allSelected, someSelected } = useMemo(() => {\n    if (!multiSelect || items.length === 0) return { allSelected: false, someSelected: false };\n    const all = items.every((item, i) => effectiveSelectedKeys.has(getKey(item, i)));\n    const some = !all && items.some((item, i) => effectiveSelectedKeys.has(getKey(item, i)));\n    return { allSelected: all, someSelected: some };\n  }, [multiSelect, items, effectiveSelectedKeys, getKey]);\n\n  return { effectiveSelectedKeys, handleRowSelect, handleSelectAll, allSelected, someSelected };\n}\n","import React, { useState, useRef, useEffect, useLayoutEffect, useCallback } from 'react';\n\nimport {\n  buildColumnTemplate,\n  buildFrozenTemplate,\n  columnWidthVar,\n  DEFAULT_FROZEN_WIDTH,\n  measureCellWidth,\n  loadPersistedColumns,\n  isColumnInitiallyVisible,\n  COLUMN_STORAGE_PREFIX,\n  type PersistedColumns,\n} from './virtualList.utils';\nimport type { VirtualListColumn } from './virtualList.types';\n\ninterface UseColumnsArgs<T> {\n  columns?: VirtualListColumn<T>[];\n  items: T[];\n  multiSelect: boolean;\n  visibleColumnKeys?: string[];\n  onVisibleColumnsChange?: (keys: string[]) => void;\n  persistColumnsKey?: string;\n  showColumnToggle?: boolean;\n  /** User-set column widths, from `useColumnResize`. */\n  userWidths: Record<string, number>;\n  /** Whether those widths have taken over the layout. */\n  isFrozen: boolean;\n  rootRef: React.RefObject<HTMLDivElement | null>;\n  /** Visible-window bounds — re-measure `fit` columns as new cells scroll in. */\n  startIndex: number;\n  endIndex: number;\n  containerHeight: number;\n  /** Re-measure `fit` columns when the container width settles (e.g. after a route transition). */\n  containerWidth: number;\n  /** Re-measure `fit` columns when rows replace the spinner under the same `items`. */\n  isLoading: boolean;\n}\n\nexport interface UseColumnsResult<T> {\n  hasColumns: boolean;\n  visibleColumns: VirtualListColumn<T>[];\n  columnTemplate: string | undefined;\n  /**\n   * Custom properties for the list element: the total width of a frozen layout,\n   * as a `calc()` over the per-column properties so that rewriting one width\n   * during a drag updates the total without any bookkeeping.\n   */\n  widthVars: React.CSSProperties;\n  shouldShowToggle: boolean;\n  effectiveVisible: Set<string>;\n  /** How many default-visible columns the user has hidden (excludes defaultHidden). */\n  manuallyHiddenCount: number;\n  toggleOpen: boolean;\n  setToggleOpen: React.Dispatch<React.SetStateAction<boolean>>;\n  handleToggleColumn: (key: string) => void;\n  /** Imperative escape hatch: drop fit-column pins so they re-measure on the\n   *  next layout. For out-of-band content changes that don't flow through a new\n   *  `items` array or `columns` set (which reset automatically). */\n  recalculateColumns: () => void;\n}\n\n/**\n * Table-mode column logic: visibility (controlled or uncontrolled, with\n * localStorage persistence and runtime reconciliation), the toggle popover, and\n * `fit`-column width measurement that pins one shared px track across the header\n * and every row grid so they stay aligned.\n */\nexport function useColumns<T>({\n  columns,\n  items,\n  multiSelect,\n  visibleColumnKeys,\n  onVisibleColumnsChange,\n  persistColumnsKey,\n  showColumnToggle,\n  userWidths,\n  isFrozen,\n  rootRef,\n  startIndex,\n  endIndex,\n  containerHeight,\n  containerWidth,\n  isLoading,\n}: UseColumnsArgs<T>): UseColumnsResult<T> {\n  const isControlled = visibleColumnKeys !== undefined;\n  const [toggleOpen, setToggleOpen] = useState(false);\n  // Measured px widths for `fit` columns, keyed by column key. Pinning these\n  // into the shared column template keeps the header and rows aligned (a raw\n  // `fit-content` track resolves per-grid and drifts). See the effects below.\n  const [fitWidths, setFitWidths] = useState<Record<string, number>>({});\n\n  const [internalVisibleKeys, setInternalVisibleKeys] = useState<Set<string>>(() => {\n    const persisted = loadPersistedColumns(persistColumnsKey);\n    return new Set((columns ?? []).filter((c) => isColumnInitiallyVisible(c, persisted)).map((c) => c.key));\n  });\n  // The set of column keys the internal visibility state was last reconciled\n  // against. The lazy initializer above only runs at mount, so without this a\n  // list that mounts in list mode (no columns) and later switches to table mode\n  // would keep an empty visible set and render no columns.\n  const knownColumnKeysRef = useRef<Set<string>>(new Set((columns ?? []).map((c) => c.key)));\n\n  // Re-derive internal visibility when the column set changes at runtime: new\n  // columns get their saved/default visibility, removed columns drop out, and\n  // existing columns keep whatever the user toggled them to. This is also what\n  // applies persisted settings when a list switches from list mode to table\n  // mode (columns absent at mount, supplied later).\n  useEffect(() => {\n    if (isControlled) return;\n    const cols = columns ?? [];\n    const currentKeys = new Set(cols.map((c) => c.key));\n    const known = knownColumnKeysRef.current;\n    let identical = currentKeys.size === known.size;\n    if (identical)\n      for (const k of currentKeys)\n        if (!known.has(k)) {\n          identical = false;\n          break;\n        }\n    if (identical) return;\n    knownColumnKeysRef.current = currentKeys;\n    const persisted = loadPersistedColumns(persistColumnsKey);\n    setInternalVisibleKeys((prev) => {\n      const next = new Set<string>();\n      for (const c of cols) {\n        if (known.has(c.key)) {\n          if (prev.has(c.key)) next.add(c.key); // preserve the current-session choice\n        } else if (isColumnInitiallyVisible(c, persisted)) {\n          next.add(c.key); // newly-added column: saved setting, else defaultHidden\n        }\n      }\n      return next;\n    });\n  }, [columns, isControlled, persistColumnsKey]);\n\n  // Persist visibility whenever it changes (uncontrolled mode only). Skip while\n  // there are no columns, or the transient empty state during a list→table\n  // switch before the reconcile effect above has filled in the visible set —\n  // writing then would clobber the saved config with an empty set.\n  useEffect(() => {\n    if (isControlled || !persistColumnsKey || typeof window === 'undefined') return;\n    const cols = columns ?? [];\n    if (cols.length === 0 || internalVisibleKeys.size === 0) return;\n    try {\n      window.localStorage.setItem(\n        COLUMN_STORAGE_PREFIX + persistColumnsKey,\n        JSON.stringify({\n          visible: [...internalVisibleKeys],\n          known: cols.map((c) => c.key),\n          widths: userWidths,\n        } satisfies PersistedColumns),\n      );\n    } catch {\n      // Storage full or unavailable — non-fatal, just don't persist.\n    }\n  }, [internalVisibleKeys, columns, isControlled, persistColumnsKey, userWidths]);\n\n  // Locked columns can never be hidden — force them into the visible set even if\n  // a controlled `visibleColumnKeys` or persisted state omits them. Only clone\n  // (rather than mutate the state Set) when a locked key is actually missing;\n  // in uncontrolled mode they're already present via isColumnInitiallyVisible.\n  const baseVisible: Set<string> = isControlled ? new Set(visibleColumnKeys) : internalVisibleKeys;\n  const lockedKeys = (columns ?? []).filter((c) => c.lockVisible).map((c) => c.key);\n  const effectiveVisible: Set<string> = lockedKeys.some((k) => !baseVisible.has(k))\n    ? new Set([...baseVisible, ...lockedKeys])\n    : baseVisible;\n\n  const hasColumns = !!columns?.length;\n  const visibleColumns = hasColumns ? columns!.filter((c) => effectiveVisible.has(c.key)) : [];\n  // A column counts as \"manually hidden\" only if it's default-visible yet\n  // currently hidden — a defaultHidden column being hidden is its default\n  // state, not a user choice, so it never contributes to the chip.\n  const manuallyHiddenCount = hasColumns\n    ? columns!.filter((c) => !c.defaultHidden && !effectiveVisible.has(c.key)).length\n    : 0;\n  const selectPrefix = multiSelect && hasColumns ? '40px ' : '';\n  const columnTemplate = hasColumns\n    ? `${selectPrefix}${isFrozen ? buildFrozenTemplate(visibleColumns) : buildColumnTemplate(visibleColumns, fitWidths)}`\n    : undefined;\n  // Hide the gear entirely when nothing is left to toggle (every column opts out).\n  const shouldShowToggle = showColumnToggle !== false && hasColumns && columns!.some((c) => !c.hideFromToggle);\n\n  // Sum of the tracks, so the header and body can stretch past the viewport and\n  // the list scrolls horizontally instead of clipping. Kept as a `calc()` over\n  // the same custom properties the tracks read, so a drag rewriting one of them\n  // resizes the row canvas in the same layout pass.\n  const widthVars: React.CSSProperties = {};\n  if (isFrozen && hasColumns) {\n    const parts = visibleColumns.map((c) => `var(${columnWidthVar(c.key)}, ${DEFAULT_FROZEN_WIDTH}px)`);\n    if (multiSelect) parts.unshift('40px');\n    if (shouldShowToggle) parts.push('32px');\n    (widthVars as Record<string, string>)['--vl-total'] = `calc(${parts.join(' + ')})`;\n  }\n\n  // ── `fit` column width measurement ──────────────────────────────────────────\n  const fitColumns = visibleColumns.filter((c) => c.width.type === 'fit');\n  // A frozen layout is exact px and ignores the pins, so measuring would only\n  // fight the user: `measureFitWidths` never shrinks a column, and every drag\n  // narrower than the content would be undone on the next scroll frame.\n  const hasFit = !isFrozen && fitColumns.length > 0;\n  const fitColKey = fitColumns.map((c) => c.key).join('|');\n\n  // Drop the measured pins when the data or the fit-column set changes, so the\n  // measure effect below re-reads the true intrinsic width on the next pass —\n  // this is what lets a column shrink (during scroll the effect only grows it,\n  // to avoid jitter). Resetting state during render is React's recommended\n  // pattern for \"reset on prop change\" and avoids a cascading effect render.\n  const [fitReset, setFitReset] = useState<{ key: string; items: T[] }>({ key: fitColKey, items });\n  if (fitReset.key !== fitColKey || fitReset.items !== items) {\n    setFitReset({ key: fitColKey, items });\n    if (Object.keys(fitWidths).length > 0) setFitWidths({});\n  }\n\n  // Measure every fit cell currently in the DOM (header + visible rows), keyed\n  // by its `data-fit-key`, and grow each column's pin to the widest. Reading the\n  // key off the DOM (rather than closing over `fitColumns`) keeps this a stable\n  // callback the ResizeObserver below can reuse. `measureCellWidth` reports the\n  // true content width even when the cell is pinned narrower, so a later pass\n  // always recovers a column first measured while it was still empty.\n  const measureFitWidths = useCallback(() => {\n    const root = rootRef.current;\n    if (!root) return;\n    const maxByKey = new Map<string, number>();\n    root.querySelectorAll('[data-fit-key]').forEach((node) => {\n      const el = node as HTMLElement;\n      const key = el.getAttribute('data-fit-key');\n      if (!key) return;\n      const w = measureCellWidth(el);\n      if (w > (maxByKey.get(key) ?? 0)) maxByKey.set(key, w);\n    });\n    if (maxByKey.size === 0) return;\n    setFitWidths((prev) => {\n      let changed = false;\n      const next = { ...prev };\n      for (const [key, max] of maxByKey) {\n        if (max <= 0) continue;\n        // Cap at the viewport width so one oversized cell can't blow out layout.\n        const pinned = Math.min(Math.ceil(max), root.clientWidth);\n        if (pinned > (prev[key] ?? 0)) {\n          next[key] = pinned;\n          changed = true;\n        }\n      }\n      return changed ? next : prev;\n    });\n  }, [rootRef]);\n\n  // One ResizeObserver shared across all fit cells. It re-measures whenever a\n  // cell's box changes — crucially when content first appears after mount (which\n  // changes the cell's height even while its width is pinned), so a too-early or\n  // empty-cell measure self-corrects instead of sticking. Its initial callback\n  // also fires once after layout settles, covering route transitions and other\n  // post-mount layout shifts without depending on font/rAF timing.\n  const [fitObserver] = useState<ResizeObserver | null>(() =>\n    typeof ResizeObserver === 'undefined' ? null : new ResizeObserver(() => measureFitWidths()),\n  );\n  const observedRef = useRef<Set<Element>>(new Set());\n  useEffect(\n    () => () => {\n      fitObserver?.disconnect();\n      observedRef.current.clear();\n    },\n    [fitObserver],\n  );\n\n  // Measure synchronously (before paint, so a wider row scrolling in never shows\n  // a clipped frame), then *diff* the observed set rather than re-observing every\n  // cell. Re-observing fires a redundant ResizeObserver callback per node, and\n  // `startIndex`/`endIndex` change every scroll frame — so observing only the\n  // cells that entered (and dropping the ones that left) keeps stable, already\n  // pinned cells from generating any observer traffic while scrolling. A newly\n  // entered cell still fires exactly once, which is when we want to measure it.\n  useLayoutEffect(() => {\n    if (!hasFit) return;\n    const root = rootRef.current;\n    if (!root) return;\n    measureFitWidths();\n    if (!fitObserver) return;\n    const current = new Set<Element>(root.querySelectorAll('[data-fit-key]'));\n    const observed = observedRef.current;\n    for (const el of observed)\n      if (!current.has(el)) {\n        fitObserver.unobserve(el);\n        observed.delete(el);\n      }\n    for (const el of current)\n      if (!observed.has(el)) {\n        fitObserver.observe(el);\n        observed.add(el);\n      }\n  }, [\n    rootRef,\n    hasFit,\n    fitColKey,\n    fitWidths,\n    startIndex,\n    endIndex,\n    containerHeight,\n    containerWidth,\n    items,\n    isLoading,\n    measureFitWidths,\n    fitObserver,\n  ]);\n\n  // Cheap insurance: re-measure on the next two frames after mount / data /\n  // fit-column changes, in case the icons (or the injected FontAwesome sizing\n  // CSS) resolve a frame after the first synchronous pass. Safe and scoped —\n  // measureFitWidths only ever grows a pin, and this runs on mount / data change\n  // (not every scroll frame), so it keeps the diff-observe perf win.\n  useEffect(() => {\n    if (!hasFit) return;\n    let raf2 = 0;\n    const raf1 = requestAnimationFrame(() => {\n      measureFitWidths();\n      raf2 = requestAnimationFrame(() => measureFitWidths());\n    });\n    return () => {\n      cancelAnimationFrame(raf1);\n      cancelAnimationFrame(raf2);\n    };\n  }, [hasFit, fitColKey, items, isLoading, measureFitWidths]);\n\n  // Force a fresh fit-column measure regardless of current pins. Always installs\n  // a new empty object so the measure layout effect re-runs even when nothing\n  // was pinned (e.g. content changed while the column was still fit-content).\n  const recalculateColumns = useCallback(() => setFitWidths({}), []);\n\n  function handleToggleColumn(key: string) {\n    // Locked columns can't be toggled off.\n    if (columns?.find((c) => c.key === key)?.lockVisible) return;\n    const next = new Set(effectiveVisible);\n    if (next.has(key)) {\n      if (next.size <= 1) return;\n      next.delete(key);\n    } else {\n      next.add(key);\n    }\n    const nextArray = [...next];\n    if (!isControlled) setInternalVisibleKeys(next);\n    onVisibleColumnsChange?.(nextArray);\n  }\n\n  return {\n    hasColumns,\n    visibleColumns,\n    columnTemplate,\n    widthVars,\n    shouldShowToggle,\n    effectiveVisible,\n    manuallyHiddenCount,\n    toggleOpen,\n    setToggleOpen,\n    handleToggleColumn,\n    recalculateColumns,\n  };\n}\n","import React, { useState, useRef, useEffect, useLayoutEffect, useCallback } from 'react';\n\nimport styles from './virtualList.module.pcss';\nimport { resolveReorderTarget, resolveTreeDropTarget, lastDescendantIndex } from './virtualList.utils';\nimport type { TreeDropPosition, TreeDropTarget } from './virtualList.types';\nimport type { ListViewportRect } from './useVirtualWindow';\n\ninterface UseRowDragArgs {\n  reorderable: boolean;\n  treeReorder: boolean;\n  onReorder?: (fromIndex: number, toIndex: number) => void;\n  onTreeDrop?: (dragKey: string | number, target: TreeDropTarget) => void;\n  longPressDelay: number;\n  dragHandle: boolean;\n  treeIndentPx: number;\n  /** Current flat ordering and depth tags (mouse handlers read these directly). */\n  keys: (string | number)[];\n  depths: number[];\n  isGroupAt: (index: number) => boolean;\n  rootRef: React.RefObject<HTMLDivElement | null>;\n  /** Latest keys for async (touch / rAF) consumers — owned by useVirtualWindow. */\n  keysRef: React.RefObject<(string | number)[]>;\n  /** Where the list is on screen, which is not its own box once it rides a\n   *  scrollport — owned by useVirtualWindow, as is `scrollBy`. */\n  getVisibleRect: () => ListViewportRect | null;\n  scrollBy: (dy: number) => void;\n}\n\n/** The drag-related props the parent spreads onto a given row. */\nexport interface RowDragProps {\n  reorderable: boolean;\n  /** Native mouse DnD is active for the list (independent of which row is armed). */\n  mouseDragEnabled: boolean;\n  draggable: boolean | undefined;\n  isDragging: boolean;\n  onMouseDown: ((e: React.MouseEvent<HTMLDivElement>) => void) | undefined;\n  onMouseOver: ((e: React.MouseEvent<HTMLDivElement>) => void) | undefined;\n  onMouseLeave: ((e: React.MouseEvent<HTMLDivElement>) => void) | undefined;\n  onDragStart: ((e: React.DragEvent<HTMLDivElement>) => void) | undefined;\n  onDragOver: ((e: React.DragEvent<HTMLDivElement>) => void) | undefined;\n  onDragEnd: (() => void) | undefined;\n  onDrop: ((e: React.DragEvent<HTMLDivElement>) => void) | undefined;\n  onTouchStart: ((e: React.TouchEvent<HTMLDivElement>) => void) | undefined;\n  dropPosition: TreeDropPosition | undefined;\n  dropIndentPx: number | undefined;\n}\n\ninterface UseRowDragResult {\n  /** True while a row is being dragged (for the root reordering style). */\n  draggingKey: string | number | null;\n  /** Whether either drag mode is active. */\n  dragEnabled: boolean;\n  /** Drag props for the row at `index` / `key`. */\n  getRowDragProps: (index: number, key: string | number) => RowDragProps;\n  /** Returns true (and resets) if the next click should be swallowed (post-drag). */\n  consumeSuppressedClick: () => boolean;\n}\n\n/**\n * Row drag-and-drop for both modes:\n * - `reorderable` (flat): native HTML5 drag on desktop; rows move live via\n *   `onReorder` with midpoint hysteresis.\n * - `treeReorder`: a drop indicator (before/after/inside) is shown during the\n *   drag and `onTreeDrop` fires once on release; dropping onto the dragged\n *   node's own subtree is forbidden.\n * Touch uses a long-press → lifted-clone → pointer-move path with edge\n * auto-scroll. `dragHandle` restricts initiation to `[data-drag-handle]`.\n */\nexport function useRowDrag({\n  reorderable,\n  treeReorder,\n  onReorder,\n  onTreeDrop,\n  longPressDelay,\n  dragHandle,\n  treeIndentPx,\n  keys,\n  depths,\n  isGroupAt,\n  rootRef,\n  keysRef,\n  getVisibleRect,\n  scrollBy,\n}: UseRowDragArgs): UseRowDragResult {\n  // The dragged item is tracked by its stable key (indices shift after each\n  // live reorder); the ref drives the logic, the state drives the row's style.\n  const dragKeyRef = useRef<string | number | null>(null);\n  const [draggingKey, setDraggingKey] = useState<string | number | null>(null);\n  // Tree-reorder drop indicator. Unlike flat reorder (which moves rows live),\n  // tree mode shows where the drop will land and commits once on release.\n  const [dropTarget, setDropTarget] = useState<TreeDropTarget | null>(null);\n  const dropTargetRef = useRef<TreeDropTarget | null>(null);\n  const onReorderRef = useRef(onReorder);\n  const onTreeDropRef = useRef(onTreeDrop);\n  const treeModeRef = useRef(treeReorder);\n  const depthsRef = useRef<number[]>(depths);\n  const isGroupAtRef = useRef<(i: number) => boolean>(isGroupAt);\n  // When `dragHandle` is on, a mousedown sets this to whether it landed on a\n  // `[data-drag-handle]` element, gating whether the ensuing dragstart is honoured.\n  const dragFromHandleRef = useRef(true);\n  // In `dragHandle` mode the `draggable` attribute is only on the row whose\n  // handle the pointer is currently over (\"armed\"). A permanently `draggable`\n  // row breaks mouse text selection (caret placement, double-click word select,\n  // drag select) inside any child input in Firefox — see Mozilla bugs 739071 /\n  // 800050 / 1189486. Arming on hover rather than on mousedown means the\n  // attribute is already in place before the press, so the browser never has to\n  // notice it appearing mid-gesture, and any pointer move re-derives it.\n  const armedKeyRef = useRef<string | number | null>(null);\n  const [armedKey, setArmedKeyState] = useState<string | number | null>(null);\n  const longPressRef = useRef<number | null>(null);\n  const touchActiveRef = useRef(false);\n  const touchStartYRef = useRef<number | null>(null);\n  const pointerXRef = useRef(0);\n  const pointerYRef = useRef(0);\n  const autoScrollRef = useRef<number | null>(null);\n  const suppressNextClickRef = useRef(false);\n  // Floating clone that follows the finger during a touch drag.\n  const dragCloneRef = useRef<HTMLElement | null>(null);\n  const grabOffsetYRef = useRef(0);\n  const cloneLeftRef = useRef(0);\n  const dragHeightRef = useRef(0);\n  // The native `draggable` attribute (mouse DnD) makes a long-press start the\n  // browser's own drag on touch devices, which hijacks our touch handlers. So\n  // expose it only where the primary pointer is fine (mouse/trackpad); touch\n  // devices use the touch handlers below instead.\n  const [isCoarsePointer] = useState(() =>\n    typeof window !== 'undefined' && typeof window.matchMedia === 'function'\n      ? window.matchMedia('(pointer: coarse)').matches\n      : false,\n  );\n\n  useLayoutEffect(() => {\n    onReorderRef.current = onReorder;\n    onTreeDropRef.current = onTreeDrop;\n    treeModeRef.current = treeReorder;\n    depthsRef.current = depths;\n    dropTargetRef.current = dropTarget;\n    isGroupAtRef.current = isGroupAt;\n  });\n\n  // ── Mouse / shared start ──────────────────────────────────────────────────\n\n  /** Move the `draggable` attribute to `next` (or off entirely for `null`). */\n  function arm(next: string | number | null) {\n    // Arming exists purely to place the native `draggable` attribute, which is\n    // never set on a coarse pointer (touch uses the handlers below instead). A\n    // tap still synthesises mouseover/mousedown, so without this a touch device\n    // would re-render the list on every handle tap to no effect whatsoever.\n    if (isCoarsePointer) return;\n    if (armedKeyRef.current === next) return; // mouseover fires constantly — no-op\n    armedKeyRef.current = next;\n    setArmedKeyState(next);\n  }\n\n  // Arm while the pointer rests on this row's handle, disarm as it moves off.\n  // `buttons !== 0` means a press is already in progress: the pointer can drift\n  // off the handle before the browser reaches its drag threshold, so hover is\n  // ignored until release rather than disarming a drag that is about to start.\n  function handleRowMouseOver(key: string | number, e: React.MouseEvent<HTMLDivElement>) {\n    if (e.buttons !== 0) return;\n    arm((e.target as HTMLElement).closest('[data-drag-handle]') ? key : null);\n  }\n\n  function handleRowMouseLeave(e: React.MouseEvent<HTMLDivElement>) {\n    if (e.buttons !== 0) return;\n    arm(null);\n  }\n\n  // With `dragHandle`, remember on mousedown whether the press landed on a\n  // handle, so the dragstart that follows can be ignored if it didn't. Arming\n  // here too covers a handle that came to sit under an already-stationary cursor\n  // (the list scrolled), which produces no mouseover.\n  function handleRowMouseDown(key: string | number, e: React.MouseEvent<HTMLDivElement>) {\n    if (e.button !== 0) return; // only a primary press can begin a drag\n    const fromHandle = !!(e.target as HTMLElement).closest('[data-drag-handle]');\n    dragFromHandleRef.current = fromHandle;\n    if (fromHandle) arm(key);\n  }\n\n  function handleReorderStart(key: string | number, e: React.DragEvent<HTMLDivElement>) {\n    if (dragHandle && !dragFromHandleRef.current) {\n      e.preventDefault(); // not started from a handle — let the row stay put\n      return;\n    }\n    dragKeyRef.current = key;\n    setDraggingKey(key);\n    if (treeReorder) setDropTarget(null);\n  }\n\n  // ── Tree mode ─────────────────────────────────────────────────────────────\n\n  /** Resolve the tree drop target for the row at `overIndex` under pointer `y`. */\n  function computeTreeDrop(overIndex: number, y: number, rect: DOMRect): TreeDropTarget | null {\n    const dragKey = dragKeyRef.current;\n    if (dragKey == null) return null;\n    const fromIndex = keys.indexOf(dragKey);\n    // Forbid dropping onto the dragged node or anything in its own subtree.\n    if (fromIndex !== -1 && overIndex >= fromIndex && overIndex <= lastDescendantIndex(fromIndex, depths)) {\n      return null;\n    }\n    const position = resolveTreeDropTarget(y, rect.top, rect.height, isGroupAt(overIndex));\n    return { key: keys[overIndex], position };\n  }\n\n  function handleTreeDragOver(overIndex: number, e: React.DragEvent<HTMLDivElement>) {\n    e.preventDefault(); // allow the drop\n    const rect = e.currentTarget.getBoundingClientRect();\n    const next = computeTreeDrop(overIndex, e.clientY, rect);\n    setDropTarget((prev) => (prev?.key === next?.key && prev?.position === next?.position ? prev : next));\n  }\n\n  function commitTreeDrop() {\n    const dragKey = dragKeyRef.current;\n    const target = dropTargetRef.current;\n    if (dragKey != null && target && target.key !== dragKey) onTreeDrop?.(dragKey, target);\n  }\n\n  function handleTreeDrop(e: React.DragEvent<HTMLDivElement>) {\n    e.preventDefault();\n    commitTreeDrop();\n    setDropTarget(null);\n    dragKeyRef.current = null;\n    setDraggingKey(null);\n    arm(null);\n  }\n\n  function handleTreeDragEnd() {\n    // Fires after `drop` (or on its own when released off a valid target).\n    setDropTarget(null);\n    dragKeyRef.current = null;\n    setDraggingKey(null);\n    arm(null);\n  }\n\n  // ── Flat mode ─────────────────────────────────────────────────────────────\n\n  function handleReorderOver(overIndex: number, e: React.DragEvent<HTMLDivElement>) {\n    e.preventDefault(); // allow the drop\n    const dragKey = dragKeyRef.current;\n    if (dragKey == null) return;\n    const fromIndex = keys.indexOf(dragKey);\n    // Midpoint hysteresis (see resolveReorderTarget): a move only happens once\n    // the pointer crosses the centre of the row it's over. After a swap the\n    // dragged row sits under the cursor, so moving again needs real pointer\n    // travel — this stops the oscillation that made dragging feel jumpy.\n    const rect = e.currentTarget.getBoundingClientRect();\n    const target = resolveReorderTarget(fromIndex, overIndex, e.clientY, rect.top, rect.height);\n    if (target != null) onReorder?.(fromIndex, target);\n  }\n\n  function handleReorderEnd() {\n    dragKeyRef.current = null;\n    setDraggingKey(null);\n    arm(null);\n  }\n\n  // ── Touch reordering (long-press to arm, then drag) ──────────────────────────\n  // These handlers run from window listeners during a touch drag, so they read\n  // mutable state from refs and are stable across renders.\n\n  const reorderAtPoint = useCallback(\n    (x: number) => {\n      const dragKey = dragKeyRef.current;\n      const root = rootRef.current;\n      if (dragKey == null || !root) return;\n      // Reorder against the centre of the lifted clone, not the raw finger — the\n      // finger sits below the clone's top by the grab offset, so using it would\n      // make the top (and bottom) rows unreachable. The clone is what the user\n      // aims with, so match it.\n      const y = dragCloneRef.current\n        ? pointerYRef.current - grabOffsetYRef.current + dragHeightRef.current / 2\n        : pointerYRef.current;\n      const el = document.elementFromPoint(x, y) as HTMLElement | null;\n      const rowEl = el && root.contains(el) ? el.closest<HTMLElement>('[data-index]') : null;\n      if (!rowEl) return;\n      const overIndex = Number(rowEl.dataset.index);\n      if (Number.isNaN(overIndex)) return;\n      const fromIndex = keysRef.current.indexOf(dragKey);\n      const rect = rowEl.getBoundingClientRect();\n      if (treeModeRef.current) {\n        // Tree mode: don't move live — just track where the drop will land.\n        const ks = keysRef.current;\n        const ds = depthsRef.current;\n        if (fromIndex !== -1 && overIndex >= fromIndex && overIndex <= lastDescendantIndex(fromIndex, ds)) {\n          setDropTarget(null);\n          return;\n        }\n        const position = resolveTreeDropTarget(y, rect.top, rect.height, isGroupAtRef.current(overIndex));\n        const next: TreeDropTarget = { key: ks[overIndex], position };\n        setDropTarget((prev) => (prev?.key === next.key && prev?.position === next.position ? prev : next));\n        return;\n      }\n      const target = resolveReorderTarget(fromIndex, overIndex, y, rect.top, rect.height);\n      if (target != null) onReorderRef.current?.(fromIndex, target);\n    },\n    [rootRef, keysRef],\n  );\n\n  // Keep the floating clone glued under the finger (same grab point).\n  const positionDragClone = useCallback(() => {\n    const clone = dragCloneRef.current;\n    if (!clone) return;\n    clone.style.transform = `translate(${cloneLeftRef.current}px, ${pointerYRef.current - grabOffsetYRef.current}px)`;\n  }, []);\n\n  // While the finger sits near a top/bottom edge (and within the list's width),\n  // scroll the list and keep reordering against the rows now passing under it.\n  const autoScrollTick = useCallback(() => {\n    const rect = touchActiveRef.current ? getVisibleRect() : null;\n    if (!rect) {\n      autoScrollRef.current = null;\n      return;\n    }\n    const x = pointerXRef.current;\n    const y = pointerYRef.current;\n    const EDGE = 48;\n    let dy = 0;\n    // Only auto-scroll while the finger is actually over the list — moving it\n    // outside (any direction) stops the scroll instead of running away; pinning\n    // the dragged row keeps touch events alive so it resumes when you come back.\n    const inside = x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom;\n    if (inside) {\n      if (y < rect.top + EDGE) dy = -Math.min(18, Math.ceil((rect.top + EDGE - y) / 3));\n      else if (y > rect.bottom - EDGE) dy = Math.min(18, Math.ceil((y - (rect.bottom - EDGE)) / 3));\n    }\n    if (dy !== 0) {\n      scrollBy(dy);\n      reorderAtPoint(x);\n    }\n    autoScrollRef.current = requestAnimationFrame(autoScrollTick);\n  }, [reorderAtPoint, getVisibleRect, scrollBy]);\n\n  const endTouchDrag = useCallback(() => {\n    if (longPressRef.current != null) {\n      clearTimeout(longPressRef.current);\n      longPressRef.current = null;\n    }\n    window.removeEventListener('touchmove', handleTouchMove);\n    window.removeEventListener('touchend', endTouchDrag);\n    window.removeEventListener('touchcancel', endTouchDrag);\n    if (autoScrollRef.current != null) {\n      cancelAnimationFrame(autoScrollRef.current);\n      autoScrollRef.current = null;\n    }\n    if (dragCloneRef.current) {\n      dragCloneRef.current.remove();\n      dragCloneRef.current = null;\n    }\n    // Restore native scrolling that was locked for the duration of the drag.\n    document.body.style.touchAction = '';\n    document.body.style.overscrollBehavior = '';\n    if (touchActiveRef.current) {\n      // A real drag happened — swallow the click the browser fires afterwards\n      // so it doesn't, e.g., toggle row selection.\n      suppressNextClickRef.current = true;\n      setTimeout(() => {\n        suppressNextClickRef.current = false;\n      }, 350);\n      // Tree mode commits the pending drop on release (flat mode already moved\n      // rows live during the drag, so there's nothing to commit here).\n      if (treeModeRef.current) {\n        const dk = dragKeyRef.current;\n        const target = dropTargetRef.current;\n        if (dk != null && target && target.key !== dk) onTreeDropRef.current?.(dk, target);\n      }\n    }\n    setDropTarget(null);\n    touchActiveRef.current = false;\n    touchStartYRef.current = null;\n    dragKeyRef.current = null;\n    setDraggingKey(null);\n    // handleTouchMove and endTouchDrag remove each other’s listeners, so neither can list\n    // the other without a cycle.\n    // oxlint-disable-next-line react-hooks/exhaustive-deps\n  }, []);\n\n  const handleTouchMove = useCallback(\n    (e: TouchEvent) => {\n      const t = e.touches[0];\n      if (!t) return;\n      pointerXRef.current = t.clientX;\n      pointerYRef.current = t.clientY;\n      if (!touchActiveRef.current) {\n        // Before the long-press fires: if the finger travels, the user means to\n        // scroll, so disarm and let the browser handle it (we don't preventDefault).\n        const sy = touchStartYRef.current;\n        if (sy != null && Math.abs(t.clientY - sy) > 10 && longPressRef.current != null) {\n          clearTimeout(longPressRef.current);\n          longPressRef.current = null;\n        }\n        return;\n      }\n      // Non-passive listener: preventDefault cancels the native scroll for this\n      // gesture — the only reliable way once touch-action has been latched at\n      // touchstart. This is what makes the drag actually move on touch devices.\n      if (e.cancelable) e.preventDefault();\n      positionDragClone();\n      reorderAtPoint(t.clientX);\n    },\n    [reorderAtPoint, positionDragClone],\n  );\n\n  function handleRowTouchStart(key: string | number, e: React.TouchEvent<HTMLDivElement>) {\n    if (!reorderable && !treeReorder) return;\n    // With a drag handle, only a press that starts on it arms the drag, so the\n    // rest of the row stays free for tapping/typing into inputs.\n    if (dragHandle && !(e.target as HTMLElement).closest('[data-drag-handle]')) return;\n    if (longPressRef.current != null || touchActiveRef.current) return; // ignore extra fingers\n    const t = e.touches[0];\n    if (!t) return;\n    touchStartYRef.current = t.clientY;\n    pointerXRef.current = t.clientX;\n    pointerYRef.current = t.clientY;\n    window.addEventListener('touchmove', handleTouchMove, { passive: false });\n    window.addEventListener('touchend', endTouchDrag);\n    window.addEventListener('touchcancel', endTouchDrag);\n    longPressRef.current = window.setTimeout(() => {\n      longPressRef.current = null;\n      touchActiveRef.current = true;\n      dragKeyRef.current = key;\n      setDraggingKey(key);\n      navigator.vibrate?.(10);\n      // Lock native scrolling for the whole gesture — relying on per-move\n      // preventDefault isn't enough once the finger leaves and re-enters the\n      // list (the browser commits to a scroll and ignores it). We do all the\n      // scrolling ourselves via autoScrollTick.\n      document.body.style.touchAction = 'none';\n      document.body.style.overscrollBehavior = 'none';\n      createDragClone(key);\n      if (autoScrollRef.current == null) autoScrollRef.current = requestAnimationFrame(autoScrollTick);\n    }, longPressDelay);\n  }\n\n  // Build a floating copy of the picked-up row that tracks the finger, so the\n  // drag has a visible \"lifted\" element (the original row stays dimmed in place\n  // as a drop placeholder).\n  function createDragClone(key: string | number) {\n    const root = rootRef.current;\n    if (!root) return;\n    const idx = keysRef.current.indexOf(key);\n    const src = root.querySelector<HTMLElement>(`[data-index=\"${idx}\"]`);\n    if (!src) return;\n    const r = src.getBoundingClientRect();\n    grabOffsetYRef.current = pointerYRef.current - r.top;\n    cloneLeftRef.current = r.left;\n    dragHeightRef.current = r.height;\n    const clone = src.cloneNode(true) as HTMLElement;\n    clone.removeAttribute('data-index');\n    clone.setAttribute('data-drag-clone', 'true');\n    clone.setAttribute('aria-hidden', 'true');\n    clone.classList.add(styles.virtualListDragClone);\n    Object.assign(clone.style, {\n      position: 'fixed',\n      left: '0',\n      top: '0',\n      width: `${r.width}px`,\n      height: `${r.height}px`,\n      margin: '0',\n      opacity: '1',\n      pointerEvents: 'none',\n      // Above the sticky header (z-index 2) and any focused row (z-index 1).\n      zIndex: '3',\n      background: 'var(--background)',\n      boxShadow: 'var(--card-shadow)',\n      borderRadius: 'var(--default-border-radius)',\n    } as Partial<CSSStyleDeclaration>);\n    // Appended inside the list so it inherits the active theme's CSS variables;\n    // position:fixed keeps it in viewport coordinates regardless.\n    root.appendChild(clone);\n    dragCloneRef.current = clone;\n    positionDragClone();\n  }\n\n  // Tear down any in-flight touch drag if the component unmounts mid-gesture.\n  useEffect(() => endTouchDrag, [endTouchDrag]);\n\n  // Either drag mode arms the same row gestures; the handlers above branch on\n  // `treeReorder` for the differing target resolution / commit behaviour.\n  const dragEnabled = reorderable || treeReorder;\n  // Native mouse DnD is in play for this list. Row-level fallbacks that must\n  // cover *every* row — swallowing the browser's own drop and context menu —\n  // key off this, not off `draggable`, which in `dragHandle` mode is only ever\n  // on the single armed row.\n  const mouseDragEnabled = dragEnabled && !isCoarsePointer;\n\n  function getRowDragProps(index: number, key: string | number): RowDragProps {\n    // The drop indicator only renders on the row currently targeted. `before`/\n    // `after` lines indent to the target's depth so they sit under where the\n    // dropped content will start; `inside` highlights the whole row instead.\n    const rowDrop = treeReorder && dropTarget?.key === key ? dropTarget.position : undefined;\n    return {\n      // Drives the row-wide grab cursor. With `dragHandle`, only the handle\n      // initiates a drag, so the cursor belongs on the handle (styled by the\n      // consumer), not across the whole row.\n      reorderable: dragEnabled && !dragHandle,\n      mouseDragEnabled,\n      // In `dragHandle` mode the attribute only goes on the row the pointer has\n      // armed, so rows stay text-selectable in Firefox the rest of the time.\n      draggable: (mouseDragEnabled && (!dragHandle || key === armedKey)) || undefined,\n      isDragging: dragEnabled && key === draggingKey,\n      onMouseDown: dragEnabled && dragHandle ? (e) => handleRowMouseDown(key, e) : undefined,\n      onMouseOver: dragEnabled && dragHandle ? (e) => handleRowMouseOver(key, e) : undefined,\n      onMouseLeave: dragEnabled && dragHandle ? handleRowMouseLeave : undefined,\n      onDragStart: dragEnabled ? (e) => handleReorderStart(key, e) : undefined,\n      onDragOver: treeReorder\n        ? (e) => handleTreeDragOver(index, e)\n        : reorderable\n          ? (e) => handleReorderOver(index, e)\n          : undefined,\n      onDragEnd: treeReorder ? handleTreeDragEnd : reorderable ? handleReorderEnd : undefined,\n      onDrop: treeReorder ? handleTreeDrop : undefined,\n      onTouchStart: dragEnabled ? (e) => handleRowTouchStart(key, e) : undefined,\n      dropPosition: rowDrop,\n      dropIndentPx: rowDrop ? (depths[index] ?? 0) * treeIndentPx : undefined,\n    };\n  }\n\n  function consumeSuppressedClick(): boolean {\n    if (suppressNextClickRef.current) {\n      suppressNextClickRef.current = false;\n      return true;\n    }\n    return false;\n  }\n\n  return { draggingKey, dragEnabled, getRowDragProps, consumeSuppressedClick };\n}\n","import { useMemo, useState } from 'react';\n\nimport { nextSort, sortItems } from './virtualList.utils';\nimport type { VirtualListColumn, VirtualListSort } from './virtualList.types';\n\ninterface UseSortingArgs<T> {\n  items: T[];\n  columns?: VirtualListColumn<T>[];\n  /** Controlled sort. When provided, internal state is ignored. */\n  sort?: VirtualListSort | null;\n  defaultSort?: VirtualListSort | null;\n  onSortChange?: (sort: VirtualListSort | null) => void;\n  /** For comparing strings. */\n  locale?: string;\n}\n\ninterface UseSortingResult<T> {\n  /** `items`, reordered when the sorted column carries a comparator. */\n  rows: T[];\n  effectiveSort: VirtualListSort | null;\n  /** True only when this hook reordered the rows, not when it merely reported. */\n  isSorted: boolean;\n  handleHeaderSort: (key: string) => void;\n}\n\n/**\n * Header sorting, controlled or uncontrolled. Whether the list reorders the data\n * or only reports the click is the column's decision, not a prop: a column with\n * `sortValue`/`compare` is sorted here, a bare `sortable` one is left alone so a\n * server-side sort can own it. Sorting a paginated list here would silently\n * order only the rows loaded so far.\n */\nexport function useSorting<T>({\n  items,\n  columns,\n  sort,\n  defaultSort = null,\n  onSortChange,\n  locale,\n}: UseSortingArgs<T>): UseSortingResult<T> {\n  const isControlled = sort !== undefined;\n  const [internalSort, setInternalSort] = useState<VirtualListSort | null>(defaultSort);\n  const effectiveSort = isControlled ? sort! : internalSort;\n\n  // Returns `items` itself when nothing applies, which is what `isSorted` reads.\n  const rows = useMemo(() => sortItems(items, effectiveSort, columns, locale), [items, effectiveSort, columns, locale]);\n\n  function handleHeaderSort(key: string) {\n    const next = nextSort(effectiveSort, key);\n    if (!isControlled) setInternalSort(next);\n    onSortChange?.(next);\n  }\n\n  return { rows, effectiveSort, isSorted: rows !== items, handleHeaderSort };\n}\n","import React, { useCallback, useLayoutEffect, useRef, useState } from 'react';\n\nimport {\n  columnCellSelector,\n  columnWidthVar,\n  loadPersistedColumns,\n  resolveFrozenWidth,\n  MIN_COLUMN_WIDTH,\n} from './virtualList.utils';\nimport type { VirtualListColumn } from './virtualList.types';\n\ninterface UseColumnResizeArgs<T> {\n  columns?: VirtualListColumn<T>[];\n  /**\n   * The columns currently rendered, in order. A ref rather than a value because\n   * the visible set is derived one hook later, and only the freeze reads it, at\n   * event time.\n   */\n  visibleColumnsRef: React.RefObject<VirtualListColumn<T>[]>;\n  resizableColumns: boolean;\n  /** Controlled widths. When provided, internal state is ignored. */\n  columnWidths?: Record<string, number>;\n  onColumnWidthsChange?: (widths: Record<string, number>) => void;\n  /** Restores widths saved under this key. Uncontrolled mode only. */\n  persistColumnsKey?: string;\n  rootRef: React.RefObject<HTMLDivElement | null>;\n}\n\nexport interface UseColumnResizeResult<T> {\n  widths: Record<string, number>;\n  /**\n   * True once every visible column has an exact px width. Nothing moves at that\n   * moment, but from then on the layout is pixels rather than `fr`/`fit`.\n   */\n  isFrozen: boolean;\n  isResizing: boolean;\n  /** Drops every user width and hands the layout back to the column definitions. */\n  reset: () => void;\n  /** Whether this column takes a resize at all, by handle or by keyboard. */\n  isResizable: (col: VirtualListColumn<T>) => boolean;\n  /** Widens (or, negative, narrows) one column. The keyboard entry point. */\n  resizeBy: (key: string, delta: number) => void;\n  /** Takes one column down to its minimum. */\n  resizeToMin: (key: string) => void;\n  /** Sizes one column to its widest rendered cell. */\n  autoFit: (key: string) => void;\n  /** Pointer props for one column's handle, or null when that column can't be resized. */\n  getHandleProps: (col: VirtualListColumn<T>) => React.HTMLAttributes<HTMLDivElement> | null;\n}\n\nfunction minWidthFor<T>(col: VirtualListColumn<T> | undefined): number {\n  return Math.max(1, col?.minWidth ?? MIN_COLUMN_WIDTH);\n}\n\n/**\n * User column resizing: the width state, and a drag that doesn't go through\n * React.\n *\n * A pointer move writes the dragged column's custom property straight onto the\n * list element. The header and every row read that same property, so one DOM\n * write resizes all of them in a single layout pass. Going through state\n * instead would re-render the header plus every visible row on every frame, and\n * the handle would trail the cursor by one frame, which is the whole difference\n * between this feeling attached to the pointer and feeling cheap. React is told\n * once, on release.\n *\n * The first resize **freezes** every visible column to its measured width. A\n * `fr` track can't express \"this one column is now 240px\" without the others\n * silently absorbing the difference, and once they hit their minimum the dragged\n * edge stops following the cursor, which is the worst moment for it to happen.\n * Frozen, the table is exact pixels and simply scrolls horizontally instead.\n */\nexport function useColumnResize<T>({\n  columns,\n  visibleColumnsRef,\n  resizableColumns,\n  columnWidths,\n  onColumnWidthsChange,\n  persistColumnsKey,\n  rootRef,\n}: UseColumnResizeArgs<T>): UseColumnResizeResult<T> {\n  const isControlled = columnWidths !== undefined;\n  const [internalWidths, setInternalWidths] = useState<Record<string, number>>(\n    () => loadPersistedColumns(persistColumnsKey)?.widths ?? {},\n  );\n  const [isResizing, setIsResizing] = useState(false);\n  const effectiveWidths = isControlled ? columnWidths! : internalWidths;\n\n  // Read by the pointer handlers, which are installed once per drag and must not\n  // capture a stale snapshot of either.\n  const widthsRef = useRef(effectiveWidths);\n  widthsRef.current = effectiveWidths;\n\n  const commit = useCallback(\n    (next: Record<string, number>) => {\n      if (!isControlled) setInternalWidths(next);\n      onColumnWidthsChange?.(next);\n    },\n    [isControlled, onColumnWidthsChange],\n  );\n\n  /**\n   * Current width of every visible column, measured from the rendered header so\n   * the freeze is invisible: the px values are the ones already on screen.\n   * Columns that already have a width keep it, and a header cell that can't be\n   * measured (jsdom, a display:none list) falls back to its declared width.\n   */\n  const freeze = useCallback((): Record<string, number> => {\n    const root = rootRef.current;\n    const next: Record<string, number> = { ...widthsRef.current };\n    for (const col of visibleColumnsRef.current) {\n      if (next[col.key] != null) continue;\n      const cell = root?.querySelector(`[aria-rowindex=\"1\"] ${columnCellSelector(col.key)}`);\n      const measured = cell ? Math.round(cell.getBoundingClientRect().width) : 0;\n      next[col.key] = measured > 0 ? measured : resolveFrozenWidth(col);\n    }\n    return next;\n  }, [rootRef, visibleColumnsRef]);\n\n  // The widths live on the element as custom properties, written from here and\n  // from the drag, never through React's `style`. One owner means a drag's\n  // imperative writes can't be clobbered by a render that happens to land\n  // mid-gesture, and the steady state still follows the committed widths.\n  useLayoutEffect(() => {\n    const root = rootRef.current;\n    if (!root) return;\n    for (const [key, width] of Object.entries(effectiveWidths))\n      root.style.setProperty(columnWidthVar(key), `${width}px`);\n  }, [effectiveWidths, rootRef]);\n\n  const applyWidth = useCallback(\n    (key: string, width: number) => {\n      rootRef.current?.style.setProperty(columnWidthVar(key), `${width}px`);\n    },\n    [rootRef],\n  );\n\n  /**\n   * In controlled mode the drag has already written widths the prop may not\n   * agree with. A frame later either the consumer has accepted them (the ref\n   * holds the new values and this writes them again, a no-op) or it hasn't, and\n   * the columns snap back to what the prop says. Without it a rejected width\n   * would stay on screen, since nothing re-rendered to correct it.\n   */\n  const syncControlled = useCallback(() => {\n    if (!isControlled) return;\n    requestAnimationFrame(() => {\n      for (const [key, width] of Object.entries(widthsRef.current)) applyWidth(key, width);\n    });\n  }, [applyWidth, isControlled]);\n\n  const resize = useCallback(\n    (key: string, toWidth: (from: number) => number) => {\n      const base = freeze();\n      const min = minWidthFor(columns?.find((c) => c.key === key));\n      const next = { ...base, [key]: Math.max(min, Math.round(toWidth(base[key]))) };\n      applyWidth(key, next[key]);\n      commit(next);\n      syncControlled();\n    },\n    [applyWidth, columns, commit, freeze, syncControlled],\n  );\n\n  const beginDrag = useCallback(\n    (event: React.PointerEvent<HTMLDivElement>, key: string) => {\n      if (event.button !== 0) return;\n      // The handle sits on top of the header's sort button; without this the\n      // drag would also count as a click on it.\n      event.preventDefault();\n      event.stopPropagation();\n\n      const handle = event.currentTarget;\n      const base = freeze();\n      const startWidth = base[key];\n      const startX = event.clientX;\n      const min = minWidthFor(columns?.find((c) => c.key === key));\n\n      let width = startWidth;\n      let frame = 0;\n      const paint = () => {\n        frame = 0;\n        applyWidth(key, width);\n      };\n\n      function finish(commitWidth: number | null) {\n        if (frame) cancelAnimationFrame(frame);\n        handle.releasePointerCapture?.(event.pointerId);\n        handle.removeEventListener('pointermove', onMove);\n        handle.removeEventListener('pointerup', onUp);\n        handle.removeEventListener('pointercancel', onCancel);\n        handle.removeEventListener('lostpointercapture', onCancel);\n        window.removeEventListener('keydown', onKeyDown, true);\n        document.body.style.cursor = '';\n        document.body.style.userSelect = '';\n        setIsResizing(false);\n        if (commitWidth == null) {\n          // The freeze was committed at pointerdown and stays: on its own it\n          // changes nothing the user can see. Only the drag is undone.\n          applyWidth(key, startWidth);\n          return;\n        }\n        if (commitWidth !== startWidth) commit({ ...base, [key]: commitWidth });\n        syncControlled();\n      }\n\n      function onMove(moveEvent: PointerEvent) {\n        // Always from the values captured at pointerdown, never from the\n        // element's current geometry: re-measuring mid-drag lets rounding\n        // accumulate and the handle slides away from the cursor.\n        width = Math.max(min, Math.round(startWidth + moveEvent.clientX - startX));\n        if (!frame) frame = requestAnimationFrame(paint);\n      }\n      function onUp() {\n        finish(width);\n      }\n      function onCancel() {\n        finish(null);\n      }\n      function onKeyDown(keyEvent: KeyboardEvent) {\n        if (keyEvent.key !== 'Escape') return;\n        keyEvent.preventDefault();\n        finish(null);\n      }\n\n      handle.setPointerCapture?.(event.pointerId);\n      handle.addEventListener('pointermove', onMove);\n      handle.addEventListener('pointerup', onUp);\n      handle.addEventListener('pointercancel', onCancel);\n      handle.addEventListener('lostpointercapture', onCancel);\n      window.addEventListener('keydown', onKeyDown, true);\n      // On the body, not on the handle: the pointer leaves the handle as soon as\n      // the drag starts, and the cursor must not flicker back to the default.\n      document.body.style.cursor = 'col-resize';\n      document.body.style.userSelect = 'none';\n      setIsResizing(true);\n      // Committing the freeze up front, rather than on release: until React has\n      // rendered the frozen template, the tracks are still `fr`/`fit` and read\n      // no custom property, so the drag would move nothing at all.\n      if (Object.keys(widthsRef.current).length === 0) commit(base);\n    },\n    [applyWidth, columns, commit, freeze, syncControlled],\n  );\n\n  /**\n   * Width the column would need for its widest cell, measured by sizing its\n   * track to `max-content` for one synchronous layout.\n   *\n   * Reading `scrollWidth` off the cells instead reports the *min*-content width:\n   * a body cell is free to wrap, so long text never overflows it, it only makes\n   * the row taller, and the widest thing that can't wrap is a single word.\n   * `max-content` is the width with no wrapping at all, which is what fitting a\n   * column means. The header and every row are separate grids, so each sizes its\n   * own track independently and all of them have to be read: the widest is the\n   * one width that fits the whole column.\n   */\n  const measureAutoFit = useCallback(\n    (key: string): number => {\n      const root = rootRef.current;\n      if (!root) return 0;\n      const widthProperty = columnWidthVar(key);\n      const previousWidth = root.style.getPropertyValue(widthProperty);\n      // `--vl-total` is a calc() over these same properties, so it turns invalid\n      // while one of them holds a keyword, and the header and body would be left\n      // with an unresolvable width mid-measurement.\n      const previousTotal = root.style.getPropertyValue('--vl-total');\n\n      root.style.setProperty(widthProperty, 'max-content');\n      root.style.removeProperty('--vl-total');\n      let measured = 0;\n      root.querySelectorAll(columnCellSelector(key)).forEach((cell) => {\n        const width = Math.ceil(cell.getBoundingClientRect().width);\n        if (width > measured) measured = width;\n      });\n      root.style.setProperty(widthProperty, previousWidth);\n      if (previousTotal) root.style.setProperty('--vl-total', previousTotal);\n\n      // Only the rendered rows are measurable, so this fits what is on screen.\n      return Math.min(measured, root.clientWidth);\n    },\n    [rootRef],\n  );\n\n  const autoFit = useCallback(\n    (key: string) => {\n      const run = () => {\n        const measured = measureAutoFit(key);\n        if (measured > 0) resize(key, () => measured);\n      };\n      // Until the frozen template has rendered, the tracks read no custom\n      // property and sizing one to `max-content` measures nothing. A drag has\n      // already frozen by the time its double-click lands; a keyboard `End` on\n      // an untouched list has not, so it waits for the render it just asked for.\n      if (Object.keys(widthsRef.current).length > 0) run();\n      else {\n        commit(freeze());\n        requestAnimationFrame(run);\n      }\n    },\n    [commit, freeze, measureAutoFit, resize],\n  );\n\n  const reset = useCallback(() => {\n    const root = rootRef.current;\n    // The layout effect only ever writes the widths it has, so the properties of\n    // the ones being dropped have to be cleared here or they would linger and\n    // reappear with the next freeze.\n    if (root) for (const key of Object.keys(widthsRef.current)) root.style.removeProperty(columnWidthVar(key));\n    commit({});\n  }, [commit, rootRef]);\n\n  const isResizable = useCallback(\n    (col: VirtualListColumn<T>) => resizableColumns && col.resizable !== false,\n    [resizableColumns],\n  );\n\n  const resizeBy = useCallback((key: string, delta: number) => resize(key, (w) => w + delta), [resize]);\n\n  const resizeToMin = useCallback(\n    (key: string) => resize(key, () => minWidthFor(columns?.find((c) => c.key === key))),\n    [columns, resize],\n  );\n\n  const getHandleProps = useCallback(\n    (col: VirtualListColumn<T>): React.HTMLAttributes<HTMLDivElement> | null => {\n      if (!isResizable(col)) return null;\n      return {\n        // Deliberately not focusable, and hidden from assistive technology: the\n        // keyboard path is Alt plus the arrow keys on the header cell itself, so\n        // a second tab stop per column would buy nothing and cost a lot.\n        'aria-hidden': true,\n        onPointerDown: (event: React.PointerEvent<HTMLDivElement>) => beginDrag(event, col.key),\n        onDoubleClick: (event: React.MouseEvent) => {\n          event.stopPropagation();\n          autoFit(col.key);\n        },\n        onClick: (event: React.MouseEvent) => event.stopPropagation(),\n      };\n    },\n    [autoFit, beginDrag, isResizable],\n  );\n\n  return {\n    widths: effectiveWidths,\n    isFrozen: Object.keys(effectiveWidths).length > 0,\n    isResizing,\n    reset,\n    isResizable,\n    resizeBy,\n    resizeToMin,\n    autoFit,\n    getHandleProps,\n  };\n}\n","import React, { useLayoutEffect, useRef, useState } from 'react';\n\nimport { findStartIndex, ROW_ENTER_SELECTOR, ROW_FOCUSABLE_SELECTOR } from './virtualList.utils';\nimport type { VirtualListScrollOptions } from './virtualList.types';\nimport type { VisibleSlice } from './useVirtualWindow';\n\ninterface UseRowNavigationArgs {\n  enabled: boolean;\n  tabAcrossRows: boolean;\n  rowCount: number;\n  /** Row tops, from the windowing engine, used for paging and visibility. */\n  offsets: number[];\n  rootRef: React.RefObject<HTMLDivElement | null>;\n  headerRef: React.RefObject<HTMLDivElement | null>;\n  /** Read live: a held arrow key moves the slice faster than the state commits. */\n  getVisibleSlice: () => VisibleSlice;\n  /** The whole scrollport, which is what one PageDown covers. */\n  viewportHeight: number;\n  scrollToIndex: (index: number, options?: VirtualListScrollOptions) => void;\n  /** Row index the active row starts on, normally the selected one. */\n  initialIndex: number;\n  multiSelect: boolean;\n  /** Undefined when the consumer gave the rows no activation of their own. */\n  onActivate?: (index: number) => void;\n  onToggleSelect: (index: number) => void;\n}\n\ninterface UseRowNavigationResult {\n  /** Index of the row the arrow keys are on, or -1 when navigation is off. */\n  activeIndex: number;\n  /** Keeps the arrow keys where the pointer last was. */\n  setActiveIndex: (index: number) => void;\n  /**\n   * The row currently being used, or -1. Its controls are in the tab order and\n   * it stays mounted even when it scrolls out of the window.\n   */\n  enteredIndex: number;\n  containerProps: {\n    tabIndex?: number;\n    onKeyDown?: (event: React.KeyboardEvent) => void;\n    onFocus?: (event: React.FocusEvent) => void;\n    onBlur?: (event: React.FocusEvent) => void;\n  };\n}\n\n/**\n * Arrow-key navigation over the rows, with the tab stop on the list itself.\n *\n * The stop cannot sit on the active row, the way a roving tabindex normally\n * would: a virtualized row unmounts when it scrolls out of the window, and the\n * list would be left with no tab stop at all (the same reason `Tree` puts its\n * stop on its container). Instead the list keeps the focus and points at the\n * active row with `aria-activedescendant`.\n *\n * Keys are only handled when the list element itself has the focus. A cell may\n * hold an input, a dropdown or a button, and every one of them wants the arrows,\n * Enter and Space for itself. `F2` steps into such a row and `Escape` steps back\n * out, which is the one pair of keys that does work from inside a cell.\n *\n * `tabAcrossRows` adds the second pair, Tab and Shift+Tab, which the browser\n * cannot carry across a row boundary itself: the next row may not be in the DOM\n * when it looks for the next stop.\n */\nexport function useRowNavigation({\n  enabled,\n  tabAcrossRows,\n  rowCount,\n  offsets,\n  rootRef,\n  headerRef,\n  getVisibleSlice,\n  viewportHeight,\n  scrollToIndex,\n  initialIndex,\n  multiSelect,\n  onActivate,\n  onToggleSelect,\n}: UseRowNavigationArgs): UseRowNavigationResult {\n  const [index, setIndex] = useState(initialIndex);\n  const [enteredIndex, setEnteredIndex] = useState(-1);\n  const offsetsRef = useRef(offsets);\n  offsetsRef.current = offsets;\n\n  // Rows come and go (a filter, a page, a reset), so the active index is clamped\n  // during render rather than corrected in an effect.\n  const activeIndex = enabled && rowCount > 0 ? Math.min(Math.max(index, 0), rowCount - 1) : -1;\n  if (activeIndex >= 0 && activeIndex !== index) setIndex(activeIndex);\n  const navigable = activeIndex >= 0;\n\n  /** Scrolls only when the row isn't already fully in view, so paging doesn't jump. */\n  function revealRow(target: number) {\n    const headerHeight = headerRef.current?.offsetHeight ?? 0;\n    const rowTop = offsetsRef.current[target] ?? 0;\n    const rowBottom = offsetsRef.current[target + 1] ?? rowTop;\n    const { top, height } = getVisibleSlice();\n    if (rowTop < top + headerHeight) scrollToIndex(target, { align: 'start' });\n    else if (rowBottom > top + height) scrollToIndex(target, { align: 'end' });\n  }\n\n  function moveTo(target: number) {\n    const next = Math.max(0, Math.min(target, rowCount - 1));\n    setIndex(next);\n    revealRow(next);\n  }\n\n  /** How many rows fit in the viewport below the sticky header, at least one. */\n  function pageSize(from: number): number {\n    const usable = Math.max(0, viewportHeight - (headerRef.current?.offsetHeight ?? 0));\n    const edge = findStartIndex(offsetsRef.current, (offsetsRef.current[from] ?? 0) + usable, rowCount);\n    return Math.max(1, edge - from);\n  }\n\n  function rowElement(target: number): HTMLElement | null {\n    return rootRef.current?.querySelector<HTMLElement>(`[data-index=\"${target}\"]`) ?? null;\n  }\n\n  /** Moves the focus into the row's first control. False when it has none. */\n  function enterRow(target: number): boolean {\n    const focusable = rowElement(target)?.querySelector<HTMLElement>(ROW_ENTER_SELECTOR);\n    if (!focusable) return false;\n    // The row's controls are still out of the tab order at this point, which is\n    // no obstacle to focusing one directly; the render that follows puts them\n    // back in, so Tab then moves along that row.\n    setEnteredIndex(target);\n    focusable.focus();\n    return true;\n  }\n\n  // A row reached by Tab may not be in the DOM yet: entering it is what pins it\n  // there, and that only takes effect on the next commit.\n  const pendingFocusRef = useRef<{ index: number; edge: 'first' | 'last' } | null>(null);\n  // Set when Tab ran out of rows to land in, so the next one is left to the browser.\n  const releaseTabRef = useRef(false);\n  // The chain of updates below is bounded: each pass clears the ref and moves one\n  // row closer to an end of the list.\n  // oxlint-disable-next-line react-hooks/exhaustive-deps\n  useLayoutEffect(() => {\n    const pending = pendingFocusRef.current;\n    if (!pending) return;\n    // Cleared whether or not the row was found: the commit that set this renders\n    // it, so a second attempt at the same row would be a later commit stealing\n    // the focus.\n    pendingFocusRef.current = null;\n    const focusables = rowElement(pending.index)?.querySelectorAll<HTMLElement>(ROW_ENTER_SELECTOR);\n    if (focusables?.length) {\n      (pending.edge === 'first' ? focusables[0] : focusables[focusables.length - 1]).focus();\n      return;\n    }\n    // The Tab that got here was already prevented, so a row with nothing to focus\n    // must not swallow it.\n    const next = pending.index + (pending.edge === 'first' ? 1 : -1);\n    if (next >= 0 && next < rowCount) {\n      pendingFocusRef.current = { index: next, edge: pending.edge };\n      setEnteredIndex(next);\n      revealRow(next);\n      return;\n    }\n    setEnteredIndex(-1);\n    // Backwards needs no release: Shift+Tab off the list's own stop already leaves.\n    releaseTabRef.current = navigable && pending.edge === 'first';\n    if (navigable) rootRef.current?.focus();\n  });\n\n  function rowIndexOf(node: EventTarget | null): number {\n    const row = (node as HTMLElement | null)?.closest?.<HTMLElement>('[data-index]');\n    const parsed = Number(row?.dataset.index);\n    return Number.isFinite(parsed) ? parsed : -1;\n  }\n\n  /** A click or a Tab landing in a row counts as entering it, same as F2. */\n  function handleFocus(event: React.FocusEvent) {\n    if (event.target === event.currentTarget) return;\n    const rowIndex = rowIndexOf(event.target);\n    if (rowIndex < 0) return;\n    setEnteredIndex(rowIndex);\n    setIndex(rowIndex);\n  }\n\n  /** Leaving the entered row puts its controls back out of the tab order. */\n  function handleBlur(event: React.FocusEvent) {\n    // A release only holds while the list keeps the focus. `contains` counts the\n    // list itself, which is where the release puts the focus in the first place.\n    if (!event.currentTarget.contains(event.relatedTarget)) releaseTabRef.current = false;\n    if (rowIndexOf(event.relatedTarget) === enteredIndex) return;\n    setEnteredIndex(-1);\n  }\n\n  /** Escape from inside a cell: hands the focus back to the list. */\n  function handleExitKey(event: React.KeyboardEvent): void {\n    // Something inside the row may own Escape, a dropdown closing for instance.\n    if (event.key !== 'Escape' || event.defaultPrevented || !navigable) return;\n    const rowIndex = rowIndexOf(event.target);\n    if (rowIndex >= 0) setIndex(rowIndex);\n    setEnteredIndex(-1);\n    rootRef.current?.focus();\n    event.preventDefault();\n  }\n\n  /**\n   * Only the row's last control (first, going back) is taken over: everywhere\n   * else the browser's order is already right, and at the ends of the list Tab\n   * is left alone so it can leave.\n   */\n  function handleTabKey(event: React.KeyboardEvent): void {\n    if (!tabAcrossRows || event.key !== 'Tab' || event.defaultPrevented) return;\n    if (event.altKey || event.ctrlKey || event.metaKey) return;\n    const rowIndex = rowIndexOf(event.target);\n    if (rowIndex < 0) return;\n    const focusables = rowElement(rowIndex)?.querySelectorAll<HTMLElement>(ROW_FOCUSABLE_SELECTOR);\n    if (!focusables?.length) return;\n    const at = Array.prototype.indexOf.call(focusables, event.target);\n    if (at < 0) return;\n    const back = event.shiftKey;\n    if (back ? at > 0 : at < focusables.length - 1) return;\n    const target = back ? rowIndex - 1 : rowIndex + 1;\n    if (target < 0 || target >= rowCount) return;\n    event.preventDefault();\n    // The active row follows the focus, through `handleFocus`, so that it stays\n    // where the user is when the focus never lands.\n    setEnteredIndex(target);\n    revealRow(target);\n    pendingFocusRef.current = { index: target, edge: back ? 'last' : 'first' };\n  }\n\n  function handleKeyDown(event: React.KeyboardEvent) {\n    // Focus inside a cell means the row's own content is being used.\n    if (event.target !== event.currentTarget) {\n      handleTabKey(event);\n      handleExitKey(event);\n      return;\n    }\n    // The consumer's own handler ran first and may have taken the key, which is\n    // what `handleTabKey` and `handleExitKey` already check for.\n    if (!navigable || event.defaultPrevented) return;\n    const releasing = releaseTabRef.current;\n    releaseTabRef.current = false;\n    switch (event.key) {\n      case 'ArrowDown':\n        moveTo(activeIndex + 1);\n        break;\n      case 'ArrowUp':\n        moveTo(activeIndex - 1);\n        break;\n      case 'Home':\n        moveTo(0);\n        break;\n      case 'End':\n        moveTo(rowCount - 1);\n        break;\n      case 'PageDown':\n        moveTo(activeIndex + pageSize(activeIndex));\n        break;\n      case 'PageUp':\n        moveTo(activeIndex - pageSize(Math.max(0, activeIndex - 1)));\n        break;\n      case 'Enter':\n        // With no activation of its own, a row's content is the only thing\n        // Enter could mean, so it steps in rather than doing nothing.\n        if (onActivate) onActivate(activeIndex);\n        else if (!enterRow(activeIndex)) return;\n        break;\n      case 'F2':\n        if (!enterRow(activeIndex)) return;\n        break;\n      case 'Tab':\n        // The list's own tab stop is in front of rows whose controls are all\n        // parked, so without this Tab would step over every one of them. Which is\n        // the point once `releasing` says Tab has run out of rows to land in.\n        if (!tabAcrossRows || event.shiftKey || releasing || !enterRow(activeIndex)) return;\n        break;\n      case ' ':\n        if (!multiSelect) return;\n        onToggleSelect(activeIndex);\n        break;\n      default:\n        return;\n    }\n    // Reached only by a handled key, so the page never scrolls under the list.\n    event.preventDefault();\n  }\n\n  if (!navigable && !tabAcrossRows)\n    return { activeIndex, enteredIndex: -1, setActiveIndex: setIndex, containerProps: {} };\n  const containerProps: UseRowNavigationResult['containerProps'] = {\n    onKeyDown: handleKeyDown,\n    onFocus: handleFocus,\n    onBlur: handleBlur,\n  };\n  // Without arrow navigation the list is not a composite widget and must not take\n  // a stop of its own. The key is left out rather than set to `undefined`, which\n  // would erase a `tabIndex` the consumer passed.\n  if (navigable) containerProps.tabIndex = 0;\n  return { activeIndex, enteredIndex, setActiveIndex: setIndex, containerProps };\n}\n","import React, { useMemo, useCallback, useEffect, useId, useImperativeHandle, useRef } from 'react';\nimport cx from 'classnames';\nimport styles from './virtualList.module.pcss';\nimport Loader from '../loader';\nimport Checkbox from '../checkbox';\nimport VirtualRow from './virtualRow';\nimport VirtualListHeader from './virtualListHeader';\nimport { useVirtualWindow } from './useVirtualWindow';\nimport { useSelection } from './useSelection';\nimport { useColumns } from './useColumns';\nimport { useRowDrag } from './useRowDrag';\nimport { useSorting } from './useSorting';\nimport { useColumnResize } from './useColumnResize';\nimport { useRowNavigation } from './useRowNavigation';\nimport { useLabels, useLocale } from '../configProvider';\nimport type { VirtualListColumn, VirtualListLabels, VirtualListProps } from './virtualList.types';\n\nconst DEFAULT_LABELS: Required<VirtualListLabels> = {\n  selectRow: 'Select row',\n  selectAllRows: 'Select all rows',\n  toggleColumns: 'Toggle column visibility',\n  columnsHidden: (count) => `${count} columns hidden`,\n  resetColumnWidths: 'Reset column widths',\n};\n\n// Pure helpers live in ./virtualList.utils; re-exported here so existing\n// imports (and unit tests) keep working against the component entry point.\nexport {\n  buildOffsets,\n  findStartIndex,\n  getVisibleRange,\n  resolveReorderTarget,\n  resolveTreeDropTarget,\n  lastDescendantIndex,\n  buildColumnTemplate,\n  compareSortValues,\n  isColumnSortable,\n  isEmptySortValue,\n  nextSort,\n  sortItems,\n} from './virtualList.utils';\n\n// ─── Orchestrator ─────────────────────────────────────────────────────────────\n// VirtualListInner wires the hooks together and renders the two pieces of\n// markup. Each concern lives in its own module:\n//   useSorting          — table mode: header sort state, and the sorted row order\n//   useVirtualWindow    — generic scroll / measurement / windowing (all modes)\n//   useColumnResize     — table mode: user column widths, and the drag that sets them\n//   useSelection        — multi-row selection\n//   useColumns          — table mode: columns, visibility, persistence, fit widths\n//   useRowDrag          — reorder + tree drag-and-drop (mouse + touch)\n//   useRowNavigation    — arrow-key navigation over the rows\n//   virtualListHeader   — the header row: sorting, resizing, the column menu\n//   virtualRow          — one positioned row, with its drag wiring\n//\n// The hook order is not free: useColumnResize decides whether the layout is\n// frozen, which useColumns needs to build the grid template.\n\n/** One handler after the other, or whichever of the two exists. */\nfunction chain<E>(theirs: ((event: E) => void) | undefined, ours: ((event: E) => void) | undefined) {\n  if (!theirs || !ours) return theirs ?? ours;\n  return (event: E) => {\n    theirs(event);\n    ours(event);\n  };\n}\n\nfunction VirtualList<T>(props: VirtualListProps<T>) {\n  const {\n    ref,\n    items,\n    renderRow,\n    ariaRoles,\n    columns,\n    height = '100%',\n    estimatedRowHeight = 40,\n    overscan = 3,\n    rowGap = 0,\n    rowPadding,\n    selectedKey,\n    getItemKey,\n    onRowClick,\n    keyboardNavigation = true,\n    tabAcrossRows = false,\n    scrollParent,\n    visibleColumnKeys,\n    onVisibleColumnsChange,\n    persistColumnsKey,\n    resizableColumns = false,\n    columnWidths,\n    onColumnWidthsChange,\n    labels,\n    sort,\n    defaultSort,\n    onSortChange,\n    showColumnToggle,\n    onLoadMore,\n    loadMoreThreshold = 100,\n    isLoading = false,\n    multiSelect = false,\n    selectedKeys,\n    onSelectionChange,\n    reorderable = false,\n    onReorder,\n    longPressDelay = 400,\n    treeReorder = false,\n    getItemDepth,\n    isGroup,\n    onTreeDrop,\n    treeIndentPx = 24,\n    dragHandle = false,\n    showDivider = true,\n    rowHover = true,\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    ...rest\n  } = props;\n  // Read off the raw props: the destructured value carries a default and so\n  // cannot tell a passed '100%' from an omitted one.\n  const heightIgnored = props.scrollParent != null && props.height !== undefined;\n  // StrictMode runs every effect twice on mount; a developer warning should appear once.\n  const warnedHeightRef = useRef(false);\n  useEffect(() => {\n    if (!heightIgnored || warnedHeightRef.current) return;\n    warnedHeightRef.current = true;\n    console.warn(\n      'VirtualList: `height` is ignored alongside `scrollParent`, which leaves the list no viewport to size.',\n    );\n  }, [heightIgnored]);\n\n  const text = useLabels('VirtualList', labels, DEFAULT_LABELS);\n  const locale = useLocale();\n  const { rows, effectiveSort, isSorted, handleHeaderSort } = useSorting<T>({\n    items,\n    columns,\n    sort,\n    defaultSort,\n    onSortChange,\n    locale,\n  });\n\n  const getKey = useCallback(\n    (item: T, index: number): string | number => (getItemKey ? getItemKey(item, index) : index),\n    [getItemKey],\n  );\n  const keys = useMemo(() => rows.map((item, i) => getKey(item, i)), [rows, getKey]);\n  const depths = useMemo(\n    () => (getItemDepth ? rows.map((item, i) => getItemDepth(item, i)) : []),\n    [rows, getItemDepth],\n  );\n  const isGroupAt = useCallback((i: number) => (isGroup ? isGroup(rows[i], i) : false), [isGroup, rows]);\n\n  const win = useVirtualWindow<T>({\n    items: rows,\n    keys,\n    getItemKey,\n    estimatedRowHeight,\n    rowGap,\n    overscan,\n    onLoadMore,\n    loadMoreThreshold,\n    isLoading,\n    scrollParent,\n  });\n  const { rootRef, headerRef, rowObserver, keysRef, offsets, totalHeight, startIndex, endIndex } = win;\n\n  // Filled from `cols` below: the resize hook only reads the visible set when a\n  // handle is actually used, which is what lets it run before the hook that\n  // derives it.\n  const visibleColumnsRef = useRef<VirtualListColumn<T>[]>([]);\n  const columnResize = useColumnResize<T>({\n    columns,\n    visibleColumnsRef,\n    resizableColumns,\n    columnWidths,\n    onColumnWidthsChange,\n    persistColumnsKey,\n    rootRef,\n  });\n\n  const selection = useSelection<T>({ items: rows, getKey, multiSelect, selectedKeys, onSelectionChange });\n  const { effectiveSelectedKeys } = selection;\n\n  const cols = useColumns<T>({\n    columns,\n    items: rows,\n    multiSelect,\n    visibleColumnKeys,\n    onVisibleColumnsChange,\n    persistColumnsKey,\n    showColumnToggle,\n    userWidths: columnResize.widths,\n    isFrozen: columnResize.isFrozen,\n    rootRef,\n    startIndex,\n    endIndex,\n    containerHeight: win.containerHeight,\n    containerWidth: win.containerWidth,\n    isLoading,\n  });\n  // The rest of `cols` is the header's business and goes there whole.\n  const { hasColumns, visibleColumns, columnTemplate, widthVars, shouldShowToggle, recalculateColumns } = cols;\n  visibleColumnsRef.current = visibleColumns;\n\n  // Compose the forwarded handle from the hooks that own each piece: scroll\n  // methods from the windowing engine, column controls from the column hooks.\n  useImperativeHandle(ref, () => ({ ...win.scrollApi, recalculateColumns, resetColumnWidths: columnResize.reset }), [\n    win.scrollApi,\n    recalculateColumns,\n    columnResize.reset,\n  ]);\n\n  // Dragging a row to position N is meaningless while the list decides the\n  // order: the drop would be undone by the next sort pass.\n  const dragBlockedBySort = isSorted && (reorderable || treeReorder);\n  const warnedSortRef = useRef(false);\n  useEffect(() => {\n    if (!dragBlockedBySort || warnedSortRef.current) return;\n    warnedSortRef.current = true;\n    console.warn('VirtualList: row reordering is disabled while the list is sorted. Clear the sort to drag rows.');\n  }, [dragBlockedBySort]);\n\n  const drag = useRowDrag({\n    reorderable: reorderable && !isSorted,\n    treeReorder: treeReorder && !isSorted,\n    onReorder,\n    onTreeDrop,\n    longPressDelay,\n    dragHandle,\n    treeIndentPx,\n    keys,\n    depths,\n    isGroupAt,\n    rootRef,\n    keysRef,\n    getVisibleRect: win.getVisibleRect,\n    scrollBy: win.scrollBy,\n  });\n\n  const listId = useId();\n  const rowDomId = (index: number) => `${listId}-row-${index}`;\n  const nav = useRowNavigation({\n    enabled: keyboardNavigation,\n    tabAcrossRows,\n    rowCount: rows.length,\n    offsets,\n    rootRef,\n    headerRef,\n    getVisibleSlice: win.getVisibleSlice,\n    viewportHeight: win.viewportHeight,\n    scrollToIndex: win.scrollApi.scrollToIndex,\n    initialIndex: selectedKey != null ? Math.max(0, keys.indexOf(selectedKey)) : 0,\n    multiSelect,\n    onActivate: onRowClick && ((index: number) => onRowClick(rows[index], index)),\n    onToggleSelect: (index) => selection.handleRowSelect(keys[index]),\n  });\n\n  const rowPaddingValue = typeof rowPadding === 'number' ? `${rowPadding}px` : rowPadding;\n\n  const renderRowAt = (i: number) => {\n    const item = rows[i];\n    const key = getKey(item, i);\n    const isSelected = (selectedKey != null && key === selectedKey) || effectiveSelectedKeys.has(key);\n    const dragProps = drag.getRowDragProps(i, key);\n\n    return (\n      <VirtualRow\n        key={key}\n        id={rowDomId(i)}\n        index={i}\n        top={offsets[i]}\n        padding={rowPaddingValue}\n        columnTemplate={columnTemplate}\n        isSelected={isSelected}\n        isActive={i === nav.activeIndex}\n        manageTabStops={keyboardNavigation}\n        isEntered={i === nav.enteredIndex}\n        activeClassName={classNames?.activeRow}\n        activeStyle={slotStyles?.activeRow}\n        showDivider={showDivider}\n        hover={rowHover}\n        onClick={() => {\n          if (drag.consumeSuppressedClick()) return;\n          // Keep the arrows where the pointer last was.\n          nav.setActiveIndex(i);\n          onRowClick?.(item, i);\n        }}\n        observer={rowObserver}\n        ariaRowIndex={hasColumns ? i + 2 : undefined}\n        ariaPosInSet={hasColumns ? undefined : i + 1}\n        ariaSetSize={hasColumns ? undefined : rows.length}\n        rowRole={ariaRoles?.row ?? (hasColumns ? 'row' : 'listitem')}\n        reserveToggleGutter={shouldShowToggle}\n        {...dragProps}\n        dropIndicatorClassName={classNames?.dropIndicator}\n        dropIndicatorStyle={slotStyles?.dropIndicator}\n        rowClassName={classNames?.row}\n        rowStyle={slotStyles?.row}\n      >\n        {hasColumns ? (\n          <>\n            {multiSelect && (\n              <div\n                className={cx(styles.virtualListSelectCell, classNames?.selectCell)}\n                style={slotStyles?.selectCell}\n                role='gridcell'\n                onClick={(e) => e.stopPropagation()}\n              >\n                <Checkbox\n                  selected={effectiveSelectedKeys.has(key)}\n                  onToggle={() => selection.handleRowSelect(key)}\n                  aria-label={text.selectRow}\n                  // One tab stop per rendered row, changing as the list scrolls,\n                  // would strand the focus the moment a row unmounts. Space on\n                  // the active row is the keyboard path instead.\n                  tabIndex={-1}\n                />\n              </div>\n            )}\n            {visibleColumns.map((col) => (\n              <div\n                key={col.key}\n                className={cx(styles.virtualListCell, classNames?.cell)}\n                style={slotStyles?.cell}\n                role='gridcell'\n                data-col-key={col.key}\n                data-fit-key={col.width.type === 'fit' ? col.key : undefined}\n              >\n                {col.renderCell(item, i)}\n              </div>\n            ))}\n          </>\n        ) : (\n          renderRow?.(item, i)\n        )}\n      </VirtualRow>\n    );\n  };\n\n  const visibleRows: React.ReactNode[] = [];\n  for (let i = startIndex; i <= endIndex; i++) visibleRows.push(renderRowAt(i));\n\n  // Keep the row being touch-dragged mounted even if it scrolls out of the\n  // window. Touch events are dispatched to the element under the finger at\n  // touchstart; if that row unmounts, the browser stops delivering touchmove\n  // and the drag freezes. Pinning its DOM node keeps the gesture alive.\n  const draggedIndex = drag.draggingKey != null ? keys.indexOf(drag.draggingKey) : -1;\n  if (draggedIndex >= 0 && (draggedIndex < startIndex || draggedIndex > endIndex)) {\n    visibleRows.push(renderRowAt(draggedIndex));\n  }\n\n  // Same reason, one step further: the row being used holds the focus, and\n  // unmounting it would drop that focus to the document body mid-scroll.\n  if (nav.enteredIndex >= 0 && (nav.enteredIndex < startIndex || nav.enteredIndex > endIndex)) {\n    visibleRows.push(renderRowAt(nav.enteredIndex));\n  }\n\n  // Navigation must not replace handlers the consumer passed. Theirs runs first,\n  // and a `preventDefault` in it keeps the event: the navigation handlers all\n  // check `defaultPrevented`.\n  const containerProps = {\n    ...nav.containerProps,\n    onKeyDown: chain<React.KeyboardEvent<HTMLDivElement>>(rest.onKeyDown, nav.containerProps.onKeyDown),\n    onFocus: chain<React.FocusEvent<HTMLDivElement>>(rest.onFocus, nav.containerProps.onFocus),\n    onBlur: chain<React.FocusEvent<HTMLDivElement>>(rest.onBlur, nav.containerProps.onBlur),\n  };\n\n  return (\n    <div\n      {...rest}\n      {...containerProps}\n      ref={rootRef}\n      className={cx(\n        styles.virtualList,\n        win.isWindowed && styles.virtualListWindowed,\n        drag.draggingKey != null && styles.virtualListReordering,\n        columnResize.isResizing && styles.virtualListResizing,\n        className,\n        classNames?.root,\n      )}\n      style={{ ...(win.isWindowed ? null : { height }), ...widthVars, ...style, ...slotStyles?.root }}\n      role={ariaRoles?.container ?? (hasColumns ? 'grid' : 'list')}\n      aria-rowcount={hasColumns ? rows.length + 1 : undefined}\n      aria-busy={isLoading || win.showLoadMoreSpinner || rest['aria-busy'] || undefined}\n      // Only a composite role may point at an active descendant, and the default\n      // `list` is not one. In list mode, pass `ariaRoles` (e.g. listbox/option)\n      // to have the active row announced.\n      aria-activedescendant={\n        nav.activeIndex >= 0 && (ariaRoles?.container ?? (hasColumns ? 'grid' : 'list')) !== 'list'\n          ? rowDomId(nav.activeIndex)\n          : undefined\n      }\n    >\n      {hasColumns && (\n        <VirtualListHeader<T>\n          headerRef={headerRef}\n          columns={columns!}\n          cols={cols}\n          resize={columnResize}\n          sort={effectiveSort}\n          onSort={handleHeaderSort}\n          multiSelect={multiSelect}\n          allSelected={selection.allSelected}\n          someSelected={selection.someSelected}\n          onSelectAll={selection.handleSelectAll}\n          labels={text}\n          classNames={classNames}\n          styles={slotStyles}\n        />\n      )}\n      {/* Hidden, with the list marked busy instead: a list or grid may only own rows. */}\n      {isLoading ? (\n        <div className={styles.virtualListLoadingFull} aria-hidden='true'>\n          <Loader size={32} />\n        </div>\n      ) : (\n        <div\n          className={cx(styles.virtualListBody, classNames?.body)}\n          style={{ height: totalHeight, ...slotStyles?.body }}\n        >\n          {visibleRows}\n        </div>\n      )}\n      {win.showLoadMoreSpinner && (\n        <div\n          className={cx(styles.virtualListLoadingIndicator, classNames?.loadingIndicator)}\n          style={slotStyles?.loadingIndicator}\n          aria-hidden='true'\n        >\n          <Loader size={32} />\n        </div>\n      )}\n    </div>\n  );\n}\n\nexport default VirtualList;\n","import type { FlatTreeNode, TreeNode, TreeValue } from './tree.types';\n\n/** The visible nodes in render order. Flat, not nested, deliberately: see docs/Tree.md. */\nexport function flattenTree(\n  nodes: TreeNode[],\n  expandedKeys: TreeValue[],\n  level = 0,\n  parentKey: TreeValue | null = null,\n): FlatTreeNode[] {\n  return nodes.flatMap((node, index) => {\n    const isBranch = node.children !== undefined;\n    const isExpanded = isBranch && expandedKeys.includes(node.key);\n    const self: FlatTreeNode = {\n      node,\n      level,\n      parentKey,\n      isBranch,\n      isExpanded,\n      setSize: nodes.length,\n      posInSet: index + 1,\n    };\n    if (!isExpanded || !node.children?.length) return [self];\n    return [self, ...flattenTree(node.children, expandedKeys, level + 1, node.key)];\n  });\n}\n",".tree {\n  display: flex;\n  flex-direction: column;\n  width: 100%;\n  color: var(--text-color);\n\n  &-disabled {\n    opacity: 0.5;\n  }\n\n  &-node {\n    display: flex;\n    align-items: center;\n    gap: 6px;\n    box-sizing: border-box;\n    min-height: var(--tree-node-height, 32px);\n    padding-right: 8px;\n    border-radius: var(--default-border-radius);\n    cursor: pointer;\n    -webkit-tap-highlight-color: transparent;\n    user-select: none;\n\n    &:hover {\n      background: var(--background-accent);\n    }\n\n    &:focus-visible {\n      outline: var(--focus-border-color, var(--primary-color)) solid 2px;\n      outline-offset: -2px;\n    }\n\n    &-selected {\n      background: var(--tree-selected-background, var(--primary-color));\n      color: var(--tree-selected-color, var(--text-on-primary));\n\n      &:hover {\n        background: var(--tree-selected-background, var(--primary-color));\n      }\n\n      &:focus-visible {\n        outline-color: var(--tree-selected-color, var(--text-on-primary));\n      }\n    }\n\n    &-disabled {\n      opacity: 0.5;\n      cursor: default;\n\n      &:hover {\n        background: transparent;\n      }\n    }\n  }\n\n  &-toggle {\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    flex: none;\n    width: 1.25em;\n    height: 1.25em;\n    font-size: 0.8em;\n    border-radius: 50%;\n    transition: transform 0.15s;\n\n    &-open {\n      transform: rotate(90deg);\n    }\n\n    /* Kept in the layout rather than removed, so leaf labels line up with their siblings'. */\n    &-hidden {\n      visibility: hidden;\n    }\n  }\n\n  &-icon {\n    /* FontAwesome's own rule is unlayered, so it outranks `width` from inside the library's layer and\n       reads this instead. `width` still sizes a custom icon. */\n    --fa-width: 1em;\n\n    flex: none;\n    width: 1em;\n  }\n\n  &-label {\n    overflow: hidden;\n    text-overflow: ellipsis;\n    white-space: nowrap;\n  }\n\n  &-empty {\n    padding: 12px 8px;\n    color: var(--text-dark);\n  }\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .tree-toggle {\n    transition: none;\n  }\n}\n","import { useEffect, useRef, useState } from 'react';\nimport type { ReactElement } from 'react';\nimport cx from 'classnames';\nimport { faChevronRight } from '@fortawesome/free-solid-svg-icons';\n\nimport VirtualList from 'components/common/virtualList';\nimport type { VirtualListHandle } from 'components/common/virtualList';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport { renderIcon } from '../utils/renderIcon';\nimport { flattenTree } from './flattenTree';\nimport type { FlatTreeNode, TreeLabels, TreeProps, TreeValue } from './tree.types';\n\nimport styles from './tree.module.pcss';\n\nconst EMPTY_KEYS: TreeValue[] = [];\n\nconst DEFAULT_LABELS: Required<TreeLabels> = {\n  empty: 'Nothing here',\n};\n\nfunction Tree(props: TreeProps): ReactElement {\n  const {\n    nodes,\n    expandedKeys: controlledExpanded,\n    defaultExpandedKeys = EMPTY_KEYS,\n    onExpandedChange = () => {},\n    selectedKey: controlledSelected,\n    defaultSelectedKey,\n    onSelect = () => {},\n    expandOnSelect = true,\n    indent = 20,\n    toggleIcon = faChevronRight,\n    height,\n    estimatedRowHeight = 32,\n    labels,\n    disabled = false,\n    'aria-label': ariaLabel = 'Tree',\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    ...rest\n  } = useComponentDefaults('Tree', props);\n  const text = useLabels('Tree', labels, DEFAULT_LABELS);\n\n  const nodeRefs = useRef(new Map<TreeValue, HTMLDivElement>());\n  const listRef = useRef<VirtualListHandle>(null);\n  const pendingFocusRef = useRef<TreeValue | null>(null);\n  const isWindowed = height !== undefined;\n\n  const isExpandedControlled = controlledExpanded !== undefined;\n  const [internalExpanded, setInternalExpanded] = useState<TreeValue[]>(defaultExpandedKeys);\n  const expandedKeys = isExpandedControlled ? controlledExpanded : internalExpanded;\n\n  const isSelectedControlled = controlledSelected !== undefined;\n  const [internalSelected, setInternalSelected] = useState<TreeValue | null>(defaultSelectedKey ?? null);\n  const selectedKey = isSelectedControlled ? controlledSelected : internalSelected;\n\n  const visible = flattenTree(nodes, expandedKeys);\n\n  // Windowed, the tab stop sits on the container instead of this row: a roving tabindex on a row\n  // scrolled out of the window would leave the tree with no tab stop at all.\n  const activeIndex = Math.max(\n    0,\n    visible.findIndex((entry) => entry.node.key === selectedKey),\n  );\n  const activeKey = visible[activeIndex]?.node.key;\n\n  function setExpanded(next: TreeValue[]) {\n    if (!isExpandedControlled) setInternalExpanded(next);\n    onExpandedChange(next);\n  }\n\n  function toggle(key: TreeValue) {\n    setExpanded(expandedKeys.includes(key) ? expandedKeys.filter((k) => k !== key) : [...expandedKeys, key]);\n  }\n\n  function select(entry: FlatTreeNode) {\n    if (disabled || entry.node.disabled) return;\n    if (!isSelectedControlled) setInternalSelected(entry.node.key);\n    onSelect(entry.node.key, entry.node);\n    if (expandOnSelect && entry.isBranch) toggle(entry.node.key);\n  }\n\n  function focusAt(index: number) {\n    const key = visible[index]?.node.key;\n    if (key === undefined) return;\n\n    const element = nodeRefs.current.get(key);\n    if (element) {\n      element.focus();\n      return;\n    }\n\n    // Outside the window, so it has to be scrolled in before it exists to receive focus.\n    pendingFocusRef.current = key;\n    listRef.current?.scrollToIndex(index, { align: 'center' });\n  }\n\n  useEffect(() => {\n    const key = pendingFocusRef.current;\n    if (key === null) return;\n    const element = nodeRefs.current.get(key);\n    if (element) {\n      element.focus();\n      pendingFocusRef.current = null;\n    }\n  });\n\n  function handleKeyDown(event: React.KeyboardEvent<HTMLDivElement>, entry: FlatTreeNode, index: number) {\n    if (disabled) return;\n\n    switch (event.key) {\n      case 'ArrowDown':\n        event.preventDefault();\n        focusAt(Math.min(index + 1, visible.length - 1));\n        break;\n      case 'ArrowUp':\n        event.preventDefault();\n        focusAt(Math.max(index - 1, 0));\n        break;\n      case 'ArrowRight':\n        event.preventDefault();\n        if (!entry.isBranch) break;\n        if (entry.isExpanded) focusAt(index + 1);\n        else toggle(entry.node.key);\n        break;\n      case 'ArrowLeft': {\n        event.preventDefault();\n        if (entry.isBranch && entry.isExpanded) {\n          toggle(entry.node.key);\n          break;\n        }\n        const parentIndex = visible.findIndex((e) => e.node.key === entry.parentKey);\n        if (parentIndex >= 0) focusAt(parentIndex);\n        break;\n      }\n      case 'Home':\n        event.preventDefault();\n        focusAt(0);\n        break;\n      case 'End':\n        event.preventDefault();\n        focusAt(visible.length - 1);\n        break;\n      case 'Enter':\n      case ' ':\n        event.preventDefault();\n        select(entry);\n        break;\n      default:\n        break;\n    }\n  }\n\n  function renderNode(entry: FlatTreeNode, index: number) {\n    const { node, level, isBranch, isExpanded } = entry;\n    const isSelected = node.key === selectedKey;\n    const isDisabled = disabled || node.disabled;\n\n    return (\n      <div\n        key={node.key}\n        ref={(el) => {\n          if (el) nodeRefs.current.set(node.key, el);\n          else nodeRefs.current.delete(node.key);\n        }}\n        role='treeitem'\n        aria-level={level + 1}\n        aria-setsize={entry.setSize}\n        aria-posinset={entry.posInSet}\n        aria-expanded={isBranch ? isExpanded : undefined}\n        aria-selected={isSelected}\n        aria-disabled={isDisabled || undefined}\n        tabIndex={node.key === activeKey && !isDisabled ? 0 : -1}\n        className={cx(\n          styles.treeNode,\n          isSelected && styles.treeNodeSelected,\n          isDisabled && styles.treeNodeDisabled,\n          classNames?.node,\n        )}\n        style={{ paddingLeft: level * indent, ...slotStyles?.node }}\n        onClick={() => select(entry)}\n        onKeyDown={(event) => handleKeyDown(event, entry, index)}\n      >\n        <span\n          className={cx(\n            styles.treeToggle,\n            isExpanded && styles.treeToggleOpen,\n            !isBranch && styles.treeToggleHidden,\n            classNames?.toggle,\n          )}\n          style={slotStyles?.toggle}\n          aria-hidden='true'\n          onClick={(event) => {\n            event.stopPropagation();\n            if (!isDisabled && isBranch) toggle(node.key);\n          }}\n        >\n          {isBranch && renderIcon(toggleIcon)}\n        </span>\n\n        {node.icon &&\n          renderIcon(node.icon, {\n            className: cx(styles.treeIcon, classNames?.icon),\n            style: slotStyles?.icon,\n          })}\n\n        <span className={cx(styles.treeLabel, classNames?.label)} style={slotStyles?.label}>\n          {node.label}\n        </span>\n      </div>\n    );\n  }\n\n  return (\n    <div\n      {...rest}\n      role='tree'\n      aria-label={ariaLabel}\n      aria-disabled={disabled || undefined}\n      tabIndex={isWindowed && !disabled ? 0 : undefined}\n      onFocus={(event) => {\n        if (event.target === event.currentTarget) focusAt(activeIndex);\n      }}\n      className={cx(styles.tree, disabled && styles.treeDisabled, classNames?.root, className)}\n      style={{ ...style, ...slotStyles?.root }}\n    >\n      {visible.length === 0 ? (\n        <div className={cx(styles.treeEmpty, classNames?.empty)} style={slotStyles?.empty}>\n          {text.empty}\n        </div>\n      ) : isWindowed ? (\n        <VirtualList\n          ref={listRef}\n          items={visible}\n          height={height}\n          estimatedRowHeight={estimatedRowHeight}\n          getItemKey={(entry) => entry.node.key}\n          getItemDepth={(entry) => entry.level}\n          ariaRoles={{ container: 'none', row: 'none' }}\n          // Tree owns the keyboard: its rows carry the roving tabindex and their\n          // own key handling, and a second tab stop here would sit inside it.\n          keyboardNavigation={false}\n          renderRow={renderNode}\n        />\n      ) : (\n        visible.map(renderNode)\n      )}\n    </div>\n  );\n}\n\nexport default Tree;\n","import type { CSSProperties, SVGAttributes } from 'react';\nimport type { DataProps } from '../types/slots.types';\n\nexport enum WeatherType {\n  Sunny = 'sunny',\n  ClearNight = 'clearNight',\n  Cloudy = 'cloudy',\n  PartlyCloudy = 'partlyCloudy',\n  PartlyCloudyNight = 'partlyCloudyNight',\n  RainyLight = 'rainyLight',\n  Rainy = 'rainy',\n  RainyHeavy = 'rainyHeavy',\n  Snowy = 'snowy',\n  Hail = 'hail',\n  Foggy = 'foggy',\n  Thunderstorm = 'thunderstorm',\n}\n\n/** The accessible name of each weather, read out by screen readers. */\nexport type WeatherIconLabels = Partial<Record<`${WeatherType}`, string>>;\n\n/**\n * The root is an `<svg>`, so the native attributes come from `SVGAttributes` rather than\n * `HtmlProps`. Its `type` is the attribute of `<script>` and `<style>`, not of an `<svg>`.\n */\nexport interface WeatherIconProps extends DataProps, Omit<SVGAttributes<SVGSVGElement>, 'type'> {\n  /**\n   * The weather to draw. Takes the enum or its string value, so an API's own enum with the same\n   * values passes as it is.\n   */\n  type: WeatherType | `${WeatherType}`;\n  /** Width and height, any CSS size. Default `64`. */\n  size?: number | string;\n  /**\n   * Default `true`. `false` draws a still frame, for small or repeated icons where the motion is\n   * too small to see but costs the same. Always still under `prefers-reduced-motion`.\n   */\n  animated?: boolean;\n  /** Text this component renders of its own. */\n  labels?: WeatherIconLabels;\n  className?: string;\n  style?: CSSProperties;\n}\n\n/** What a falling part needs to know to pick its still frame. */\nexport interface WeatherPartProps {\n  animated: boolean;\n}\n","/* Line art in `currentColor`, like every other icon: it takes the text colour it sits in. */\n.weatherIcon {\n  display: inline-block;\n  flex-shrink: 0;\n  fill: none;\n  stroke: currentcolor;\n  stroke-linecap: round;\n  stroke-linejoin: round;\n\n  &-behind {\n    transform-origin: 50px 50px;\n    transform: translate(14px, -16px) scale(0.62);\n  }\n\n  &-cloudBehind {\n    transform-origin: 50px 50px;\n    transform: translate(16px, -14px) scale(0.8);\n    opacity: 0.55;\n  }\n\n  /* Wandering and breathing share one keyframe: a running animation replaces the whole\n     `transform`, so two of them on one element would clobber each other. */\n  &-cloud {\n    stroke-width: 3;\n    transform-box: fill-box;\n    transform-origin: center;\n    animation: weatherIconCloudDrift 20s ease-in-out infinite;\n  }\n\n  /* Negative, so the second cloud starts partway through its cycle instead of easing in. */\n  &-cloudDelayed {\n    animation-delay: -9s;\n  }\n\n  &-sunRays {\n    transform-box: fill-box;\n    transform-origin: center;\n    animation: weatherIconRotate 150s linear infinite;\n  }\n\n  /* Anchored at the ray's inner end (y1 in parts.tsx), so it grows outwards whichever way it points. */\n  &-sunRay {\n    stroke-width: 3;\n    transform-origin: 50px 23px;\n    animation: weatherIconRayPulse 5s ease-in-out infinite;\n  }\n\n  &-sunRays > g:nth-child(3n + 2) &-sunRay {\n    animation-delay: 0.8s;\n  }\n\n  &-sunRays > g:nth-child(3n) &-sunRay {\n    animation-delay: 1.6s;\n  }\n\n  &-sunCore,\n  &-moonCore {\n    stroke-width: 3;\n    transform-box: fill-box;\n    transform-origin: center;\n    animation: weatherIconGlow 4s ease-in-out infinite;\n  }\n\n  &-star {\n    stroke-width: 1.4;\n    transform-box: fill-box;\n    transform-origin: center;\n    animation-name: weatherIconTwinkle;\n    animation-timing-function: ease-in-out;\n    animation-iteration-count: infinite;\n  }\n\n  &-rain {\n    stroke-width: 2;\n  }\n\n  /* A dash of 6 slides along a path of 24.12 with a gap of exactly that length: it grows out of\n     the cloud, falls, and shrinks into the ground. With any other gap the loop does not close.\n     `backwards` keeps it hidden through its delay instead of showing the unanimated frame. */\n  &-drop {\n    stroke-dasharray: 6 24.12;\n    animation-name: weatherIconFall;\n    animation-timing-function: linear;\n    animation-iteration-count: infinite;\n    animation-fill-mode: backwards;\n  }\n\n  /* The drop's leading edge reaches the ground 24.12 into a cycle of 30.12, at 80%. The drop\n     fades out as it hits and the splash is at its fullest by 82%: on a slow lane, a splash that\n     peaked later came a visible moment after its drop. Left to sink into the ground, the drop's\n     tail also arrived after the splash and ended as a round-capped dot. */\n  &-splash {\n    stroke-width: 1;\n    opacity: 0;\n    transform-box: fill-box;\n    transform-origin: center;\n    animation-name: weatherIconRing;\n    animation-timing-function: ease-out;\n    animation-iteration-count: infinite;\n  }\n\n  &-snow {\n    stroke-width: 1.3;\n  }\n\n  /* Linear: easing would stop the flake in mid-air at every sway waypoint. */\n  &-flake {\n    animation-name: weatherIconSnowFall;\n    animation-timing-function: linear;\n    animation-iteration-count: infinite;\n    animation-fill-mode: backwards;\n  }\n\n  &-stone {\n    fill: currentcolor;\n    stroke: none;\n    animation-name: weatherIconHailFall;\n    animation-timing-function: linear;\n    animation-iteration-count: infinite;\n    animation-fill-mode: backwards;\n  }\n\n  &-impact {\n    stroke-width: 1;\n    opacity: 0;\n    transform-box: fill-box;\n    transform-origin: center;\n    animation-name: weatherIconRing;\n    animation-timing-function: ease-out;\n    animation-iteration-count: infinite;\n  }\n\n  /* Straight bars of different lengths: a wavy fog reads as an odd shape, not as fog. */\n  &-fog {\n    stroke-width: 4;\n\n    & line:nth-child(1) {\n      opacity: 0.55;\n      animation: weatherIconSway 6.5s ease-in-out infinite;\n    }\n\n    & line:nth-child(2) {\n      opacity: 0.85;\n      animation: weatherIconSway 8s ease-in-out -1.2s infinite reverse;\n    }\n\n    & line:nth-child(3) {\n      opacity: 0.7;\n      animation: weatherIconSway 7s ease-in-out -0.6s infinite;\n    }\n\n    & line:nth-child(4) {\n      opacity: 0.5;\n      animation: weatherIconSway 9s ease-in-out -2s infinite reverse;\n    }\n  }\n\n  /* Two quick flashes and a long dark stretch, well under the three flashes a second that\n     WCAG 2.3.1 sets as the limit. */\n  &-bolt {\n    stroke-width: 2.5;\n    animation: weatherIconFlash 4s linear infinite;\n  }\n}\n\n@keyframes weatherIconCloudDrift {\n  0%,\n  100% {\n    transform: translate(0, 0) scale(1);\n  }\n\n  22% {\n    transform: translate(1.2px, -1.6px) scale(1.015);\n  }\n\n  48% {\n    transform: translate(-0.9px, 1.4px) scale(1.03);\n  }\n\n  74% {\n    transform: translate(0.8px, -1.2px) scale(1.015);\n  }\n\n  90% {\n    transform: translate(-1.1px, 1px) scale(1.005);\n  }\n}\n\n@keyframes weatherIconRotate {\n  to {\n    transform: rotate(360deg);\n  }\n}\n\n@keyframes weatherIconRayPulse {\n  50% {\n    transform: scaleY(1.3);\n  }\n}\n\n@keyframes weatherIconGlow {\n  0%,\n  100% {\n    opacity: 0.92;\n    transform: scale(1);\n  }\n\n  50% {\n    opacity: 1;\n    transform: scale(1.04);\n  }\n}\n\n@keyframes weatherIconTwinkle {\n  0%,\n  100% {\n    opacity: 0.3;\n    transform: scale(0.7);\n  }\n\n  50% {\n    opacity: 1;\n    transform: scale(1.1);\n  }\n}\n\n@keyframes weatherIconFall {\n  0% {\n    stroke-dashoffset: 6;\n    opacity: 1;\n  }\n\n  80% {\n    opacity: 1;\n  }\n\n  84% {\n    opacity: 0;\n  }\n\n  100% {\n    stroke-dashoffset: -24.12;\n    opacity: 0;\n  }\n}\n\n@keyframes weatherIconRing {\n  0%,\n  80% {\n    opacity: 0;\n    transform: scale(0.6);\n  }\n\n  82% {\n    opacity: 0.85;\n    transform: scale(1);\n  }\n\n  100% {\n    opacity: 0;\n    transform: scale(1.4);\n  }\n}\n\n@keyframes weatherIconSnowFall {\n  0% {\n    transform: translate(0, 0);\n    opacity: 0;\n  }\n\n  6% {\n    opacity: 1;\n  }\n\n  25% {\n    transform: translate(3px, 8px);\n  }\n\n  50% {\n    transform: translate(-3px, 16px);\n  }\n\n  75% {\n    transform: translate(2px, 24px);\n  }\n\n  92% {\n    transform: translate(-1px, 29px);\n    opacity: 1;\n  }\n\n  100% {\n    transform: translate(0, 30px);\n    opacity: 0;\n  }\n}\n\n/* Lands at 80%, then bounces on along the way it was already drifting (sideways as well as up),\n   which reads as a bounce rather than a pop. Each stone sets its own landing spot. */\n@keyframes weatherIconHailFall {\n  0% {\n    transform: translate(0, 0);\n    opacity: 0;\n  }\n\n  10% {\n    opacity: 1;\n  }\n\n  80% {\n    transform: translate(calc(var(--weather-icon-dx) * 1px), calc(var(--weather-icon-dy) * 1px));\n    opacity: 1;\n    animation-timing-function: ease-out;\n  }\n\n  88% {\n    transform: translate(calc(var(--weather-icon-dx) * 2px), calc((var(--weather-icon-dy) - 2.5) * 1px));\n    opacity: 1;\n    animation-timing-function: ease-in;\n  }\n\n  100% {\n    transform: translate(calc(var(--weather-icon-dx) * 2.6px), calc(var(--weather-icon-dy) * 1px));\n    opacity: 0;\n  }\n}\n\n@keyframes weatherIconSway {\n  50% {\n    transform: translateX(3px);\n  }\n}\n\n@keyframes weatherIconFlash {\n  0%,\n  8%,\n  14%,\n  100% {\n    opacity: 0.35;\n  }\n\n  4%,\n  11% {\n    opacity: 1;\n  }\n}\n\n/* Falling parts pause rather than stop: parts.tsx gives each a negative delay that freezes it\n   partway down. The rest stop, and an impact frozen mid-pulse would be a stray mark, so it hides. */\n.weatherIcon-still {\n  & .weatherIcon-drop,\n  & .weatherIcon-flake,\n  & .weatherIcon-stone {\n    animation-play-state: paused;\n  }\n\n  & .weatherIcon-cloud,\n  & .weatherIcon-sunRays,\n  & .weatherIcon-sunRay,\n  & .weatherIcon-sunCore,\n  & .weatherIcon-moonCore,\n  & .weatherIcon-star,\n  & .weatherIcon-fog line,\n  & .weatherIcon-bolt {\n    animation: none;\n  }\n\n  & .weatherIcon-splash,\n  & .weatherIcon-impact {\n    animation: none;\n    opacity: 0;\n  }\n\n  & .weatherIcon-bolt {\n    opacity: 1;\n  }\n}\n\n/* The component switches to its still frame by itself; this covers where it cannot read the\n   setting, such as a server render. */\n@media (prefers-reduced-motion: reduce) {\n  .weatherIcon * {\n    animation-play-state: paused;\n  }\n}\n","import type { CSSProperties, ReactElement } from 'react';\nimport cx from 'classnames';\n\nimport type { WeatherPartProps } from './weatherIcon.types';\nimport styles from './weatherIcon.module.pcss';\n\n/** Exported so a scene can clip what sits behind the cloud to this same silhouette. */\nexport const CLOUD_PATH =\n  'M 26.3 62.9 C 15.4 65.0 15.0 48.5 27.8 49.8 C 23.9 43.9 34.2 34.0 43.0 39.6 C 46.0 26.2 73.9 32.0 68.9 46.3 C 85.0 44.3 84.6 64.2 72.4 62.5 Z';\n\n/**\n * A still frame of a falling part must not be its first one, which bunches every drop right under\n * the cloud. A negative delay on a paused animation freezes it that far into its cycle. The\n * fractions come from a jitter table rather than a ramp: lanes run left to right, so a ramp would\n * freeze them along one diagonal line.\n */\nfunction fallStyle(animated: boolean, duration: number, delay: number, freezeFraction: number): CSSProperties {\n  return {\n    animationDuration: `${duration}s`,\n    animationDelay: `${animated ? delay : -freezeFraction * duration}s`,\n  };\n}\n\nexport function Cloud({ className, delayed = false }: { className?: string; delayed?: boolean }): ReactElement {\n  return (\n    <path\n      className={cx(styles.weatherIconCloud, delayed && styles.weatherIconCloudDelayed, className)}\n      d={CLOUD_PATH}\n    />\n  );\n}\n\nconst RAY_COUNT = 12;\n\nexport function Sun({ className }: { className?: string }): ReactElement {\n  return (\n    <g className={className}>\n      <g className={styles.weatherIconSunRays}>\n        {Array.from({ length: RAY_COUNT }, (_, index) => (\n          <g key={index} transform={`rotate(${(index / RAY_COUNT) * 360} 50 50)`}>\n            {/* Every second ray reaches further. */}\n            <line className={styles.weatherIconSunRay} x1={50} y1={23} x2={50} y2={index % 2 === 0 ? 16 : 11} />\n          </g>\n        ))}\n      </g>\n      <circle className={styles.weatherIconSunCore} cx={50} cy={50} r={18} />\n    </g>\n  );\n}\n\n// Scattered at uneven distances as well as angles: at one distance they read as a ring.\nconst STARS = [\n  { x: 74, y: 32, size: 2.2, duration: 2.4, delay: 0 },\n  { x: 28, y: 30, size: 2.6, duration: 3.1, delay: 0.4 },\n  { x: 80, y: 60, size: 1.8, duration: 2.7, delay: 0.9 },\n  { x: 22, y: 66, size: 2.4, duration: 3.6, delay: 0.2 },\n  { x: 58, y: 82, size: 2, duration: 2.9, delay: 1.3 },\n  { x: 34, y: 80, size: 2.5, duration: 3.3, delay: 0.6 },\n];\n\nexport function Moon({ className }: { className?: string }): ReactElement {\n  return (\n    <g className={className}>\n      {STARS.map((star) => (\n        // The position sits on its own group: an animated `transform` would replace a static one.\n        <g key={`${star.x}-${star.y}`} transform={`translate(${star.x}, ${star.y})`}>\n          <g\n            className={styles.weatherIconStar}\n            style={{ animationDuration: `${star.duration}s`, animationDelay: `${star.delay}s` }}\n          >\n            <path d={`M 0 ${-star.size} L 0 ${star.size} M ${-star.size} 0 L ${star.size} 0`} />\n          </g>\n        </g>\n      ))}\n      <circle className={styles.weatherIconMoonCore} cx={50} cy={50} r={18} />\n    </g>\n  );\n}\n\nexport enum RainIntensity {\n  Light = 'light',\n  Medium = 'medium',\n  Heavy = 'heavy',\n}\n\n// Density and speed rise together: speed alone reads as light rain on fast-forward.\nconst RAIN = {\n  [RainIntensity.Light]: { lanes: 4, duration: 1.5 },\n  [RainIntensity.Medium]: { lanes: 6, duration: 0.95 },\n  [RainIntensity.Heavy]: { lanes: 10, duration: 0.55 },\n};\n\nconst DURATION_JITTER = [0.85, 1.1, 0.95, 1.2, 0.9, 1.05, 1.15, 0.8, 1, 1.08];\nconst DELAY_JITTER = [0.05, 0.62, 0.31, 0.85, 0.18, 0.5, 0.74, 0.1, 0.43, 0.95];\nconst PER_LANE = 2;\n// A drop fades out from 80% of its fall, so a still frame keeps every drop short of that.\nconst RAIN_FREEZE_SPAN = 0.65;\n\nfunction freezeAt(index: number, jitter: number[] = DELAY_JITTER, span = 0.8): number {\n  return 0.1 + span * jitter[index % jitter.length];\n}\n\nexport function Rain({\n  animated,\n  intensity = RainIntensity.Medium,\n}: WeatherPartProps & { intensity?: RainIntensity }): ReactElement {\n  const { lanes, duration: base } = RAIN[intensity];\n  return (\n    <g className={styles.weatherIconRain}>\n      {Array.from({ length: lanes }, (_, lane) => {\n        const x1 = 26 + (50 * lane) / (lanes - 1);\n        const duration = base * DURATION_JITTER[lane];\n        const delay = duration * DELAY_JITTER[lane];\n        return Array.from({ length: PER_LANE }, (_, drop) => {\n          const style = fallStyle(\n            animated,\n            duration,\n            delay + (drop * duration) / PER_LANE,\n            freezeAt(lane * PER_LANE + drop, DELAY_JITTER, RAIN_FREEZE_SPAN),\n          );\n          return (\n            <g key={`${lane}-${drop}`}>\n              <line className={styles.weatherIconDrop} style={style} x1={x1} y1={68} x2={x1 - 2.4} y2={92} />\n              <ellipse className={styles.weatherIconSplash} style={style} cx={x1 - 2.4} cy={92} rx='1.4' ry='0.7' />\n            </g>\n          );\n        });\n      })}\n    </g>\n  );\n}\n\n// A wide spread of speeds: with similar ones, flakes drift out of step so slowly that they spend\n// long stretches falling in a line.\nconst FLAKE_DURATION_JITTER = [0.65, 1.5, 0.85, 1.25, 1.65, 0.75, 1.1];\nconst FLAKE_DELAY_JITTER = [0.15, 0.7, 0.35, 0.9, 0.05, 0.55, 0.8];\nconst FLAKE_LANES = 7;\n\nexport function Snow({ animated }: WeatherPartProps): ReactElement {\n  return (\n    <g className={styles.weatherIconSnow}>\n      {Array.from({ length: FLAKE_LANES }, (_, lane) => {\n        const x = 24 + (54 * lane) / (FLAKE_LANES - 1);\n        const duration = 7 * FLAKE_DURATION_JITTER[lane];\n        const delay = duration * FLAKE_DELAY_JITTER[lane];\n        return Array.from({ length: PER_LANE }, (_, flake) => (\n          <g key={`${lane}-${flake}`} transform={`translate(${x}, 68)`}>\n            <g\n              className={styles.weatherIconFlake}\n              style={fallStyle(\n                animated,\n                duration,\n                delay + flake * duration * 0.7,\n                freezeAt(lane * PER_LANE + flake, FLAKE_DELAY_JITTER),\n              )}\n            >\n              <path d='M 0 -2 L 0 2 M -1.7 -1 L 1.7 1 M -1.7 1 L 1.7 -1' />\n            </g>\n          </g>\n        ));\n      })}\n    </g>\n  );\n}\n\n// `dx`/`dy` is where the stone first lands. The keyframe bounces it on from there, so every lane\n// shares one keyframe instead of having its own.\nconst HAIL_LANES = [\n  { x: 34, y: 66, dx: -1, dy: 18, duration: 0.6, delay: 0.05 },\n  { x: 47, y: 68, dx: -2, dy: 17, duration: 0.7, delay: 0.3 },\n  { x: 58, y: 65, dx: 1, dy: 18, duration: 0.55, delay: 0.15 },\n  { x: 40, y: 70, dx: -1, dy: 16, duration: 0.75, delay: 0.45 },\n  { x: 65, y: 67, dx: -2, dy: 17, duration: 0.65, delay: 0 },\n  { x: 52, y: 71, dx: 1, dy: 16, duration: 0.7, delay: 0.35 },\n];\n\nexport function Hail({ animated }: WeatherPartProps): ReactElement {\n  const total = HAIL_LANES.length * PER_LANE;\n  return (\n    <g className={styles.weatherIconHail}>\n      {HAIL_LANES.flatMap((lane, laneIndex) =>\n        Array.from({ length: PER_LANE }, (_, stone) => {\n          const index = laneIndex * PER_LANE + stone;\n          const style = fallStyle(\n            animated,\n            lane.duration,\n            lane.delay + (stone * lane.duration) / PER_LANE,\n            0.1 + (0.8 * (index + 0.5)) / total,\n          );\n          return (\n            <g key={index}>\n              <g transform={`translate(${lane.x}, ${lane.y})`}>\n                <g\n                  className={styles.weatherIconStone}\n                  style={{ ...style, '--weather-icon-dx': lane.dx, '--weather-icon-dy': lane.dy } as CSSProperties}\n                >\n                  <circle r='2' />\n                </g>\n              </g>\n              <ellipse\n                className={styles.weatherIconImpact}\n                style={style}\n                cx={lane.x + lane.dx}\n                cy={lane.y + lane.dy}\n                rx='1.8'\n                ry='0.9'\n              />\n            </g>\n          );\n        }),\n      )}\n    </g>\n  );\n}\n\nexport function Fog(): ReactElement {\n  return (\n    <g className={styles.weatherIconFog}>\n      <line x1='32' y1='40' x2='68' y2='40' />\n      <line x1='20' y1='52' x2='80' y2='52' />\n      <line x1='26' y1='64' x2='74' y2='64' />\n      <line x1='34' y1='76' x2='66' y2='76' />\n    </g>\n  );\n}\n\nexport function Lightning(): ReactElement {\n  return <path className={styles.weatherIconBolt} d='M 54 52 L 44 72 H 52 L 46 90 L 62 66 H 54 L 60 52 Z' />;\n}\n","import { useId, type ReactElement } from 'react';\nimport cx from 'classnames';\n\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport { useMediaQuery } from '../hooks/useMediaQuery';\nimport { WeatherType, type WeatherIconLabels, type WeatherIconProps } from './weatherIcon.types';\nimport { CLOUD_PATH, Cloud, Fog, Hail, Lightning, Moon, Rain, RainIntensity, Snow, Sun } from './parts';\nimport styles from './weatherIcon.module.pcss';\n\nconst DEFAULT_LABELS: Required<WeatherIconLabels> = {\n  [WeatherType.Sunny]: 'Sunny',\n  [WeatherType.ClearNight]: 'Clear night',\n  [WeatherType.Cloudy]: 'Cloudy',\n  [WeatherType.PartlyCloudy]: 'Partly cloudy',\n  [WeatherType.PartlyCloudyNight]: 'Partly cloudy night',\n  [WeatherType.RainyLight]: 'Light rain',\n  [WeatherType.Rainy]: 'Rain',\n  [WeatherType.RainyHeavy]: 'Heavy rain',\n  [WeatherType.Snowy]: 'Snow',\n  [WeatherType.Hail]: 'Hail',\n  [WeatherType.Foggy]: 'Fog',\n  [WeatherType.Thunderstorm]: 'Thunderstorm',\n};\n\nfunction WeatherIcon(props: WeatherIconProps): ReactElement {\n  const {\n    type,\n    size = 64,\n    animated = true,\n    labels,\n    className,\n    style,\n    ...rest\n  } = useComponentDefaults('WeatherIcon', props);\n  const text = useLabels('WeatherIcon', labels, DEFAULT_LABELS);\n  const isAnimated = animated && !useMediaQuery('(prefers-reduced-motion: reduce)');\n  // useId's characters are not all safe inside `url(#…)`, and they changed between React 19 minors.\n  const clipId = `weather-icon-clip-${useId().replace(/[^\\w-]/g, '')}`;\n\n  // The cloud is only an outline, so what sits behind it would show through without this.\n  const behindCloud = (content: ReactElement) => <g clipPath={`url(#${clipId})`}>{content}</g>;\n\n  function scene(): ReactElement {\n    switch (type) {\n      case WeatherType.ClearNight:\n        return <Moon />;\n      case WeatherType.Cloudy:\n        return (\n          <>\n            {/* The offset sits on a wrapper: the cloud's own `transform` is animated and would replace it. */}\n            {behindCloud(\n              <g className={styles.weatherIconCloudBehind}>\n                <Cloud delayed />\n              </g>,\n            )}\n            <Cloud />\n          </>\n        );\n      case WeatherType.PartlyCloudy:\n        return (\n          <>\n            {behindCloud(<Sun className={styles.weatherIconBehind} />)}\n            <Cloud />\n          </>\n        );\n      case WeatherType.PartlyCloudyNight:\n        return (\n          <>\n            {behindCloud(<Moon className={styles.weatherIconBehind} />)}\n            <Cloud />\n          </>\n        );\n      case WeatherType.RainyLight:\n      case WeatherType.Rainy:\n      case WeatherType.RainyHeavy:\n        return (\n          <>\n            <Cloud />\n            <Rain\n              animated={isAnimated}\n              intensity={\n                type === WeatherType.RainyLight\n                  ? RainIntensity.Light\n                  : type === WeatherType.RainyHeavy\n                    ? RainIntensity.Heavy\n                    : RainIntensity.Medium\n              }\n            />\n          </>\n        );\n      case WeatherType.Snowy:\n        return (\n          <>\n            <Cloud />\n            <Snow animated={isAnimated} />\n          </>\n        );\n      case WeatherType.Hail:\n        return (\n          <>\n            <Cloud />\n            <Hail animated={isAnimated} />\n          </>\n        );\n      case WeatherType.Foggy:\n        return <Fog />;\n      case WeatherType.Thunderstorm:\n        return (\n          <>\n            <Cloud />\n            {behindCloud(<Lightning />)}\n          </>\n        );\n      default:\n        return <Sun />;\n    }\n  }\n\n  return (\n    <svg\n      {...rest}\n      role='img'\n      aria-label={text[type] ?? text[WeatherType.Sunny]}\n      className={cx(styles.weatherIcon, !isAnimated && styles.weatherIconStill, className)}\n      style={{ width: size, height: size, ...style }}\n      viewBox='0 0 100 100'\n    >\n      <defs>\n        {/* One path, both contours: two separate paths in a clipPath union instead of cutting a hole. */}\n        <clipPath id={clipId} clipRule='evenodd'>\n          <path d={`M -20 -20 H 120 V 120 H -20 Z ${CLOUD_PATH}`} />\n        </clipPath>\n      </defs>\n      {scene()}\n    </svg>\n  );\n}\n\nexport default WeatherIcon;\n",".wizard {\n  height: 100%;\n  width: 100%;\n  position: relative;\n\n  /* Clipped sideways only, which is all the slide needs. Clipped vertically too, it cut off the\n     floated label of a step's first field and the bottom border of its last. */\n  overflow: clip visible;\n  transition: all 0.2s;\n  padding: 1px;\n\n  &-content {\n    position: absolute;\n    height: fit-content;\n\n    &-outOfFrame {\n      * {\n        user-select: none;\n        pointer-events: none;\n        visibility: hidden;\n        display: none;\n      }\n    }\n\n    &-container {\n      display: flex;\n      transition: all 0.6s;\n      position: relative;\n    }\n  }\n}\n\n/* The step slide is the whole component's motion; without it a step change is an instant swap,\n   which is the point. */\n@media (prefers-reduced-motion: reduce) {\n  .wizard,\n  .wizard-content-container {\n    transition: none;\n  }\n}\n","import React, { useEffect, useRef, useState } from 'react';\nimport cx from 'classnames';\nimport styles from './wizard.module.pcss';\nimport type { WizardProps } from './wizard.types';\n\nexport default function Wizard({\n  currentStep = 0,\n  stepCount = 0,\n  renderStep = () => null,\n  className,\n  style,\n  classNames,\n  styles: slotStyles,\n  ...rest\n}: WizardProps) {\n  const [height, setHeight] = useState<number | undefined>(undefined);\n  const [showOtherContent, setShowOtherContent] = useState(false);\n  const [prevStep, setPrevStep] = useState(currentStep);\n  const stepRefs = useRef<(HTMLDivElement | null)[]>([]);\n  const resizeObserverRef = useRef<ResizeObserver | null>(null);\n\n  // When currentStep changes, briefly show off-screen steps so they animate out.\n  if (currentStep !== prevStep) {\n    setPrevStep(currentStep);\n    setShowOtherContent(true);\n  }\n\n  useEffect(() => {\n    if (!showOtherContent) return;\n    const id = setTimeout(() => setShowOtherContent(false), 400);\n    return () => clearTimeout(id);\n  }, [showOtherContent]);\n\n  // Track the height of the active step via ResizeObserver.\n  useEffect(() => {\n    resizeObserverRef.current?.disconnect();\n    const el = stepRefs.current[currentStep];\n    if (!el) return;\n    setHeight(el.clientHeight);\n    resizeObserverRef.current = new ResizeObserver(() => {\n      setHeight(el.clientHeight);\n    });\n    resizeObserverRef.current.observe(el);\n    return () => resizeObserverRef.current?.disconnect();\n  }, [currentStep, stepCount]);\n\n  const rootStyle: React.CSSProperties = {\n    ...(height !== undefined ? { height: `${height}px` } : {}),\n    ...slotStyles?.root,\n    ...style,\n  };\n\n  return (\n    <div {...rest} className={cx(styles.wizard, classNames?.root, className)} style={rootStyle}>\n      <div\n        className={cx(styles.wizardContentContainer, classNames?.container)}\n        style={{\n          width: `${stepCount * 100}%`,\n          marginLeft: `-${currentStep * 100}%`,\n          ...slotStyles?.container,\n        }}\n      >\n        {Array.from({ length: stepCount }, (_, index) => (\n          <div\n            key={`wizardStep${index}`}\n            ref={(ref) => {\n              stepRefs.current[index] = ref;\n            }}\n            className={cx(styles.wizardContent, classNames?.step)}\n            style={{\n              width: `${100 / stepCount}%`,\n              marginLeft: `${(100 / stepCount) * index}%`,\n              ...slotStyles?.step,\n            }}\n          >\n            <div\n              className={cx(\n                { [styles.wizardContentOutOfFrame]: currentStep !== index && !showOtherContent },\n                classNames?.stepContent,\n              )}\n              style={slotStyles?.stepContent}\n              inert={currentStep !== index ? true : undefined}\n            >\n              {renderStep(index)}\n            </div>\n          </div>\n        ))}\n      </div>\n    </div>\n  );\n}\n","import { showToast } from 'components/common/toast';\nimport { FormValidatorGroup, createFormComponent } from '@ahrowe/form-validation';\nimport type { FormGroup, FormValidatorGroupOptions, InferGroupValues, ValidationError } from '@ahrowe/form-validation';\nimport type React from 'react';\nimport { ThemeColor } from '../../common/themeProvider/color.types';\n\n/** Default onSubmitError: surface the first error as an error toast. Callers can override via options.onSubmitError. */\nconst defaultOnSubmitError = (errors: ValidationError[]): void => {\n  showToast(errors[0].message, { color: ThemeColor.Error });\n};\n\n/**\n * FormValidatorGroup with `this.Form` automatically attached.\n * Kept in a .tsx file so formValidatorGroup.ts stays framework-agnostic.\n *\n * This is the class you should use everywhere — it is a drop-in replacement\n * for FormValidatorGroup with one extra property: `this.Form`.\n *\n * Also injects the default toast-based onSubmitError so both hook and class-component\n * consumers surface errors out of the box; pass options.onSubmitError to override.\n */\nexport default class FormValidatorGroupWithForm<\n  TGroup extends FormGroup = FormGroup,\n  TInitial = InferGroupValues<TGroup>,\n> extends FormValidatorGroup<TGroup, TInitial> {\n  Form: React.FC<React.FormHTMLAttributes<HTMLFormElement>>;\n\n  constructor(\n    groupDef: TGroup,\n    component: { forceUpdate: () => void } | null = null,\n    options?: FormValidatorGroupOptions<TGroup, TInitial>,\n  ) {\n    super(groupDef, component, { ...options, onSubmitError: options?.onSubmitError ?? defaultOnSubmitError });\n    this.Form = createFormComponent(this);\n  }\n}\n","import React, { PureComponent } from 'react';\nimport { FormValidator } from '@ahrowe/form-validation';\nimport Tooltip from 'components/common/tooltip';\n\nexport interface ValidatableComponentProps {\n  formValidator?: FormValidator | null;\n  onMouseEnter?: ((event: React.MouseEvent) => void) | null;\n  onMouseLeave?: ((event: React.MouseEvent) => void) | null;\n  onFocus?: ((event: React.FocusEvent) => void) | null;\n  onBlur?: ((event: React.FocusEvent) => void) | null;\n  onChange?: ((value: unknown) => void) | null;\n  errorMessage?: string;\n  isValid?: boolean;\n  isRequired?: boolean;\n  label?: string;\n}\n\nexport interface ValidatableComponentState {\n  isFocused: boolean;\n  isHovered: boolean;\n}\n\n/**\n * Base class that extends PureComponent with FormValidator integration.\n * Manages hover/focus state and renders error tooltips.\n */\nexport default class ValidatableComponent<\n  P extends ValidatableComponentProps = ValidatableComponentProps,\n  S extends ValidatableComponentState = ValidatableComponentState,\n> extends PureComponent<P, S> {\n  static defaultProps: Partial<ValidatableComponentProps> = {\n    formValidator: null,\n    onMouseEnter: null,\n    onMouseLeave: null,\n    onFocus: null,\n    onBlur: null,\n    onChange: null,\n    errorMessage: '',\n    isValid: false,\n    isRequired: false,\n    label: '',\n  };\n\n  constructor(props: P) {\n    super(props);\n    const { formValidator } = props;\n\n    if (formValidator) {\n      formValidator.label = props.label ?? '';\n      formValidator.validate();\n      formValidator.registerOnUpdateListener(() => {\n        this.forceUpdate();\n      });\n    }\n\n    this.state = { isFocused: false, isHovered: false } as S;\n\n    this.onMouseEnter = this.onMouseEnter.bind(this);\n    this.onMouseLeave = this.onMouseLeave.bind(this);\n    this.onFocus = this.onFocus.bind(this);\n    this.onBlur = this.onBlur.bind(this);\n    this.hasError = this.hasError.bind(this);\n    this.renderTooltip = this.renderTooltip.bind(this);\n    this.onChange = this.onChange.bind(this);\n  }\n\n  componentDidMount(): void {\n    setTimeout(() => this.setState({ isFocused: false, isHovered: false } as S), 1);\n  }\n\n  componentDidUpdate(prevProps: P): void {\n    const { formValidator } = this.props;\n    if (prevProps.formValidator && formValidator && prevProps.formValidator !== formValidator) {\n      formValidator.registerOnUpdateListener(() => {\n        this.forceUpdate();\n      });\n    }\n  }\n\n  onMouseEnter(event: React.MouseEvent): void {\n    const { onMouseEnter } = this.props;\n    this.setState({ isHovered: true } as unknown as S);\n    if (onMouseEnter) onMouseEnter(event);\n  }\n\n  onMouseLeave(event: React.MouseEvent): void {\n    const { onMouseLeave } = this.props;\n    this.setState({ isHovered: false } as unknown as S);\n    if (onMouseLeave) onMouseLeave(event);\n  }\n\n  onFocus(event: React.FocusEvent): void {\n    const { onFocus } = this.props;\n    this.setState({ isFocused: true } as unknown as S);\n    if (onFocus) onFocus(event);\n  }\n\n  onBlur(event: React.FocusEvent): void {\n    const { formValidator, onBlur } = this.props;\n    if (formValidator) formValidator.touched = true;\n    this.setState({ isFocused: false } as unknown as S);\n    if (onBlur) onBlur(event);\n  }\n\n  hasError(): boolean {\n    const { errorMessage, isValid, formValidator } = this.props;\n    return formValidator ? formValidator.shouldShowError : !isValid || !!errorMessage;\n  }\n\n  isRequired(): boolean {\n    const { isRequired, formValidator } = this.props;\n    return !!(\n      isRequired ||\n      (formValidator &&\n        formValidator.validators.find((v) => (v as { validatorId?: string }).validatorId === 'required'))\n    );\n  }\n\n  renderTooltip(): React.ReactNode {\n    const { errorMessage, formValidator } = this.props;\n    const { isHovered, isFocused } = this.state;\n    return (\n      <Tooltip\n        variant='error'\n        message={formValidator ? formValidator.getCurrentErrorMessage() : errorMessage || ''}\n        isVisible={this.hasError() && (isFocused || isHovered)}\n      />\n    );\n  }\n\n  onChange(newVal: unknown): void {\n    const { onChange, formValidator } = this.props;\n    if (formValidator) {\n      let extractedVal = newVal;\n      if (newVal && (newVal as React.ChangeEvent<HTMLInputElement>).target) {\n        extractedVal = (newVal as React.ChangeEvent<HTMLInputElement>).target.value;\n      }\n      formValidator.set(extractedVal);\n    }\n    if (onChange) onChange(newVal);\n  }\n}\n","import { useFormValidatorGroupInstance } from '@ahrowe/form-validation';\nimport type { FormGroup, FormValidatorGroupOptions, InferGroupValues } from '@ahrowe/form-validation';\nimport FormValidatorGroupWithForm from './FormValidatorGroupWithForm';\n\n/**\n * @ahrowe/ui's flavour of useFormValidatorGroup: the group it creates has\n * `.Form` attached and defaults `onSubmitError` to an error toast (see\n * FormValidatorGroupWithForm). Reuses the core package's reactive wiring\n * (useFormValidatorGroupInstance) so this and the pure hook share one\n * implementation of \"subscribe a function component to a group\".\n */\nexport function useFormValidatorGroup<TGroup extends FormGroup = FormGroup, TInitial = InferGroupValues<TGroup>>(\n  groupDef: TGroup,\n  { onSubmit, onValidate, ...options }: FormValidatorGroupOptions<TGroup, TInitial> = {},\n): FormValidatorGroupWithForm<TGroup, TInitial> {\n  return useFormValidatorGroupInstance(\n    () => new FormValidatorGroupWithForm(groupDef, null, options),\n    onSubmit,\n    onValidate,\n  );\n}\n"],"mappings":"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"}