@leabankab/component-library 8.10.0 → 8.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/dist/component-library.es.js +1572 -1565
- package/dist/component-library.es.js.map +1 -1
- package/dist/component-library.umd.js +2 -2
- package/dist/component-library.umd.js.map +1 -1
- package/dist/components/animations/index.d.ts +5 -2
- package/dist/components/animations/loader/loader.d.ts +43 -0
- package/dist/components/animations/pulse-animation/pulse-animation.d.ts +21 -0
- package/dist/components/animations/{illustrations → status-animation}/status-animation.d.ts +7 -6
- package/dist/components/feedback/alert-parts.d.ts +1 -1
- package/dist/index.css +1 -1
- package/dist/theme/tokens/text-styles.d.ts +1 -1
- package/package.json +1 -1
- package/dist/components/animations/icons/index.d.ts +0 -1
- package/dist/components/animations/illustrations/index.d.ts +0 -3
- package/dist/components/animations/illustrations/pulse-animation.d.ts +0 -16
- /package/dist/components/animations/{illustrations → check-animation}/check-animation.d.ts +0 -0
- /package/dist/components/animations/{icons → status-icon}/status-icon.d.ts +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component-library.es.js","names":[],"sources":["../src/components/animations/animations.module.css","../src/components/animations/shared.tsx","../src/components/animations/spinner.tsx","../src/i18n/languages.ts","../src/i18n/language-context.tsx","../src/i18n/create-translation.ts","../src/components/animations/use-animation-labels.ts","../src/components/animations/icons/status-icon.tsx","../src/theme/tokens/colors.ts","../src/theme/tokens/palette.ts","../src/components/animations/illustrations/check-animation.tsx","../src/components/animations/illustrations/pulse-animation.tsx","../src/components/animations/illustrations/status-animation.tsx","../src/utils/merge-class-name.ts","../src/components/buttons/action-button/action-button.module.css","../src/components/buttons/button.module.css","../src/components/buttons/button-base.tsx","../src/components/buttons/action-button/action-button.tsx","../src/components/buttons/button/button.tsx","../src/components/feedback/use-close-label.ts","../src/components/buttons/icon-button/icon-button.tsx","../src/components/buttons/close-button/close-button.tsx","../src/utils/download-file.ts","../src/components/buttons/download-trigger/use-download-labels.ts","../src/components/buttons/download-trigger/download-trigger.tsx","../src/components/buttons/skip-link/skip-link.module.css","../src/components/buttons/skip-link/skip-link.tsx","../src/theme/tokens/font-sizes.ts","../src/components/typography/text/text.module.css","../src/components/typography/text/text.tsx","../src/components/data-display/data-field/data-field.module.css","../src/components/data-display/data-field/data-field.tsx","../src/components/data-display/card/card.module.css","../src/components/data-display/card/card.tsx","../src/components/data-display/chip/chip.module.css","../src/components/data-display/chip/use-chip-labels.ts","../src/components/data-display/chip/chip.tsx","../src/components/data-display/tooltip/tooltip.module.css","../src/components/data-display/tooltip/tooltip.tsx","../src/components/data-display/avatar/avatar.module.css","../src/components/data-display/avatar/avatar.tsx","../src/components/data-display/star-rating/star-rating.module.css","../src/components/data-display/star-rating/use-star-rating-labels.ts","../src/components/data-display/star-rating/star-rating.tsx","../src/components/data-display/table/table.module.css","../src/components/data-display/table/table.tsx","../src/components/data-display/rich-text/rich-text.module.css","../src/components/data-display/rich-text/rich-text.tsx","../src/components/data-display/featured-image/featured-image.module.css","../src/components/data-display/featured-image/featured-image.tsx","../src/components/data-display/video-player/video-player.module.css","../src/components/data-display/video-player/use-video-player-labels.ts","../src/components/data-display/video-player/video-player.tsx","../src/components/data-display/link-card/arrow-circle.module.css","../src/components/data-display/link-card/arrow-circle.tsx","../src/components/data-display/link-card/link-card.module.css","../src/components/data-display/link-card/link-card.tsx","../src/components/data-display/media-card/media-card.module.css","../src/components/data-display/website-card.ts","../src/components/data-display/media-card/media-card.tsx","../src/components/data-display/service-card/service-card.module.css","../src/components/data-display/service-card/service-card.tsx","../src/components/data-display/testimonial-card/testimonial-card.module.css","../src/components/data-display/testimonial-card/testimonial-card.tsx","../src/components/data-display/article-card/article-card.module.css","../src/components/data-display/article-card/article-card.tsx","../src/components/data-display/partner-row/partner-row.module.css","../src/components/data-display/partner-row/partner-row.tsx","../src/components/data-display/brand-divider/brand-divider.module.css","../src/components/data-display/brand-divider/brand-divider.tsx","../src/components/feedback/alert.module.css","../src/components/feedback/alert-parts.tsx","../src/components/feedback/alert/alert.tsx","../src/components/feedback/dialog/dialog.module.css","../src/components/feedback/dialog/dialog.tsx","../src/components/feedback/drawer/drawer.module.css","../src/components/feedback/drawer/drawer.tsx","../src/components/layout/container/container.module.css","../src/components/layout/container/container.tsx","../src/components/feedback/global-alert/global-alert.tsx","../src/components/feedback/dialog/simple-dialog.tsx","../src/components/feedback/drawer/simple-drawer.tsx","../src/components/feedback/toast/toast.module.css","../src/components/feedback/toast/toast.tsx","../src/components/feedback/skeleton/skeleton.module.css","../src/components/feedback/skeleton/skeleton.tsx","../src/components/form/amount-slider/amount-slider.module.css","../src/components/form/slider-parts.module.css","../src/components/form/slider-parts.tsx","../src/components/form/amount-slider/amount-slider.tsx","../src/components/form/combo-field.module.css","../src/components/form/field-parts.module.css","../src/components/form/use-tooltip-trigger-labels.ts","../src/components/form/field-parts.tsx","../src/components/form/input.module.css","../src/components/form/option-list.module.css","../src/components/form/use-autocomplete-labels.ts","../src/components/form/use-blur-outside.ts","../src/components/form/combo-field.tsx","../src/components/form/autocomplete/autocomplete.tsx","../src/components/form/checkbox.module.css","../src/components/form/checkbox-box.tsx","../src/components/form/choice-row.module.css","../src/components/form/checkbox/checkbox.tsx","../src/validation/types.ts","../src/validation/national-id.ts","../src/validation/mobile-patterns.ts","../src/validation/phone.ts","../src/validation/modulo-97.ts","../src/validation/bank-account.ts","../src/validation/reference-number.ts","../src/validation/email.ts","../src/components/typography/link/link.module.css","../src/components/typography/link/link.tsx","../src/components/typography/lists/lists.module.css","../src/components/typography/lists/lists.tsx","../src/components/form/email-input/email-input.module.css","../src/components/form/email-input/email-input.tsx","../src/components/form/input/input.tsx","../src/components/form/select.module.css","../src/components/form/select-field.tsx","../src/components/form/multi-select/multi-select.tsx","../src/hooks/country/countries.ts","../src/components/form/number-input/number-input-unit.ts","../src/components/form/number-input/number-input.module.css","../src/components/form/number-input/number-input.tsx","../src/components/form/phone-number-input/countries.ts","../src/components/form/phone-number-input/phone-number-input.module.css","../src/components/form/phone-number-input/phone-number-input.tsx","../src/components/form/radio.module.css","../src/components/form/radio-circle.tsx","../src/components/form/radio-group-root.module.css","../src/components/form/radio-group-root.tsx","../src/components/form/radio/radio.tsx","../src/components/form/radio-card/radio-card.module.css","../src/components/form/radio-card/radio-card.tsx","../src/components/form/select/select.tsx","../src/components/form/slider/slider.module.css","../src/components/form/slider/slider.tsx","../src/components/form/switch/switch.module.css","../src/components/form/switch/switch.tsx","../src/components/layout/grid/grid.module.css","../src/components/layout/responsive.ts","../src/components/layout/grid/grid.tsx","../src/components/layout/stack/stack.module.css","../src/components/layout/stack/stack.tsx","../src/components/navigation/form-stepper/form-stepper.module.css","../src/components/navigation/progress-circle/progress-circle.module.css","../src/components/navigation/progress-circle/progress-circle.tsx","../src/components/navigation/use-stepper-labels.ts","../src/components/navigation/form-stepper/form-stepper.tsx","../src/components/navigation/linear-form-stepper/linear-form-stepper.module.css","../src/components/navigation/linear-form-stepper/linear-form-stepper.tsx","../src/components/navigation/linear-form-stepper/step-progress.tsx","../src/components/navigation/tabs/tabs.module.css","../src/components/navigation/tabs/tabs.tsx","../src/components/navigation/breadcrumbs/breadcrumbs.module.css","../src/components/navigation/breadcrumbs/use-breadcrumbs-labels.ts","../src/components/navigation/breadcrumbs/breadcrumbs.tsx","../src/components/navigation/pagination/pagination.module.css","../src/components/navigation/use-pagination-labels.ts","../src/components/navigation/pagination/pagination.tsx","../src/components/navigation/pagination-dots/pagination-dots.module.css","../src/components/navigation/pagination-dots/pagination-dots.tsx","../src/components/navigation/app-header/app-header.module.css","../src/components/navigation/app-header/app-header.tsx","../src/components/surfaces/accordion/accordion.module.css","../src/components/surfaces/accordion/accordion.tsx","../src/components/surfaces/accordion/simple-accordion.tsx","../src/components/surfaces/panel/panel.module.css","../src/components/surfaces/panel/panel.tsx","../src/hooks/country/country-context.tsx","../src/hooks/use-format-currency.ts","../src/hooks/use-format-date.ts","../src/hooks/use-format-number.ts","../src/hooks/validation/use-validation.ts","../src/provider/lea-provider.tsx","../src/theme/tokens/breakpoints.ts","../src/utils/format-decimal.ts","../src/utils/format-decimal-string.ts","../src/utils/format-percentage.ts","../src/utils/parse-expiration-date.ts"],"sourcesContent":[".StatusIcon,\n.Check,\n.Pulse {\n --animation-base: var(--lea-palette-primary-base);\n --animation-contrast: var(--lea-palette-primary-contrast);\n --animation-shadow: rgba(255, 255, 255, 0.1) 0 1px 1px 0 inset,\n rgba(50, 50, 93, 0.25) 0 30px 60px -20px,\n rgba(0, 0, 0, 0.25) 0 18px 36px -18px;\n}\n\n.StatusIcon[data-color=\"tertiary\"],\n.Check[data-color=\"tertiary\"] {\n --animation-base: var(--lea-palette-tertiary-base);\n --animation-contrast: var(--lea-palette-tertiary-contrast);\n}\n\n.Badge {\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: var(--lea-radius-full);\n background-color: var(--animation-base);\n color: var(--animation-contrast);\n}\n\n.Spinner {\n display: inline-block;\n animation: animation-spin 1.4s linear infinite;\n}\n\n.SpinnerSvg {\n display: block;\n}\n\n@keyframes animation-spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n/* StatusIcon */\n\n.StatusIcon {\n position: relative;\n display: inline-flex;\n overflow: visible;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n color: var(--animation-base);\n}\n\n.State {\n position: absolute;\n display: flex;\n align-items: center;\n justify-content: center;\n inset: 0;\n}\n\n.Ring {\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n border-style: solid;\n border-color: var(--lea-palette-border-base);\n border-radius: var(--lea-radius-full);\n}\n\n/* CheckAnimation */\n\n.Check {\n position: relative;\n display: flex;\n overflow: visible;\n align-items: center;\n justify-content: center;\n}\n\n.Halo {\n position: absolute;\n border-radius: var(--lea-radius-full);\n animation: check-halo 2.6s var(--lea-easing-standard) infinite;\n background-color: var(--animation-base);\n opacity: 0.25;\n}\n\n.CheckBadge {\n animation: check-breathe 2.6s var(--lea-easing-standard) infinite;\n box-shadow: var(--animation-shadow);\n}\n\n@keyframes check-halo {\n 0%,\n 100% {\n opacity: 0.28;\n transform: scale(1);\n }\n\n 50% {\n opacity: 0;\n transform: scale(1.45);\n }\n}\n\n@keyframes check-breathe {\n 0%,\n 100% {\n transform: scale(1);\n }\n\n 50% {\n transform: scale(1.04);\n }\n}\n\n/* PulseAnimation */\n\n.Pulse {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.PulseRing,\n.PulseCentre {\n position: absolute;\n border-radius: var(--lea-radius-full);\n background-color: var(--animation-base);\n}\n\n.PulseRing {\n animation: pulse-ring 2s var(--lea-easing-standard) infinite;\n opacity: 0.5;\n}\n\n.PulseCentre {\n box-shadow: var(--animation-shadow);\n}\n\n/* Positioned, so it paints above the centre circle whether or not it turns. */\n.PulseSpinner {\n position: relative;\n color: var(--animation-contrast);\n}\n\n@keyframes pulse-ring {\n 0% {\n opacity: 0.6;\n transform: scale(0.4);\n }\n\n 100% {\n opacity: 0;\n transform: scale(1.6);\n }\n}\n\n/* StatusAnimation */\n\n.Status {\n position: relative;\n}\n\n.Layer {\n position: absolute;\n display: flex;\n align-items: center;\n justify-content: center;\n inset: 0;\n}\n\n.SrOnly {\n position: absolute;\n overflow: hidden;\n width: 1px;\n height: 1px;\n padding: 0;\n border: 0;\n margin: -1px;\n clip-path: inset(50%);\n white-space: nowrap;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Halo {\n animation: none;\n opacity: 0;\n }\n\n .CheckBadge,\n .PulseRing {\n animation: none;\n }\n}\n","import { Check } from \"lucide-react\";\nimport { motion } from \"motion/react\";\nimport { type ReactNode } from \"react\";\n\nimport type { ColorRoles } from \"../color-roles\";\nimport styles from \"./animations.module.css\";\n\nexport type AnimationSize = \"sm\" | \"lg\";\n\nexport type AnimationColor = ColorRoles<\"primary\" | \"tertiary\">;\n\nexport type ConfettiParticle = {\n id: number;\n x: number;\n y: number;\n dotSize: number;\n round: boolean;\n colour?: string;\n spin?: number;\n};\n\ntype BuildParticlesOptions = {\n count: number;\n burst: number;\n distanceAt: (index: number) => number;\n dotSizeAt: (index: number) => number;\n colourAt?: (index: number) => string | undefined;\n withSpin?: boolean;\n};\n\nexport const buildParticles = ({\n count,\n burst,\n distanceAt,\n dotSizeAt,\n colourAt,\n withSpin = false,\n}: BuildParticlesOptions): ConfettiParticle[] =>\n Array.from({ length: count }, (_, i) => {\n const angle = (i / count) * Math.PI * 2;\n const distance = burst * distanceAt(i);\n return {\n id: i,\n x: Math.cos(angle) * distance,\n y: Math.sin(angle) * distance,\n dotSize: dotSizeAt(i),\n round: i % 2 === 0,\n ...(colourAt ? { colour: colourAt(i) } : {}),\n ...(withSpin ? { spin: (i % 2 === 0 ? 1 : -1) * 120 } : {}),\n };\n });\n\ntype ConfettiParticlesProps = {\n particles: ConfettiParticle[];\n /** Illustration confetti drifts down slightly and spins. */\n variant?: \"illustration\" | \"icon\";\n delay?: number;\n duration?: number;\n /** When set, particles use currentColor instead of per-particle colours. */\n useCurrentColor?: boolean;\n};\n\nexport const ConfettiParticles = ({\n particles,\n variant = \"illustration\",\n delay = variant === \"illustration\" ? 0.18 : 0.1,\n duration = variant === \"illustration\" ? 1.1 : 0.8,\n useCurrentColor = variant === \"icon\",\n}: ConfettiParticlesProps) =>\n particles.map((p) => (\n <motion.span\n key={p.id}\n initial={{ x: 0, y: 0, scale: 0, opacity: 0 }}\n animate={\n variant === \"illustration\"\n ? {\n x: [0, p.x],\n y: [0, p.y, p.y + 10],\n scale: [0, 1, 0.7],\n opacity: [0, 1, 0],\n rotate: p.spin != null ? [0, p.spin] : undefined,\n }\n : {\n x: [0, p.x],\n y: [0, p.y],\n scale: [0, 1, 0.6],\n opacity: [0, 1, 0],\n }\n }\n transition={{\n duration,\n ease: \"easeOut\",\n times: variant === \"illustration\" ? [0, 0.25, 1] : [0, 0.3, 1],\n delay,\n }}\n style={{\n position: \"absolute\",\n width: p.dotSize,\n height: p.dotSize,\n borderRadius: p.round ? \"50%\" : 1,\n backgroundColor: useCurrentColor ? \"currentColor\" : p.colour,\n pointerEvents: \"none\",\n }}\n />\n ));\n\ntype AnimationBadgeProps = {\n /** Diameter in px. */\n size: number;\n iconSize: number;\n className?: string;\n children?: ReactNode;\n};\n\nexport const AnimationBadge = ({\n size,\n iconSize,\n className,\n children,\n}: AnimationBadgeProps) => (\n <span\n className={[styles.Badge, className].filter(Boolean).join(\" \")}\n style={{ width: size, height: size }}\n >\n {children ?? <Check size={iconSize} strokeWidth={3} aria-hidden />}\n </span>\n);\n","import styles from \"./animations.module.css\";\n\ntype SpinnerProps = {\n /** Diameter in px. */\n size: number;\n /** Stroke width, in the units of a 44px circle. */\n thickness?: number;\n /**\n * Accessible name. With one, the spinner is a `progressbar`; without, it is\n * hidden from assistive tech and its parent says what is loading.\n */\n label?: string;\n className?: string;\n};\n\nconst BOX = 44;\n\n/**\n * An indeterminate spinner: an arc in the current colour that turns at a\n * constant speed. Internal to the animations.\n */\nexport const Spinner = ({\n size,\n thickness = 3.6,\n label,\n className,\n}: SpinnerProps) => (\n <span\n {...(label != null\n ? { role: \"progressbar\", \"aria-label\": label }\n : { \"aria-hidden\": true })}\n className={[styles.Spinner, className].filter(Boolean).join(\" \")}\n style={{ width: size, height: size }}\n >\n <svg\n className={styles.SpinnerSvg}\n viewBox={`${BOX / 2} ${BOX / 2} ${BOX} ${BOX}`}\n >\n <circle\n cx={BOX}\n cy={BOX}\n r={(BOX - thickness) / 2}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={thickness}\n strokeDasharray=\"80 200\"\n />\n </svg>\n </span>\n);\n","/**\n * Languages supported by the component library's built-in translations. The\n * consumer selects one via `LeaProvider`'s `language` prop; components read it\n * with `useLanguage` (usually through `createTranslation`).\n */\nexport const LANGUAGES = [\"no\", \"se\", \"fi\", \"es\"] as const;\n\nexport type Language = (typeof LANGUAGES)[number];\n\n/** Fallback language used when no provider is present or a key is missing. */\nexport const DEFAULT_LANGUAGE = \"no\" satisfies Language;\n","\"use client\";\n\nimport { createContext, useContext } from \"react\";\nimport { DEFAULT_LANGUAGE, type Language } from \"./languages\";\n\n/**\n * Holds the currently selected language. Provided by `LeaProvider` and read by\n * components (typically via `createTranslation`). Defaults to `DEFAULT_LANGUAGE`\n * so components still render sensibly when used without the provider.\n */\nexport const LanguageContext = createContext<Language>(DEFAULT_LANGUAGE);\n\n/** Returns the language from the nearest `LeaProvider`, or `DEFAULT_LANGUAGE`. */\nexport const useLanguage = (): Language => useContext(LanguageContext);\n","\"use client\";\n\nimport { useLanguage } from \"./language-context\";\nimport { DEFAULT_LANGUAGE, type Language } from \"./languages\";\n\n/**\n * Translations for a single string, keyed by language. The `no` key is required\n * so the `DEFAULT_LANGUAGE` fallback always resolves; the others are optional.\n */\nexport type TranslationMap = { no: string } & Partial<Record<Language, string>>;\n\n/** A named set of translatable strings, each keyed by language. */\nexport type TranslationSchema = Record<string, TranslationMap>;\n\n/**\n * Creates a hook that returns co-located strings for the current language,\n * each falling back to `no`. Define the schema at module scope and call the\n * returned hook inside a component:\n *\n * ```tsx\n * const useLabels = createTranslation({\n * increase: { no: \"Øk\", se: \"Öka\", fi: \"Lisää\", es: \"Aumentar\" },\n * decrease: { no: \"Reduser\", se: \"Minska\", fi: \"Vähennä\", es: \"Disminuir\" },\n * });\n *\n * function Stepper() {\n * const t = useLabels();\n * return <button aria-label={t.increase} />;\n * }\n * ```\n */\nexport function createTranslation<T extends TranslationSchema>(schema: T) {\n return function useTranslation(): Record<keyof T, string> {\n const language = useLanguage();\n const result = {} as Record<keyof T, string>;\n (Object.keys(schema) as (keyof T)[]).forEach((key) => {\n const map = schema[key];\n result[key] = map[language] ?? map[DEFAULT_LANGUAGE];\n });\n return result;\n };\n}\n","import { createTranslation } from \"../../i18n\";\n\nexport const useAnimationLabels = createTranslation({\n pending: {\n no: \"Venter\",\n se: \"Väntar\",\n fi: \"Odottaa\",\n es: \"Esperando\",\n },\n loading: {\n no: \"Laster\",\n se: \"Laddar\",\n fi: \"Ladataan\",\n es: \"Cargando\",\n },\n done: { no: \"Ferdig\", se: \"Klart\", fi: \"Valmis\", es: \"Completado\" },\n confirmed: {\n no: \"Bekreftet\",\n se: \"Bekräftat\",\n fi: \"Vahvistettu\",\n es: \"Confirmado\",\n },\n});\n","\"use client\";\n\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\";\nimport { useMemo } from \"react\";\n\nimport styles from \"../animations.module.css\";\nimport {\n AnimationBadge,\n type AnimationColor,\n buildParticles,\n ConfettiParticles,\n} from \"../shared\";\nimport { Spinner } from \"../spinner\";\nimport { useAnimationLabels } from \"../use-animation-labels\";\n\ntype StatusIconStatus = \"pending\" | \"loading\" | \"done\";\n\ntype StatusIconProps = {\n status: StatusIconStatus;\n /** Rendered size in px. */\n size?: number;\n /**\n * Colour of the spinner and of the filled check.\n * @default \"primary\"\n */\n color?: AnimationColor;\n /**\n * Accessible name; defaults to the translated label for the current status in\n * the `LeaProvider` language.\n */\n label?: string;\n};\n\nconst CONFETTI_COUNT = 8;\n\n/**\n * A small, inline status icon for step / progress lists. Transitions through\n * `pending` (empty ring) → `loading` (spinner) → `done` (filled check with a\n * one-time confetti pop). One-shot, not infinite. Respects\n * `prefers-reduced-motion`.\n */\nexport const StatusIcon = ({\n status,\n size = 24,\n color = \"primary\",\n label,\n}: StatusIconProps) => {\n const prefersReducedMotion = useReducedMotion();\n const labels = useAnimationLabels();\n\n const ring = Math.max(2, Math.round(size * 0.09));\n const thickness = (ring * 44) / size;\n const checkSize = Math.round(size * 0.6);\n const burst = size;\n\n const particles = useMemo(\n () =>\n buildParticles({\n count: CONFETTI_COUNT,\n burst,\n distanceAt: (i) => 0.7 + (i % 2) * 0.25,\n dotSizeAt: (i) => Math.max(2, Math.round(size * 0.1)) + (i % 2),\n }),\n [burst, size],\n );\n\n const fadeDuration = prefersReducedMotion ? 0 : 0.2;\n\n return (\n <span\n role=\"img\"\n aria-label={label ?? labels[status]}\n className={styles.StatusIcon}\n data-color={color}\n style={{ width: size, height: size }}\n >\n <AnimatePresence mode=\"wait\">\n {status === \"pending\" && (\n <motion.span\n key=\"pending\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: fadeDuration }}\n className={styles.State}\n >\n <span className={styles.Ring} style={{ borderWidth: ring }} />\n </motion.span>\n )}\n\n {status === \"loading\" && (\n <motion.span\n key=\"loading\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: fadeDuration }}\n className={styles.State}\n >\n <Spinner size={size} thickness={thickness} />\n </motion.span>\n )}\n\n {status === \"done\" && (\n <motion.span\n key=\"done\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: fadeDuration }}\n className={styles.State}\n >\n <motion.span\n initial={prefersReducedMotion ? false : { scale: 0 }}\n animate={prefersReducedMotion ? {} : { scale: [0, 1.18, 1] }}\n transition={{\n duration: 0.4,\n ease: \"easeOut\",\n times: [0, 0.6, 1],\n }}\n style={{ display: \"flex\" }}\n >\n <AnimationBadge size={size} iconSize={checkSize} />\n </motion.span>\n </motion.span>\n )}\n </AnimatePresence>\n\n {!prefersReducedMotion && status === \"done\" && (\n <ConfettiParticles particles={particles} variant=\"icon\" />\n )}\n </span>\n );\n};\n","export const colors = {\n purple: {\n 50: \"#FAF7FB\",\n 100: \"#E2D8E4\",\n 200: \"#C5B1CA\",\n 300: \"#C29AC2\",\n 400: \"#9E6DA8\",\n 500: \"#855888\",\n 600: \"#70427B\",\n 700: \"#5A3463\",\n 800: \"#422349\",\n 900: \"#2E1834\",\n 950: \"#1A0E1E\",\n },\n teal: {\n 50: \"#F0FCFA\",\n 100: \"#DAF6F1\",\n 200: \"#CEF3ED\",\n 300: \"#08C2A7\",\n 400: \"#46D1BD\",\n 500: \"#06A892\",\n 600: \"#058F7D\",\n 700: \"#047568\",\n 800: \"#035C53\",\n 900: \"#02433E\",\n 950: \"#012A28\",\n },\n blue: {\n 50: \"#EFF6FF\",\n 100: \"#CBE5FD\",\n 200: \"#93CBFC\",\n 300: \"#4DA8F7\",\n 400: \"#057EF2\",\n 500: \"#0468C9\",\n 600: \"#0354A3\",\n 700: \"#02417D\",\n 800: \"#012E57\",\n 900: \"#011B31\",\n 950: \"#000E19\",\n },\n orange: {\n 50: \"#FFF5F2\",\n 100: \"#FFDFD7\",\n 200: \"#FFC4B5\",\n 300: \"#FF9E87\",\n 400: \"#FE653E\",\n 500: \"#E54E28\",\n 600: \"#C43E1C\",\n 700: \"#A33218\",\n 800: \"#822814\",\n 900: \"#611E0F\",\n 950: \"#401408\",\n },\n amber: {\n 50: \"#FFF9EB\",\n 100: \"#FFEFC7\",\n 200: \"#FFE299\",\n 300: \"#FFCF5C\",\n 400: \"#F7A800\",\n 500: \"#D98F00\",\n 600: \"#B37400\",\n 700: \"#8C5A00\",\n 800: \"#6B4500\",\n 900: \"#4A3000\",\n 950: \"#2E1D00\",\n },\n red: {\n 50: \"#FEF2F2\",\n 100: \"#FCDCDC\",\n 200: \"#F9BBBB\",\n 300: \"#F28888\",\n 400: \"#E85555\",\n 500: \"#D32F2F\",\n 600: \"#AD2121\",\n 700: \"#8A1919\",\n 800: \"#6F1313\",\n 900: \"#4D0E0E\",\n 950: \"#2E0808\",\n },\n beige: {\n 50: \"#FAF7F5\",\n 100: \"#F0E9E5\",\n 200: \"#E0D4CD\",\n 300: \"#C9B8AD\",\n 400: \"#B09A8C\",\n 500: \"#977C6B\",\n 600: \"#7E6556\",\n 700: \"#654F42\",\n 800: \"#4C3A32\",\n 900: \"#332722\",\n 950: \"#1A1411\",\n },\n neutral: {\n 50: \"#F7F7F6\",\n 100: \"#EBEBEB\",\n 200: \"#DBDBDB\",\n 300: \"#C4C4C4\",\n 400: \"#A2A2A2\",\n 500: \"#757575\",\n 600: \"#5E5E5E\",\n 700: \"#454545\",\n 800: \"#2E2E2E\",\n 900: \"#1A1A1A\",\n 950: \"#0F1010\",\n },\n white: \"#FFFFFF\",\n black: \"#0F1010\",\n} as const;\n","import { Palette } from \"../types\";\nimport { colors } from \"./colors\";\n\nconst { purple, teal, blue, amber, red, neutral, white, beige } = colors;\n\n/** A scale colour at an opacity, written `rgba(15, 16, 16, 0.5)`. */\nconst alpha = (hex: string, opacity: number) => {\n const [r, g, b] = [1, 3, 5].map((i) => parseInt(hex.slice(i, i + 2), 16));\n\n return `rgba(${r}, ${g}, ${b}, ${opacity})`;\n};\n\n/**\n * The website's hover colours, kept at its exact values rather than the\n * nearest step of the purple scale (purple 100 `#E2D8E4`, 950 `#1A0E1E` and\n * 300 `#C29AC2` are close, not the same). The Figma variables `bg/hover`,\n * `bg/contrast-hover` and `text/contrast-hover` hold the same raw values.\n */\nconst website = {\n hover: \"#E3D8DA\",\n contrastHover: \"#241B26\",\n textContrastHover: \"#CD86D6\",\n} as const;\n\nexport const palette: Palette = {\n bg: {\n base: white,\n subtle: neutral[50],\n muted: beige[100],\n contrast: neutral[950],\n accent: purple[100],\n overlay: alpha(neutral[950], 0.5),\n hover: website.hover,\n \"contrast-hover\": website.contrastHover,\n \"contrast-accent\": purple[800],\n },\n text: {\n base: neutral[950],\n subtle: neutral[500],\n muted: neutral[400],\n accent: purple[600],\n contrast: white,\n \"contrast-hover\": website.textContrastHover,\n },\n border: {\n base: neutral[200],\n subtle: neutral[100],\n strong: neutral[950],\n accent: purple[600],\n contrast: white,\n },\n primary: {\n base: purple[600],\n hover: purple[700],\n subtle: purple[100],\n contrast: white,\n },\n secondary: {\n base: neutral[950],\n hover: neutral[800],\n subtle: neutral[100],\n contrast: white,\n },\n tertiary: {\n base: teal[300],\n hover: teal[400],\n subtle: teal[100],\n contrast: white,\n },\n status: {\n error: {\n base: red[500],\n subtle: red[100],\n },\n warning: {\n base: amber[400],\n subtle: amber[100],\n },\n info: {\n base: blue[400],\n subtle: blue[100],\n },\n success: {\n base: teal[400],\n subtle: teal[100],\n },\n },\n} as const;\n","\"use client\";\n\nimport { motion, useReducedMotion } from \"motion/react\";\nimport { useMemo } from \"react\";\n\nimport { colors } from \"../../../theme/tokens/colors\";\nimport { palette } from \"../../../theme/tokens/palette\";\nimport styles from \"../animations.module.css\";\nimport {\n AnimationBadge,\n type AnimationColor,\n type AnimationSize,\n buildParticles,\n ConfettiParticles,\n} from \"../shared\";\nimport { useAnimationLabels } from \"../use-animation-labels\";\n\ntype CheckAnimationProps = {\n /**\n * `\"sm\"` is 90px, `\"lg\"` 150px.\n * @default \"lg\"\n */\n size?: AnimationSize;\n /**\n * Colour of the badge and its halo.\n * @default \"primary\"\n */\n color?: AnimationColor;\n /**\n * Accessible name for the illustration; defaults to the translated word for\n * \"confirmed\" in the `LeaProvider` language.\n */\n label?: string;\n};\n\n// Shared square footprint with PulseAnimation: `box` is the rendered square and\n// `badge` matches the pulse centre circle, so both animations line up at a given\n// size. The confetti is free to overflow the square.\nconst sizeMap = {\n sm: { box: 90, badge: 60, icon: 30, burst: 60 },\n lg: { box: 150, badge: 100, icon: 50, burst: 100 },\n};\n\nconst CONFETTI_COUNT = 14;\n\n// The four brand colours. Blue and orange have no semantic token of their own;\n// confetti is not a status, so it does not read `status.info` or\n// `status.warning`.\nconst confettiColours = [\n palette.primary.base,\n palette.tertiary.base,\n colors.blue[400],\n colors.orange[400],\n];\n\nexport const CheckAnimation = ({\n size = \"lg\",\n color = \"primary\",\n label,\n}: CheckAnimationProps) => {\n const prefersReducedMotion = useReducedMotion();\n const labels = useAnimationLabels();\n const { box, badge, icon, burst } = sizeMap[size];\n\n const particles = useMemo(\n () =>\n buildParticles({\n count: CONFETTI_COUNT,\n burst,\n distanceAt: (i) => 0.6 + (i % 3) * 0.15,\n dotSizeAt: (i) => 5 + (i % 3) * 2,\n colourAt: (i) => confettiColours[i % confettiColours.length],\n withSpin: true,\n }),\n [burst],\n );\n\n return (\n <div\n role=\"img\"\n aria-label={label ?? labels.confirmed}\n className={styles.Check}\n data-color={color}\n style={{ width: box, height: box }}\n >\n <span className={styles.Halo} style={{ width: badge, height: badge }} />\n\n {!prefersReducedMotion && (\n <ConfettiParticles particles={particles} variant=\"illustration\" />\n )}\n\n <motion.div\n initial={prefersReducedMotion ? false : { scale: 0, rotate: -25 }}\n animate={\n prefersReducedMotion ? {} : { scale: [0, 1.12, 1], rotate: [-25, 0] }\n }\n transition={{\n duration: 0.55,\n ease: \"easeOut\",\n times: [0, 0.65, 1],\n }}\n style={{ display: \"flex\" }}\n >\n <AnimationBadge\n size={badge}\n iconSize={icon}\n className={styles.CheckBadge}\n />\n </motion.div>\n </div>\n );\n};\n","\"use client\";\n\nimport styles from \"../animations.module.css\";\nimport { type AnimationSize } from \"../shared\";\nimport { Spinner } from \"../spinner\";\nimport { useAnimationLabels } from \"../use-animation-labels\";\n\ntype PulseAnimationProps = {\n /**\n * `\"sm\"` is 90px, `\"lg\"` 150px.\n * @default \"lg\"\n */\n size?: AnimationSize;\n /**\n * Accessible name for the spinner; defaults to the translated word for\n * \"loading\" in the `LeaProvider` language. `StatusAnimation` hides the pulse\n * from assistive tech and announces its own status text instead.\n */\n label?: string;\n};\n\n// Ratios are kept constant across variants so the pulse expands the same\n// amount relative to the centre circle, and the spinner stays proportional.\nconst sizeMap = {\n sm: { container: 90, circle: 60, spinner: 28 },\n lg: { container: 150, circle: 100, spinner: 48 },\n};\n\nexport const PulseAnimation = ({ size = \"lg\", label }: PulseAnimationProps) => {\n const { container, circle, spinner } = sizeMap[size];\n const labels = useAnimationLabels();\n\n const circleSize = { width: circle, height: circle };\n\n return (\n <div\n className={styles.Pulse}\n style={{ width: container, height: container }}\n >\n <span className={styles.PulseRing} style={circleSize} />\n <span\n className={styles.PulseRing}\n style={{ ...circleSize, animationDelay: \"0.4s\" }}\n />\n <span className={styles.PulseCentre} style={circleSize} />\n <Spinner\n size={spinner}\n label={label ?? labels.loading}\n className={styles.PulseSpinner}\n />\n </div>\n );\n};\n","\"use client\";\n\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\";\nimport { useEffect, useRef, useState } from \"react\";\n\nimport styles from \"../animations.module.css\";\nimport { type AnimationColor, type AnimationSize } from \"../shared\";\nimport { useAnimationLabels } from \"../use-animation-labels\";\nimport { CheckAnimation } from \"./check-animation\";\nimport { PulseAnimation } from \"./pulse-animation\";\n\ntype StatusAnimationProps = {\n /** While `true`, shows the pulse; when it flips to `false`, transitions to the check. */\n loading: boolean;\n /**\n * `\"sm\"` is 90px, `\"lg\"` 150px.\n * @default \"lg\"\n */\n size?: AnimationSize;\n /**\n * Colour of the resulting check (and its halo).\n * @default \"primary\"\n */\n color?: AnimationColor;\n /**\n * Minimum time (ms) the pulse stays visible once mounted in the loading\n * state, so a very fast response doesn't make the spinner flash. The check\n * appears as soon as `loading` is `false` *and* this minimum has elapsed.\n */\n minLoadingMs?: number;\n /**\n * Visually-hidden status text announced while loading. Defaults to the\n * translated word for \"loading\" in the `LeaProvider` language.\n */\n loadingLabel?: string;\n /**\n * Visually-hidden status text announced once done. Defaults to the translated\n * word for \"done\".\n */\n doneLabel?: string;\n};\n\nconst boxSize = { sm: 90, lg: 150 };\n\n/**\n * A controlled loading → success indicator: shows {@link PulseAnimation} while\n * `loading`, then cross-fades into {@link CheckAnimation} (firing its one-time\n * confetti) once `loading` becomes `false`. Announces state changes politely\n * and respects `prefers-reduced-motion`.\n */\nexport const StatusAnimation = ({\n loading,\n size = \"lg\",\n color = \"primary\",\n minLoadingMs = 0,\n loadingLabel,\n doneLabel,\n}: StatusAnimationProps) => {\n const prefersReducedMotion = useReducedMotion();\n const labels = useAnimationLabels();\n const [done, setDone] = useState(!loading);\n const loadingStart = useRef<number | null>(loading ? Date.now() : null);\n\n useEffect(() => {\n if (loading) {\n loadingStart.current = Date.now();\n setDone(false);\n return;\n }\n\n const elapsed =\n loadingStart.current != null\n ? Date.now() - loadingStart.current\n : minLoadingMs;\n const remaining = Math.max(0, minLoadingMs - elapsed);\n\n if (remaining === 0) {\n setDone(true);\n return;\n }\n\n const timer = window.setTimeout(() => setDone(true), remaining);\n return () => window.clearTimeout(timer);\n }, [loading, minLoadingMs]);\n\n const side = boxSize[size];\n const duration = prefersReducedMotion ? 0 : 0.35;\n\n return (\n <div\n role=\"status\"\n aria-live=\"polite\"\n className={styles.Status}\n style={{ width: side, height: side }}\n >\n <AnimatePresence mode=\"sync\">\n {done ? (\n <motion.div\n key=\"check\"\n aria-hidden\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration }}\n className={styles.Layer}\n >\n <CheckAnimation size={size} color={color} />\n </motion.div>\n ) : (\n <motion.div\n key=\"pulse\"\n aria-hidden\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration }}\n className={styles.Layer}\n >\n <PulseAnimation size={size} />\n </motion.div>\n )}\n </AnimatePresence>\n <span className={styles.SrOnly}>\n {done ? (doneLabel ?? labels.done) : (loadingLabel ?? labels.loading)}\n </span>\n </div>\n );\n};\n","type ClassNameProp<State> =\n | string\n | ((state: State) => string | undefined)\n | undefined;\n\n/**\n * Base UI replaces `className` wholesale, which would drop the module class of\n * a wrapper component. Returning a callback keeps the state argument working\n * for consumers who pass a function.\n *\n * Internal helper — not part of the public API, so it isn't re-exported from\n * `src/utils/index.ts`.\n */\nexport const mergeClassName =\n <State,>(base: string, className: ClassNameProp<State>) =>\n (state: State) =>\n [base, typeof className === \"function\" ? className(state) : className]\n .filter(Boolean)\n .join(\" \");\n","/*\n * The website's call-to-action: an 18px semibold uppercase label, 32px from the\n * start edge and 32px from the arrow, which sits in a black disc inside a 4px\n * rim. The sizes and the hover come from the website's CTAButton.\n */\n.ActionButton {\n --action-button-height: 3.5rem;\n --action-button-disc: var(--lea-spacing-6);\n --action-button-arrow: var(--lea-spacing-3);\n\n position: relative;\n display: inline-flex;\n min-width: calc(var(--lea-spacing) * 8);\n min-height: var(--action-button-height);\n box-sizing: border-box;\n align-items: center;\n justify-content: center;\n gap: var(--lea-spacing-4);\n padding: var(--lea-spacing-0-5);\n padding-inline-start: var(--lea-spacing-4);\n border: 0;\n border-radius: var(--lea-radius-full);\n background-color: var(--lea-palette-bg-base);\n box-shadow: 0 0 0 1px var(--lea-palette-border-strong);\n color: var(--lea-palette-text-base);\n cursor: pointer;\n font-family: inherit;\n /* 16px on a phone, 18px from the website's `mobile` width up. */\n font-size: var(--lea-font-step-0);\n font-weight: var(--lea-font-weight-semibold);\n line-height: var(--lea-line-height-tight);\n text-decoration: none;\n text-transform: uppercase;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n}\n\n@media (min-width: 640px) {\n .ActionButton {\n font-size: var(--lea-font-step-1);\n }\n}\n\n.ActionButton[data-size=\"sm\"] {\n --action-button-height: 2.5rem;\n --action-button-disc: var(--lea-spacing-4);\n --action-button-arrow: var(--lea-spacing-2-5);\n}\n\n/* Only the arrow: a circle as tall as the button, with the label kept for\n screen readers. */\n.ActionButton[data-icon-only] {\n min-width: var(--action-button-height);\n padding-inline-start: var(--lea-spacing-0-5);\n}\n\n.ActionButton[data-icon-only] .Label {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n}\n\n.ActionButton:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n.ActionButton:active:not([data-disabled]) {\n box-shadow: 0 0 0 2px var(--lea-palette-primary-base);\n}\n\n.ActionButton[data-disabled] {\n background-color: var(--lea-palette-bg-subtle);\n color: var(--lea-palette-text-subtle);\n cursor: not-allowed;\n}\n\n.Label {\n min-width: 0;\n transition: translate var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n.Disc {\n display: flex;\n width: var(--action-button-disc);\n height: var(--action-button-disc);\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n border-radius: var(--lea-radius-full);\n background-color: var(--lea-palette-bg-contrast);\n color: var(--lea-palette-text-contrast);\n transition: background-color var(--lea-duration-normal)\n var(--lea-easing-standard);\n}\n\n.Arrow {\n display: block;\n width: var(--action-button-arrow);\n height: var(--action-button-arrow);\n transition: rotate var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n@media (hover: hover) {\n .ActionButton:hover:not([data-disabled]) .Label {\n translate: var(--lea-spacing-1) 0;\n }\n\n .ActionButton:hover:not([data-disabled]) .Disc {\n background-color: var(--lea-palette-primary-base);\n }\n\n .ActionButton:hover:not([data-disabled]) .Arrow {\n rotate: -45deg;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Label,\n .Disc,\n .Arrow {\n transition-duration: 0.01ms;\n }\n}\n",".Button {\n --button-solid: var(--lea-palette-primary-base);\n --button-solid-hover: var(--lea-palette-primary-hover);\n --button-on-solid: var(--lea-palette-primary-contrast);\n --button-fg: var(--lea-palette-primary-base);\n --button-border: var(--lea-palette-primary-base);\n --button-tint: color-mix(\n in srgb,\n var(--button-fg) calc(var(--lea-opacity-hover) * 100%),\n color-mix(\n in srgb,\n var(--lea-color-white) calc(var(--lea-opacity-highlight) * 100%),\n transparent\n )\n );\n\n --button-height: 2.75rem;\n --button-icon-size: 1.25rem;\n --button-padding-inline: calc(var(--lea-spacing) * 2.5);\n\n box-sizing: border-box;\n display: inline-flex;\n min-height: var(--button-height);\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n gap: var(--lea-spacing);\n padding-block: calc(var(--lea-spacing) * 0.5);\n padding-inline: var(--button-padding-inline);\n margin: 0;\n border: 1px solid transparent;\n border-radius: var(--lea-radius-full);\n background-color: transparent;\n color: var(--button-fg);\n font-family: inherit;\n font-size: var(--lea-text-label-lg-font-size);\n font-weight: var(--lea-text-label-lg-font-weight);\n line-height: var(--lea-text-label-lg-line-height);\n letter-spacing: var(--lea-text-label-lg-letter-spacing);\n text-align: center;\n text-decoration: none;\n cursor: pointer;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n transition: background-color var(--lea-duration-normal)\n var(--lea-easing-standard),\n border-color var(--lea-duration-normal) var(--lea-easing-standard),\n color var(--lea-duration-normal) var(--lea-easing-standard),\n opacity var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n.Button svg {\n width: var(--button-icon-size);\n height: var(--button-icon-size);\n flex-shrink: 0;\n}\n\n.Button:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n/* Colours */\n\n.Button[data-color=\"secondary\"] {\n --button-solid: var(--lea-palette-secondary-base);\n --button-solid-hover: var(--lea-palette-secondary-hover);\n --button-on-solid: var(--lea-palette-secondary-contrast);\n --button-fg: var(--lea-palette-secondary-base);\n --button-border: var(--lea-palette-secondary-base);\n}\n\n.Button[data-color=\"error\"] {\n --button-solid: var(--lea-color-red-600);\n --button-solid-hover: var(--lea-color-red-500);\n --button-on-solid: var(--lea-palette-text-contrast);\n --button-fg: var(--lea-color-red-600);\n --button-border: var(--lea-color-red-600);\n}\n\n/* Variants */\n\n.Button[data-variant=\"solid\"] {\n background-color: var(--button-solid);\n color: var(--button-on-solid);\n}\n\n.Button[data-variant=\"outline\"] {\n border-color: var(--button-border);\n}\n\n/* `IconButton` only: a white disc that floats over a picture or a section,\n with the website's soft shadow. */\n.Button[data-variant=\"elevated\"] {\n background-color: var(--lea-palette-bg-base);\n box-shadow: var(--lea-shadow-soft);\n}\n\n@media (hover: hover) {\n .Button[data-variant=\"solid\"]:not([data-disabled]):hover {\n background-color: var(--button-solid-hover);\n }\n\n .Button[data-variant=\"outline\"]:not([data-disabled]):hover,\n .Button[data-variant=\"ghost\"]:not([data-disabled]):hover {\n background-color: var(--button-tint);\n }\n\n /* The tint mixed into white, so the disc stays opaque over a picture. */\n .Button[data-variant=\"elevated\"]:not([data-disabled]):hover {\n background-color: color-mix(\n in srgb,\n var(--button-fg) calc(var(--lea-opacity-hover) * 100%),\n var(--lea-palette-bg-base)\n );\n }\n}\n\n/* Sizes */\n\n.Button[data-size=\"sm\"] {\n --button-height: 2.25rem;\n --button-icon-size: 1rem;\n --button-padding-inline: calc(var(--lea-spacing) * 2);\n}\n\n.Button[data-size=\"lg\"] {\n --button-height: 3.25rem;\n --button-icon-size: 1.5rem;\n --button-padding-inline: calc(var(--lea-spacing) * 2.5);\n}\n\n.Button[data-start-icon] {\n padding-inline-start: calc(var(--button-padding-inline) - var(--lea-spacing));\n}\n\n.Button[data-end-icon] {\n padding-inline-end: calc(var(--button-padding-inline) - var(--lea-spacing));\n}\n\n.Button.IconButton {\n width: var(--button-height);\n height: var(--button-height);\n padding: 0;\n}\n\n/* The website's 40px disc (carousel arrows, video button); sm and lg keep the\n heights of the other variants. */\n.Button.IconButton[data-variant=\"elevated\"][data-size=\"md\"] {\n --button-height: 2.5rem;\n}\n\n.Button[data-disabled]:not([data-loading]) {\n color: var(--lea-color-neutral-400);\n cursor: not-allowed;\n}\n\n.Button[data-variant=\"solid\"][data-disabled]:not([data-loading]) {\n background-color: var(--lea-color-neutral-200);\n color: var(--lea-color-neutral-600);\n}\n\n.Button[data-variant=\"outline\"][data-disabled]:not([data-loading]) {\n border-color: var(--lea-palette-border-base);\n}\n\n.Button[data-loading] {\n cursor: progress;\n opacity: 0.7;\n}\n\n.Spinner {\n box-sizing: border-box;\n display: inline-block;\n width: var(--button-icon-size);\n height: var(--button-icon-size);\n flex-shrink: 0;\n border: 2px solid currentColor;\n border-inline-end-color: transparent;\n border-radius: var(--lea-radius-full);\n animation: button-spin 0.8s linear infinite;\n}\n\n@keyframes button-spin {\n to {\n rotate: 360deg;\n }\n}\n\n.SrOnly {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n border: 0;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Button {\n transition-duration: 0.01ms;\n }\n\n .Spinner {\n animation: none;\n }\n}\n","\"use client\";\n\nimport { Button as BaseButton } from \"@base-ui/react/button\";\nimport { useRender } from \"@base-ui/react/use-render\";\nimport {\n isValidElement,\n type MouseEvent,\n type ReactElement,\n type Ref,\n} from \"react\";\n\nimport { mergeClassName } from \"../../utils/merge-class-name\";\nimport type { ColorRoles } from \"../color-roles\";\nimport styles from \"./button.module.css\";\n\nexport type ButtonVariant = \"solid\" | \"outline\" | \"ghost\";\nexport type ButtonColor = ColorRoles<\"primary\" | \"secondary\" | \"error\">;\nexport type ButtonSize = \"sm\" | \"md\" | \"lg\";\n\nexport type ButtonRootProps = Omit<BaseButton.Props, \"color\" | \"ref\"> & {\n /**\n * Shows the busy state: the button is disabled, keeps its colour at reduced\n * opacity and sets `aria-busy`.\n * @default false\n */\n loading?: boolean;\n /** The rendered element: the `<button>`, or the link passed to `render`. */\n ref?: Ref<HTMLElement>;\n};\n\nexport type ButtonBaseProps = ButtonRootProps & {\n /**\n * Visual weight.\n * - `\"solid\"` — filled, for the main action.\n * - `\"outline\"` — bordered, no fill.\n * - `\"ghost\"` — no border or fill until hovered.\n * @default \"solid\"\n */\n variant?: ButtonVariant;\n /**\n * Colour scheme: `\"primary\"` is the brand purple, `\"secondary\"` the\n * near-black, `\"error\"` is for destructive actions.\n * @default \"primary\"\n */\n color?: ButtonColor;\n /**\n * @default \"md\"\n */\n size?: ButtonSize;\n};\n\n/**\n * A `render` element carrying `href` (an `<a>`, Next.js `Link`) or `to`\n * (React Router `Link`) is a link. Base UI's `Button` would give it\n * `role=\"button\"`, so links are rendered without button semantics.\n */\nconst isLinkElement = (\n render: ButtonRootProps[\"render\"],\n): render is ReactElement<{ href?: unknown; to?: unknown }> =>\n isValidElement<{ href?: unknown; to?: unknown }>(render) &&\n (render.props.href != null || render.props.to != null);\n\nconst preventDefault = (event: MouseEvent) => event.preventDefault();\n\n/**\n * A button, or the link passed to `render`, with the disabled and busy states\n * wired. It has no look of its own: the class comes from the caller. Internal\n * — not exported from the package.\n */\nexport function ButtonRoot({\n loading = false,\n disabled,\n className,\n render,\n nativeButton,\n focusableWhenDisabled,\n ref,\n ...rest\n}: ButtonRootProps) {\n const isDisabled = Boolean(disabled || loading);\n const isLink = isLinkElement(render);\n\n const shared = {\n \"aria-busy\": loading || undefined,\n \"data-loading\": loading || undefined,\n };\n\n const link = useRender({\n enabled: isLink,\n render: isLink ? render : undefined,\n ref,\n defaultTagName: \"a\",\n props: {\n ...rest,\n ...shared,\n \"aria-disabled\": isDisabled || undefined,\n \"data-disabled\": isDisabled ? \"\" : undefined,\n className:\n typeof className === \"function\"\n ? className({ disabled: isDisabled })\n : className,\n onClick: isDisabled ? preventDefault : rest.onClick,\n },\n });\n\n if (link) return link;\n\n return (\n <BaseButton\n {...rest}\n {...shared}\n ref={ref}\n render={render}\n nativeButton={nativeButton}\n focusableWhenDisabled={focusableWhenDisabled}\n disabled={isDisabled}\n className={className}\n />\n );\n}\n\n/**\n * The root shared by `Button`, `IconButton`, `CloseButton` and\n * `DownloadTrigger`: `ButtonRoot` with the button look. Internal — not\n * exported from the package. It also takes `\"elevated\"`, which only\n * `IconButton` offers.\n */\nexport function ButtonBase({\n variant = \"solid\",\n color = \"primary\",\n size = \"md\",\n className,\n ...rest\n}: Omit<ButtonBaseProps, \"variant\"> & {\n variant?: ButtonVariant | \"elevated\";\n}) {\n return (\n <ButtonRoot\n {...rest}\n data-variant={variant}\n data-color={color}\n data-size={size}\n className={mergeClassName(styles.Button, className)}\n />\n );\n}\n","\"use client\";\n\nimport { ArrowRight } from \"lucide-react\";\nimport type { ReactNode } from \"react\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport styles from \"./action-button.module.css\";\nimport { ButtonRoot, type ButtonRootProps } from \"../button-base\";\n\nexport type ActionButtonSize = \"sm\" | \"lg\";\n\n/**\n * With `iconOnly` the label is hidden but stays the accessible name, so it is\n * required there.\n */\ntype ActionButtonLabel =\n | {\n /**\n * Shows only the arrow: a round button, with the label kept for screen\n * readers. Used where space is short, such as a collapsed\n * `ExpandingCards` item.\n * @default false\n */\n iconOnly?: false;\n children?: ReactNode;\n }\n | { iconOnly: true; children: ReactNode };\n\nexport type ActionButtonProps = Omit<ButtonRootProps, \"loading\" | \"children\"> &\n ActionButtonLabel & {\n /**\n * `\"lg\"` is 56px tall with a 48px arrow; `\"sm\"` is 40px tall with a 32px\n * arrow, for headers and tight rows.\n * @default \"lg\"\n */\n size?: ActionButtonSize;\n };\n\n/**\n * A pill-shaped call-to-action button with a built-in arrow icon.\n *\n * Used for primary navigation actions on the website, such as hero sections\n * and landing pages. On hover the label slides towards the arrow and the arrow\n * turns purple and points up and to the right. To render a link, pass it\n * through `render`; an element with `href` or `to` is rendered as a real link,\n * without button semantics:\n *\n * ```tsx\n * <ActionButton render={<a href=\"/lan\" />}>Se våra lån</ActionButton>\n * <ActionButton render={<NextLink href=\"/lan\" />}>Se våra lån</ActionButton>\n * <ActionButton render={<RouterLink to=\"/lan\" />}>Se våra lån</ActionButton>\n * <ActionButton size=\"sm\" iconOnly render={<a href=\"/lan\" />}>Se våra lån</ActionButton>\n * ```\n */\nexport function ActionButton({\n children,\n className,\n size = \"lg\",\n iconOnly = false,\n ...rest\n}: ActionButtonProps) {\n return (\n <ButtonRoot\n {...rest}\n data-size={size}\n data-icon-only={iconOnly || undefined}\n className={mergeClassName(styles.ActionButton, className)}\n >\n {children != null && <span className={styles.Label}>{children}</span>}\n <span aria-hidden className={styles.Disc}>\n <ArrowRight className={styles.Arrow} />\n </span>\n </ButtonRoot>\n );\n}\n","\"use client\";\n\nimport type { ReactNode } from \"react\";\n\nimport { ButtonBase, type ButtonBaseProps } from \"../button-base\";\nimport styles from \"../button.module.css\";\n\nexport type {\n ButtonColor,\n ButtonSize,\n ButtonVariant,\n} from \"../button-base\";\n\nexport type ButtonProps = ButtonBaseProps & {\n /** Icon before the label. Replaced by the spinner while `loading`. */\n startIcon?: ReactNode;\n /** Icon after the label. */\n endIcon?: ReactNode;\n /**\n * Text announced to screen readers while `loading`, through a\n * visually-hidden `role=\"status\"` region, e.g. \"Saving…\".\n */\n loadingLabel?: string;\n};\n\n/**\n * A button built on Base UI's `Button` primitive and styled with Lea's design\n * tokens. Pick a `variant` (`solid`, `outline`, `ghost`), a `color`\n * (`primary`, `secondary`, `error`) and a `size` (`sm`, `md`, `lg`).\n *\n * ```tsx\n * <Button variant=\"outline\" startIcon={<Plus aria-hidden />}>Add</Button>\n * ```\n *\n * To render a link that looks like a button, pass the link through `render`.\n * An element with `href` or `to` is rendered as a real link, without button\n * semantics:\n *\n * ```tsx\n * <Button render={<a href=\"/about\" />}>About</Button>\n * <Button render={<NextLink href=\"/about\" />}>About</Button>\n * <Button render={<RouterLink to=\"/about\" />}>About</Button>\n * ```\n *\n * For a link inside running text, use `Link` instead.\n */\nexport function Button({\n startIcon,\n endIcon,\n loading = false,\n loadingLabel,\n children,\n ...rest\n}: ButtonProps) {\n return (\n <>\n <ButtonBase\n {...rest}\n loading={loading}\n data-start-icon={loading || startIcon != null || undefined}\n data-end-icon={endIcon != null || undefined}\n >\n {loading ? <span className={styles.Spinner} aria-hidden /> : startIcon}\n {children}\n {endIcon}\n </ButtonBase>\n {loadingLabel != null && (\n <span role=\"status\" className={styles.SrOnly}>\n {loading ? loadingLabel : null}\n </span>\n )}\n </>\n );\n}\n","import { createTranslation } from \"../../i18n\";\n\n/**\n * Translated accessible name for the close (X) button shared by the feedback\n * overlays (`SimpleDialog`, `SimpleDrawer`). Consumers can override per-instance\n * via a `closeLabel` prop.\n */\nexport const useCloseLabel = createTranslation({\n close: {\n no: \"Lukk\",\n se: \"Stäng\",\n fi: \"Sulje\",\n es: \"Cerrar\",\n },\n});\n","\"use client\";\n\nimport type { ReactNode } from \"react\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport {\n ButtonBase,\n type ButtonBaseProps,\n type ButtonVariant,\n} from \"../button-base\";\nimport styles from \"../button.module.css\";\n\n/**\n * The variants of `Button`, plus `\"elevated\"`: a white disc with the website's\n * soft shadow, for controls that float over a picture or a coloured section\n * (the arrows of a carousel, the play button of a video). At `md` it is 40px,\n * the website's size; `sm` and `lg` match the other variants.\n */\nexport type IconButtonVariant = ButtonVariant | \"elevated\";\n\n/**\n * Requires an accessible name so the button is never nameless. Provide exactly\n * one of `aria-label` or `aria-labelledby`.\n */\ntype IconButtonAccessibleName =\n | { \"aria-label\": string; \"aria-labelledby\"?: never }\n | { \"aria-labelledby\": string; \"aria-label\"?: never };\n\nexport type IconButtonProps = Omit<\n ButtonBaseProps,\n \"children\" | \"aria-label\" | \"aria-labelledby\" | \"variant\"\n> &\n IconButtonAccessibleName & {\n /** The icon. Mark it `aria-hidden` — the name comes from `aria-label`. */\n children: ReactNode;\n /**\n * Visual weight: `\"solid\"`, `\"outline\"` and `\"ghost\"` as on `Button`, or\n * `\"elevated\"`, a white disc with the website's soft shadow\n * (`shadow-soft`) and the icon in `color`; 40px at `md`.\n * @default \"solid\"\n */\n variant?: IconButtonVariant;\n };\n\n/**\n * A square, icon-only button with the same variants, colours and sizes as\n * `Button`. Because it has no visible text, TypeScript requires an\n * `aria-label` or `aria-labelledby`.\n *\n * ```tsx\n * <IconButton aria-label=\"Add\" variant=\"outline\">\n * <Plus aria-hidden />\n * </IconButton>\n * ```\n */\nexport function IconButton({\n loading = false,\n className,\n children,\n ...rest\n}: IconButtonProps) {\n return (\n <ButtonBase\n {...rest}\n loading={loading}\n className={mergeClassName(styles.IconButton, className)}\n >\n {loading ? <span className={styles.Spinner} aria-hidden /> : children}\n </ButtonBase>\n );\n}\n","\"use client\";\n\nimport { X } from \"lucide-react\";\n\nimport { useCloseLabel } from \"../../feedback/use-close-label\";\nimport type { ButtonBaseProps } from \"../button-base\";\nimport { IconButton } from \"../icon-button/icon-button\";\n\nexport type CloseButtonProps = Omit<\n ButtonBaseProps,\n \"children\" | \"aria-label\" | \"aria-labelledby\"\n> & {\n /** Accessible name. Defaults to a translated \"Close\". */\n label?: string;\n};\n\n/**\n * An `IconButton` with a built-in X icon. It always has an accessible name —\n * a translated \"Close\" unless you pass `label`.\n *\n * ```tsx\n * <CloseButton onClick={() => setOpen(false)} />\n * ```\n */\nexport function CloseButton({\n label,\n variant = \"ghost\",\n color = \"secondary\",\n ...rest\n}: CloseButtonProps) {\n const t = useCloseLabel();\n\n return (\n <IconButton\n {...rest}\n variant={variant}\n color={color}\n aria-label={label ?? t.close}\n >\n <X aria-hidden />\n </IconButton>\n );\n}\n","export type DownloadableData =\n | string\n | Blob\n | File\n | ArrayBuffer\n | Uint8Array;\n\nexport type DownloadDataSource =\n | DownloadableData\n | (() => DownloadableData | Promise<DownloadableData>);\n\nexport async function resolveDownloadData(\n data: DownloadDataSource,\n): Promise<DownloadableData> {\n return typeof data === \"function\" ? data() : data;\n}\n\nexport function downloadFile(\n data: DownloadableData,\n fileName: string,\n mimeType?: string,\n): void {\n const blob =\n data instanceof Blob\n ? data\n : new Blob([data as BlobPart], mimeType ? { type: mimeType } : undefined);\n\n const url = URL.createObjectURL(blob);\n const anchor = document.createElement(\"a\");\n anchor.href = url;\n anchor.download = fileName;\n anchor.rel = \"noopener\";\n anchor.click();\n URL.revokeObjectURL(url);\n}\n","import { createTranslation } from \"../../../i18n\";\n\n/**\n * Translated default copy for `DownloadTrigger`. Consumers can override it by\n * passing `children`.\n */\nexport const useDownloadLabels = createTranslation({\n download: {\n no: \"Last ned\",\n se: \"Ladda ner\",\n fi: \"Lataa\",\n es: \"Descargar\",\n },\n});\n","\"use client\";\n\nimport {\n downloadFile,\n resolveDownloadData,\n type DownloadDataSource,\n} from \"@/utils/download-file\";\nimport { useState } from \"react\";\n\nimport { Button, type ButtonProps } from \"../button/button\";\nimport { useDownloadLabels } from \"./use-download-labels\";\n\ntype DownloadTriggerProps = Omit<ButtonProps, \"onClick\"> & {\n /** File contents, or a function/promise that resolves to them. */\n data: DownloadDataSource;\n /** File name. */\n fileName: string;\n /** MIME type. */\n mimeType?: string;\n /** Callback for when the download starts. */\n onDownload?: () => void;\n /** Callback for when the download fails. */\n onDownloadError?: (error: unknown) => void;\n};\n\n/**\n * A button that triggers a download of a file. Shares `Button`'s variants,\n * colours and sizes, and shows the loading state while `data` resolves.\n *\n * Without `children` the label is the translated word for \"download\" in the\n * `LeaProvider` language.\n */\n\nexport const DownloadTrigger = ({\n data,\n fileName,\n mimeType,\n onDownload,\n onDownloadError,\n loading: loadingProp,\n children,\n ...rest\n}: DownloadTriggerProps) => {\n const labels = useDownloadLabels();\n const [pending, setPending] = useState(false);\n const loading = loadingProp ?? pending;\n\n const handleClick = async () => {\n setPending(true);\n try {\n const resolved = await resolveDownloadData(data);\n downloadFile(resolved, fileName, mimeType);\n onDownload?.();\n } catch (error) {\n onDownloadError?.(error);\n } finally {\n setPending(false);\n }\n };\n\n return (\n <Button {...rest} loading={loading} onClick={handleClick}>\n {children ?? labels.download}\n </Button>\n );\n};\n",".SkipLink {\n position: absolute;\n z-index: 1000;\n inset-block-start: var(--lea-spacing-1);\n inset-inline-start: var(--lea-spacing-1);\n}\n\n.SkipLink:not(:focus) {\n clip-path: inset(50%);\n pointer-events: none;\n}\n","\"use client\";\n\nimport type { ReactNode } from \"react\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport { ButtonBase, type ButtonBaseProps } from \"../button-base\";\nimport styles from \"./skip-link.module.css\";\n\nexport type SkipLinkProps = Pick<ButtonBaseProps, \"className\" | \"style\"> & {\n /** The id of the main content, as a fragment: `#main`. */\n href: string;\n children: ReactNode;\n};\n\n/**\n * A link for keyboard users to jump past the header to the main content. It\n * stays hidden until it receives focus, so place it first in the page and it\n * is the first thing the Tab key reaches:\n *\n * ```tsx\n * <body>\n * <SkipLink href=\"#main\">Hopp til hovedinnhold</SkipLink>\n * <AppHeader />\n * <main id=\"main\" tabIndex={-1}>…</main>\n * </body>\n * ```\n *\n * It is positioned against the page, so no ancestor should be positioned.\n */\nexport function SkipLink({ href, children, className, style }: SkipLinkProps) {\n return (\n <ButtonBase\n render={<a href={href} />}\n color=\"secondary\"\n className={mergeClassName(styles.SkipLink, className)}\n style={style}\n >\n {children}\n </ButtonBase>\n );\n}\n","/**\n * The first font-size scale, from before the text styles.\n *\n * @deprecated Use a text style (`textStyles`, `--lea-text-{role}-{size}-*`,\n * `text-heading-lg`) or, for a size on its own, a step on the type scale\n * (`fontStep`, `--lea-font-step-{n}`). Kept for the components that still use\n * it, and removed in a later major.\n */\nexport const fontSizes = {\n xs: \"0.75rem\",\n sm: \"0.875rem\",\n md: \"1rem\",\n lg: \"1.25rem\",\n xl: \"1.5rem\",\n \"2xl\": \"2rem\",\n \"3xl\": \"2.5rem\",\n \"4xl\": \"3rem\",\n \"5xl\": \"3.5rem\",\n \"6xl\": \"4rem\",\n} as const;\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n */\n\n/* Without a `textStyle`, text is `body-md`. Every Text sets all four\n properties itself, so one nested in another does not take its parent's. */\n.Text {\n margin: 0;\n color: var(--lea-palette-text-base);\n font-size: var(--lea-text-body-md-font-size);\n font-weight: var(--lea-text-body-md-font-weight);\n line-height: var(--lea-text-body-md-line-height);\n letter-spacing: var(--lea-text-body-md-letter-spacing);\n}\n\n/* One rule per text style. tests/tokens.test.ts checks the list against\n src/theme/tokens/text-styles.ts. */\n.Text[data-text-style=\"display-sm\"] {\n font-size: var(--lea-text-display-sm-font-size);\n font-weight: var(--lea-text-display-sm-font-weight);\n line-height: var(--lea-text-display-sm-line-height);\n letter-spacing: var(--lea-text-display-sm-letter-spacing);\n}\n\n.Text[data-text-style=\"display-md\"] {\n font-size: var(--lea-text-display-md-font-size);\n font-weight: var(--lea-text-display-md-font-weight);\n line-height: var(--lea-text-display-md-line-height);\n letter-spacing: var(--lea-text-display-md-letter-spacing);\n}\n\n.Text[data-text-style=\"heading-xs\"] {\n font-size: var(--lea-text-heading-xs-font-size);\n font-weight: var(--lea-text-heading-xs-font-weight);\n line-height: var(--lea-text-heading-xs-line-height);\n letter-spacing: var(--lea-text-heading-xs-letter-spacing);\n}\n\n.Text[data-text-style=\"heading-sm\"] {\n font-size: var(--lea-text-heading-sm-font-size);\n font-weight: var(--lea-text-heading-sm-font-weight);\n line-height: var(--lea-text-heading-sm-line-height);\n letter-spacing: var(--lea-text-heading-sm-letter-spacing);\n}\n\n.Text[data-text-style=\"heading-md\"] {\n font-size: var(--lea-text-heading-md-font-size);\n font-weight: var(--lea-text-heading-md-font-weight);\n line-height: var(--lea-text-heading-md-line-height);\n letter-spacing: var(--lea-text-heading-md-letter-spacing);\n}\n\n.Text[data-text-style=\"heading-lg\"] {\n font-size: var(--lea-text-heading-lg-font-size);\n font-weight: var(--lea-text-heading-lg-font-weight);\n line-height: var(--lea-text-heading-lg-line-height);\n letter-spacing: var(--lea-text-heading-lg-letter-spacing);\n}\n\n.Text[data-text-style=\"heading-xl\"] {\n font-size: var(--lea-text-heading-xl-font-size);\n font-weight: var(--lea-text-heading-xl-font-weight);\n line-height: var(--lea-text-heading-xl-line-height);\n letter-spacing: var(--lea-text-heading-xl-letter-spacing);\n}\n\n.Text[data-text-style=\"title-md\"] {\n font-size: var(--lea-text-title-md-font-size);\n font-weight: var(--lea-text-title-md-font-weight);\n line-height: var(--lea-text-title-md-line-height);\n letter-spacing: var(--lea-text-title-md-letter-spacing);\n}\n\n.Text[data-text-style=\"title-xl\"] {\n font-size: var(--lea-text-title-xl-font-size);\n font-weight: var(--lea-text-title-xl-font-weight);\n line-height: var(--lea-text-title-xl-line-height);\n letter-spacing: var(--lea-text-title-xl-letter-spacing);\n}\n\n.Text[data-text-style=\"body-xs\"] {\n font-size: var(--lea-text-body-xs-font-size);\n font-weight: var(--lea-text-body-xs-font-weight);\n line-height: var(--lea-text-body-xs-line-height);\n letter-spacing: var(--lea-text-body-xs-letter-spacing);\n}\n\n.Text[data-text-style=\"body-sm\"] {\n font-size: var(--lea-text-body-sm-font-size);\n font-weight: var(--lea-text-body-sm-font-weight);\n line-height: var(--lea-text-body-sm-line-height);\n letter-spacing: var(--lea-text-body-sm-letter-spacing);\n}\n\n.Text[data-text-style=\"body-md\"] {\n font-size: var(--lea-text-body-md-font-size);\n font-weight: var(--lea-text-body-md-font-weight);\n line-height: var(--lea-text-body-md-line-height);\n letter-spacing: var(--lea-text-body-md-letter-spacing);\n}\n\n.Text[data-text-style=\"body-lg\"] {\n font-size: var(--lea-text-body-lg-font-size);\n font-weight: var(--lea-text-body-lg-font-weight);\n line-height: var(--lea-text-body-lg-line-height);\n letter-spacing: var(--lea-text-body-lg-letter-spacing);\n}\n\n.Text[data-text-style=\"label-sm\"] {\n font-size: var(--lea-text-label-sm-font-size);\n font-weight: var(--lea-text-label-sm-font-weight);\n line-height: var(--lea-text-label-sm-line-height);\n letter-spacing: var(--lea-text-label-sm-letter-spacing);\n}\n\n.Text[data-text-style=\"label-md\"] {\n font-size: var(--lea-text-label-md-font-size);\n font-weight: var(--lea-text-label-md-font-weight);\n line-height: var(--lea-text-label-md-line-height);\n letter-spacing: var(--lea-text-label-md-letter-spacing);\n}\n\n.Text[data-text-style=\"label-lg\"] {\n font-size: var(--lea-text-label-lg-font-size);\n font-weight: var(--lea-text-label-lg-font-weight);\n line-height: var(--lea-text-label-lg-line-height);\n letter-spacing: var(--lea-text-label-lg-letter-spacing);\n}\n\n/* After the text styles, so a `fontWeight` wins over the style's own. */\n.Text[data-font-weight=\"thin\"] {\n font-weight: var(--lea-font-weight-thin);\n}\n\n.Text[data-font-weight=\"light\"] {\n font-weight: var(--lea-font-weight-light);\n}\n\n.Text[data-font-weight=\"regular\"] {\n font-weight: var(--lea-font-weight-regular);\n}\n\n.Text[data-font-weight=\"medium\"] {\n font-weight: var(--lea-font-weight-medium);\n}\n\n.Text[data-font-weight=\"semibold\"] {\n font-weight: var(--lea-font-weight-semibold);\n}\n\n.Text[data-font-weight=\"bold\"] {\n font-weight: var(--lea-font-weight-bold);\n}\n\n.Text[data-font-weight=\"black\"] {\n font-weight: var(--lea-font-weight-black);\n}\n\n.Text[data-color=\"inherit\"] {\n color: inherit;\n}\n\n.Text[data-align=\"start\"] {\n text-align: start;\n}\n\n.Text[data-align=\"center\"] {\n text-align: center;\n}\n\n.Text[data-align=\"end\"] {\n text-align: end;\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport type { Ref } from \"react\";\n\nimport type { ColorPath } from \"../../../theme/token-types\";\nimport { fontSizes } from \"../../../theme/tokens/font-sizes\";\nimport type { fontWeights } from \"../../../theme/tokens/font-weights\";\nimport type { TextStyleToken } from \"../../../theme/tokens/text-styles\";\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport styles from \"./text.module.css\";\n\ntype FontSizeToken = keyof typeof fontSizes;\ntype FontWeightToken = keyof typeof fontWeights;\n\n/** A palette token such as `text.subtle`, or `inherit` for the parent's colour. */\nexport type TextColor = ColorPath | \"inherit\";\nexport type TextAlign = \"start\" | \"center\" | \"end\";\n\nexport type TextProps = Omit<useRender.ComponentProps<\"p\">, \"ref\" | \"color\"> & {\n /**\n * A text style such as `heading-lg` or `body-sm`: size, weight, line height\n * and letter spacing as one token. It says how the text looks, not which\n * element it is, so pick the element with `render` from the page hierarchy.\n * @default \"body-md\"\n */\n textStyle?: TextStyleToken;\n /** A font weight token, when the text style's own weight does not fit. */\n fontWeight?: FontWeightToken;\n /**\n * A colour token from the palette, written as its path. Use `inherit` to\n * take the colour of the surrounding text.\n * @default \"text.base\"\n */\n color?: TextColor;\n /** Text alignment, following the reading direction. */\n align?: TextAlign;\n /**\n * @deprecated Use `textStyle`. The `fontSize` scale (`xs` to `6xl`) is from\n * before the text styles and is removed in a later major.\n */\n fontSize?: FontSizeToken;\n /** The rendered element: the `<p>`, or the element passed to `render`. */\n ref?: Ref<HTMLElement>;\n};\n\nconst paletteVariable = (path: ColorPath) =>\n `var(--lea-palette-${path.replaceAll(\".\", \"-\")})`;\n\n/**\n * Text set in one of Lea's text styles. Renders a `p` with no margin; pass\n * `render` for another element:\n *\n * ```tsx\n * <Text>Vi svarer innen to virkedager.</Text>\n * <Text render={<h1 />} textStyle=\"heading-lg\">Mine lån</Text>\n * <Text render={<span />} textStyle=\"body-sm\" color=\"text.subtle\">\n * Sist oppdatert i dag\n * </Text>\n * ```\n *\n * Spacing around the text belongs to the layout: use `Stack` and its `gap`, or\n * `className` and `style`.\n */\nexport function Text({\n textStyle,\n fontWeight,\n color,\n align,\n fontSize,\n render,\n ref,\n className,\n style,\n ...rest\n}: TextProps) {\n return useRender({\n render,\n ref,\n defaultTagName: \"p\",\n props: {\n ...rest,\n \"data-text-style\": textStyle,\n \"data-font-weight\": fontWeight,\n \"data-align\": align,\n \"data-color\": color === \"inherit\" ? color : undefined,\n className: mergeClassName(styles.Text, className)({}),\n style: {\n ...(color != null && color !== \"inherit\"\n ? { color: paletteVariable(color) }\n : undefined),\n ...(fontSize != null ? { fontSize: fontSizes[fontSize] } : undefined),\n ...style,\n },\n },\n });\n}\n",".DataField {\n box-sizing: border-box;\n display: flex;\n width: 100%;\n align-items: center;\n justify-content: space-between;\n gap: var(--lea-spacing-2);\n padding-block: calc(var(--lea-spacing) * 1.75);\n padding-inline: var(--lea-spacing-1);\n}\n\n.DataField[data-divider] {\n border-block-end: 1px solid var(--lea-palette-border-base);\n}\n\n.Label {\n min-width: 0;\n flex: 1;\n}\n\n.Value {\n min-width: 0;\n text-align: end;\n}\n","import type { ComponentProps, ReactNode } from \"react\";\n\nimport { Text } from \"../../typography/text/text\";\nimport styles from \"./data-field.module.css\";\n\nexport type DataFieldProps = Omit<ComponentProps<\"div\">, \"children\"> & {\n label: ReactNode;\n value: ReactNode;\n /**\n * Draws a line under the row, for stacking several into a summary.\n * @default true\n */\n divider?: boolean;\n};\n\nconst isText = (content: ReactNode) =>\n typeof content === \"string\" || typeof content === \"number\";\n\n/**\n * A single label/value row for presenting read-only details, with an optional\n * bottom divider so several fields can be stacked into a summary list.\n *\n * ```tsx\n * <DataField label=\"Lånebeløp\" value=\"150 000 kr\" />\n * <DataField label=\"Månedsbeløp\" value=\"1 316 kr\" divider={false} />\n * ```\n *\n * Text passed as `label` or `value` is set in the label and value styles. Any\n * other element is rendered as it is, for a side that needs richer content.\n */\nexport const DataField = ({\n label,\n value,\n divider = true,\n className,\n ...rest\n}: DataFieldProps) => (\n <div\n {...rest}\n className={[styles.DataField, className].filter(Boolean).join(\" \")}\n data-divider={divider || undefined}\n >\n <div className={styles.Label}>\n {isText(label) ? (\n <Text render={<span />} color=\"text.subtle\">\n {label}\n </Text>\n ) : (\n label\n )}\n </div>\n <div className={styles.Value}>\n {isText(value) ? (\n <Text render={<span />} fontWeight=\"medium\">\n {value}\n </Text>\n ) : (\n value\n )}\n </div>\n </div>\n);\n","/*\n * `--card-tint` is the fill and `--card-line` the border of the `outlined`\n * variant. The colours read the same values as `Chip`.\n */\n.Card {\n --card-tint: var(--lea-palette-bg-base);\n --card-line: var(--lea-palette-border-base);\n\n box-sizing: border-box;\n overflow: hidden;\n border: 1px solid var(--card-line);\n border-radius: var(--lea-radius-4xl);\n background-color: var(--card-tint);\n color: var(--lea-palette-text-base);\n}\n\n/* The border stays, transparent, so both variants take the same room. */\n.Card[data-variant=\"filled\"] {\n border-color: transparent;\n}\n\n.Card[data-color=\"primary\"] {\n --card-tint: var(--lea-palette-primary-subtle);\n --card-line: var(--lea-color-purple-600);\n}\n\n.Card[data-color=\"tertiary\"] {\n --card-tint: var(--lea-palette-tertiary-subtle);\n --card-line: var(--lea-color-teal-700);\n}\n\n.Card[data-color=\"neutral\"] {\n --card-tint: var(--lea-color-neutral-100);\n --card-line: var(--lea-color-neutral-400);\n}\n\n.Card[data-color=\"success\"] {\n --card-tint: var(--lea-palette-status-success-subtle);\n --card-line: var(--lea-color-teal-700);\n}\n\n.Card[data-color=\"info\"] {\n --card-tint: var(--lea-palette-status-info-subtle);\n --card-line: var(--lea-color-blue-600);\n}\n\n.Card[data-color=\"warning\"] {\n --card-tint: var(--lea-palette-status-warning-subtle);\n --card-line: var(--lea-color-amber-700);\n}\n\n.Card[data-color=\"error\"] {\n --card-tint: var(--lea-palette-status-error-subtle);\n --card-line: var(--lea-color-red-600);\n}\n\n.Content {\n box-sizing: border-box;\n padding: var(--lea-spacing-2);\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport type { Ref } from \"react\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport type { ColorRoles } from \"../../color-roles\";\nimport styles from \"./card.module.css\";\n\nexport type CardColor = ColorRoles<\n \"primary\" | \"tertiary\" | \"neutral\" | \"success\" | \"info\" | \"warning\" | \"error\"\n>;\nexport type CardVariant = \"outlined\" | \"filled\";\n\nexport type CardProps = Omit<\n useRender.ComponentProps<\"div\">,\n \"ref\" | \"color\"\n> & {\n /** The rendered element: the `<div>`, or the element passed to `render`. */\n ref?: Ref<HTMLElement>;\n /**\n * A tint for the card. Left out, the card is white.\n * - A brand tint: `\"primary\"` (purple), `\"tertiary\"` (teal) or `\"neutral\"`\n * (grey).\n * - A status: `\"success\"` (teal), `\"info\"` (blue), `\"warning\"` (amber) or\n * `\"error\"` (red).\n */\n color?: CardColor;\n /**\n * With a 1px border, or without one. The card keeps its size either way, so\n * the two line up in a row.\n * @default \"outlined\"\n */\n variant?: CardVariant;\n};\n\n/**\n * A flat surface that groups related content: white, with a 1px border and\n * rounded corners. It has no padding of its own, so an image or a divider can\n * run edge to edge; put text in `CardContent`.\n *\n * Set `color` for a tint, and `variant=\"filled\"` to drop the border:\n *\n * ```tsx\n * <Card color=\"primary\" variant=\"filled\">…</Card>\n * ```\n *\n * ```tsx\n * <Card>\n * <CardContent>\n * <Text textStyle=\"heading-sm\">Forbrukslån</Text>\n * <Text color=\"text.subtle\">150 000 kr over 10 år</Text>\n * </CardContent>\n * </Card>\n * ```\n *\n * Renders a `div`; pass `render` for another element, e.g.\n * `render={<section />}` or `render={<li />}`.\n */\nexport function Card({\n render,\n ref,\n className,\n color,\n variant = \"outlined\",\n ...rest\n}: CardProps) {\n return useRender({\n render,\n ref,\n props: {\n ...rest,\n \"data-color\": color,\n \"data-variant\": variant,\n className: mergeClassName(styles.Card, className)({}),\n },\n });\n}\n\nexport type CardContentProps = Omit<useRender.ComponentProps<\"div\">, \"ref\"> & {\n /** The rendered element: the `<div>`, or the element passed to `render`. */\n ref?: Ref<HTMLElement>;\n};\n\n/**\n * The padded area of a `Card`. A card can hold several, separated by whatever\n * sits between them.\n */\nexport function CardContent({\n render,\n ref,\n className,\n ...rest\n}: CardContentProps) {\n return useRender({\n render,\n ref,\n props: {\n ...rest,\n className: mergeClassName(styles.Content, className)({}),\n },\n });\n}\n",".Chip {\n --chip-tint: var(--lea-palette-primary-subtle);\n --chip-shade: var(--lea-color-purple-800);\n --chip-line: var(--lea-color-purple-600);\n --chip-ink: var(--lea-color-purple-600);\n --chip-outlined-disc: var(--chip-ink);\n --chip-outlined-cross: var(--chip-tint);\n\n --chip-edge: calc(var(--lea-spacing) * 0.75);\n --chip-gap: calc(var(--lea-spacing) * 0.75);\n --chip-label-edge: calc(var(--lea-spacing) * 1.75);\n --chip-padding-block: calc(var(--lea-spacing) * 0.25);\n --chip-delete-size: 1.25rem;\n\n display: inline-flex;\n max-width: 100%;\n box-sizing: border-box;\n align-items: center;\n padding-block: var(--chip-padding-block);\n border: 1px solid transparent;\n border-radius: var(--lea-radius-full);\n background-color: var(--chip-tint);\n color: var(--chip-shade);\n font-size: var(--lea-text-label-md-font-size);\n font-weight: var(--lea-text-label-md-font-weight);\n gap: var(--chip-gap);\n letter-spacing: var(--lea-text-label-md-letter-spacing);\n line-height: var(--lea-line-height-normal);\n vertical-align: middle;\n white-space: nowrap;\n}\n\n.Chip[data-size=\"sm\"] {\n --chip-edge: var(--lea-spacing-0-5);\n --chip-gap: var(--lea-spacing-0-5);\n --chip-label-edge: var(--lea-spacing-1);\n --chip-padding-block: 0px;\n --chip-delete-size: 1.125rem;\n}\n\n/*\n * The website's pill: 16px semibold, 4px above and below the text and 16px at\n * the sides, each counted with the 1px border (hence the minus 1px), 28px tall.\n */\n.Chip[data-size=\"lg\"] {\n --chip-edge: var(--lea-spacing-1);\n --chip-gap: var(--lea-spacing-1);\n --chip-label-edge: calc(var(--lea-spacing-2) - 1px);\n --chip-padding-block: calc(var(--lea-spacing-0-5) - 1px);\n\n /* heading-xs, at the tight line height of a label. */\n font-size: var(--lea-text-heading-xs-font-size);\n font-weight: var(--lea-text-heading-xs-font-weight);\n letter-spacing: var(--lea-text-heading-xs-letter-spacing);\n line-height: var(--lea-line-height-tight);\n}\n\n.Chip[data-color=\"tertiary\"] {\n --chip-tint: var(--lea-palette-tertiary-subtle);\n --chip-shade: var(--lea-color-teal-800);\n --chip-line: var(--lea-color-teal-700);\n --chip-ink: var(--lea-color-teal-700);\n}\n\n.Chip[data-color=\"neutral\"] {\n --chip-tint: var(--lea-color-neutral-100);\n --chip-shade: var(--lea-color-neutral-800);\n --chip-line: var(--lea-color-neutral-400);\n --chip-ink: var(--lea-color-neutral-700);\n}\n\n/* The website's beige pill: the words stay `text.base`. */\n.Chip[data-color=\"muted\"] {\n --chip-tint: var(--lea-palette-bg-muted);\n --chip-shade: var(--lea-palette-text-base);\n --chip-line: var(--lea-color-beige-400);\n --chip-ink: var(--lea-palette-text-base);\n}\n\n.Chip[data-color=\"success\"] {\n --chip-tint: var(--lea-palette-status-success-subtle);\n --chip-shade: var(--lea-color-teal-800);\n --chip-line: var(--lea-color-teal-700);\n --chip-ink: var(--lea-color-teal-700);\n}\n\n.Chip[data-color=\"info\"] {\n --chip-tint: var(--lea-palette-status-info-subtle);\n --chip-shade: var(--lea-color-blue-800);\n --chip-line: var(--lea-color-blue-600);\n --chip-ink: var(--lea-color-blue-600);\n --chip-outlined-disc: var(--lea-color-white);\n --chip-outlined-cross: var(--lea-color-blue-600);\n}\n\n.Chip[data-color=\"warning\"] {\n --chip-tint: var(--lea-palette-status-warning-subtle);\n --chip-shade: var(--lea-color-amber-800);\n --chip-line: var(--lea-color-amber-700);\n --chip-ink: var(--lea-color-amber-700);\n}\n\n.Chip[data-color=\"error\"] {\n --chip-tint: var(--lea-palette-status-error-subtle);\n --chip-shade: var(--lea-color-red-800);\n --chip-line: var(--lea-color-red-600);\n --chip-ink: var(--lea-color-red-600);\n}\n\n.Chip[data-variant=\"outlined\"] {\n border-color: var(--chip-line);\n background-color: transparent;\n color: var(--chip-ink);\n}\n\n.Chip[data-disabled] {\n opacity: 0.38;\n}\n\n.Chip:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n.Label {\n overflow: hidden;\n min-width: 0;\n padding-inline: var(--chip-label-edge);\n text-overflow: ellipsis;\n}\n\n.Icon + .Label {\n padding-inline-start: 0;\n}\n\n.Label:has(+ .Delete) {\n padding-inline-end: 0;\n}\n\n.Icon {\n display: inline-flex;\n flex-shrink: 0;\n margin-inline-start: var(--chip-edge);\n}\n\n.Icon svg {\n display: block;\n}\n\n.Delete {\n display: inline-flex;\n width: var(--chip-delete-size);\n height: var(--chip-delete-size);\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n padding: 0;\n border: 0;\n border-radius: var(--lea-radius-full);\n margin-inline-end: var(--chip-edge);\n background: none;\n color: var(--chip-shade);\n cursor: pointer;\n transition: opacity var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n.Delete svg {\n display: block;\n width: 100%;\n height: 100%;\n fill: var(--chip-tint);\n scale: calc(24 / 22);\n}\n\n.Chip[data-variant=\"outlined\"] .Delete {\n color: var(--chip-outlined-cross);\n}\n\n.Chip[data-variant=\"outlined\"] .Delete svg {\n fill: var(--chip-outlined-disc);\n}\n\n.Delete:disabled {\n cursor: not-allowed;\n}\n\n.Delete:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n@media (hover: hover) {\n .Delete:hover:not(:disabled) {\n opacity: 0.85;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Delete {\n transition-duration: 0.01ms;\n }\n}\n","import { createTranslation } from \"../../../i18n\";\n\n/**\n * Translated accessible name for `Chip`'s delete affordance, which MUI renders\n * as an unlabelled icon. Consumers can override it by passing their own\n * `deleteIcon`.\n */\nexport const useChipLabels = createTranslation({\n remove: { no: \"Fjern\", se: \"Ta bort\", fi: \"Poista\", es: \"Eliminar\" },\n});\n","\"use client\";\n\nimport { CircleX } from \"lucide-react\";\nimport type { ComponentProps, MouseEvent, ReactNode } from \"react\";\n\nimport type { ColorRoles } from \"../../color-roles\";\nimport styles from \"./chip.module.css\";\nimport { useChipLabels } from \"./use-chip-labels\";\n\nexport type ChipColor = ColorRoles<\n | \"primary\"\n | \"tertiary\"\n | \"neutral\"\n | \"muted\"\n | \"success\"\n | \"info\"\n | \"warning\"\n | \"error\"\n>;\nexport type ChipVariant = \"filled\" | \"outlined\";\nexport type ChipSize = \"sm\" | \"md\" | \"lg\";\n\nexport type ChipProps = Omit<ComponentProps<\"span\">, \"children\" | \"color\"> & {\n /** The text of the chip. */\n label: ReactNode;\n /**\n * What the chip stands for.\n * - A tag without a status: `\"primary\"` (purple), `\"tertiary\"` (teal) or\n * `\"neutral\"` (grey); on the website, `\"muted\"` (beige), as the category\n * and date of an article (with `size=\"lg\"`).\n * - A status: `\"success\"` (teal), `\"info\"` (blue), `\"warning\"` (amber) or\n * `\"error\"` (red).\n * @default \"primary\"\n */\n color?: ChipColor;\n /**\n * Tinted fill, or a border on a transparent background.\n * @default \"filled\"\n */\n variant?: ChipVariant;\n /**\n * `sm` for dense rows and inside other controls, `md` on its own, `lg` for\n * the website's pills (16px semibold, 28px tall), as the category and date\n * of an article or the facts of a job opening. Every colour and variant\n * comes in every size.\n * @default \"md\"\n */\n size?: ChipSize;\n /** Icon before the label. Mark a decorative icon `aria-hidden`. */\n icon?: ReactNode;\n /**\n * Shows a remove button after the label and is called when it is pressed.\n * The button is named in the `LeaProvider` language and includes the label,\n * so a row of chips reads unambiguously: \"Fjern Medlånetaker\".\n */\n onDelete?: (event: MouseEvent<HTMLButtonElement>) => void;\n /** Accessible name of the remove button, when the default does not fit. */\n deleteLabel?: string;\n /** Dims the chip and disables its remove button. */\n disabled?: boolean;\n};\n\n/**\n * A compact label for tags, statuses, or filter selections.\n *\n * ```tsx\n * <Chip label=\"Medlånetaker\" />\n * <Chip label=\"Verifisert\" color=\"success\" icon={<CircleCheck size={16} aria-hidden />} />\n * <Chip label=\"Oslo\" variant=\"outlined\" onDelete={() => remove(\"oslo\")} />\n * ```\n *\n * The chip itself is not interactive; only its remove button is. Inside a\n * widget that moves focus itself, `tabIndex={-1}` takes the chip and its\n * remove button out of the tab order.\n */\nexport function Chip({\n label,\n color = \"primary\",\n variant = \"filled\",\n size = \"md\",\n icon,\n onDelete,\n deleteLabel,\n disabled = false,\n className,\n tabIndex,\n ...rest\n}: ChipProps) {\n const labels = useChipLabels();\n\n const deleteName =\n deleteLabel ??\n (typeof label === \"string\" ? `${labels.remove} ${label}` : labels.remove);\n\n return (\n <span\n {...rest}\n tabIndex={tabIndex}\n className={[styles.Chip, className].filter(Boolean).join(\" \")}\n data-color={color}\n data-variant={variant}\n data-size={size}\n data-disabled={disabled ? \"\" : undefined}\n >\n {icon != null && <span className={styles.Icon}>{icon}</span>}\n <span className={styles.Label}>{label}</span>\n {onDelete && (\n <button\n type=\"button\"\n aria-label={deleteName}\n className={styles.Delete}\n disabled={disabled}\n tabIndex={tabIndex === -1 ? -1 : undefined}\n onClick={onDelete}\n >\n <CircleX aria-hidden />\n </button>\n )}\n </span>\n );\n}\n",".Positioner {\n z-index: 1500;\n}\n\n.Popup {\n --tooltip-bg: var(--lea-palette-bg-base);\n --tooltip-fg: var(--lea-palette-text-base);\n\n box-sizing: border-box;\n max-width: 18.75rem;\n padding: var(--lea-spacing-1);\n border-radius: var(--lea-radius-sm);\n background-color: var(--tooltip-bg);\n box-shadow: var(--lea-shadow-md);\n color: var(--tooltip-fg);\n font-size: var(--lea-text-body-sm-font-size);\n font-weight: var(--lea-text-body-sm-font-weight);\n letter-spacing: var(--lea-text-body-sm-letter-spacing);\n line-height: var(--lea-text-body-sm-line-height);\n outline: none;\n overflow-wrap: break-word;\n transform-origin: var(--transform-origin);\n transition: opacity var(--lea-duration-fast) var(--lea-easing-standard),\n scale var(--lea-duration-fast) var(--lea-easing-standard);\n}\n\n.Popup[data-surface=\"dark\"] {\n --tooltip-bg: var(--lea-palette-bg-contrast);\n --tooltip-fg: var(--lea-palette-text-contrast);\n}\n\n.Popup[data-starting-style],\n.Popup[data-ending-style] {\n opacity: 0;\n scale: 0.95;\n}\n\n/* A square turned on its corner, half of it behind the edge of the bubble. */\n.Arrow {\n width: 0.625rem;\n height: 0.625rem;\n background-color: var(--tooltip-bg);\n rotate: 45deg;\n}\n\n.Arrow[data-side=\"top\"] {\n bottom: -0.3125rem;\n}\n\n.Arrow[data-side=\"bottom\"] {\n top: -0.3125rem;\n}\n\n.Arrow[data-side=\"left\"] {\n right: -0.3125rem;\n}\n\n.Arrow[data-side=\"right\"] {\n left: -0.3125rem;\n}\n\n/* The built-in trigger */\n\n.TriggerBox {\n display: inline-flex;\n}\n\n/*\n * A 44px target around a 16px icon. The negative margin gives the extra\n * 14px on each side back, so the button takes the room of the icon alone and\n * lines up with the text next to it.\n */\n.Trigger {\n display: inline-flex;\n width: calc(var(--lea-spacing) * 5.5);\n height: calc(var(--lea-spacing) * 5.5);\n box-sizing: border-box;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n padding: 0;\n border: 0;\n border-radius: var(--lea-radius-full);\n margin: calc(var(--lea-spacing) * -1.75);\n background-color: transparent;\n color: var(--lea-palette-primary-base);\n cursor: pointer;\n line-height: 0;\n transition: color var(--lea-duration-fast) var(--lea-easing-standard);\n}\n\n.Trigger[data-variant=\"filled\"] {\n color: var(--lea-palette-primary-contrast);\n}\n\n.Trigger[data-variant=\"outlined\"] {\n color: var(--lea-palette-text-base);\n}\n\n.Trigger:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n@media (hover: hover) {\n .Trigger:hover {\n color: var(--lea-palette-primary-hover);\n }\n\n .Trigger[data-variant=\"filled\"]:hover {\n color: var(--lea-palette-primary-contrast);\n }\n\n .Trigger[data-variant=\"outlined\"]:hover {\n color: var(--lea-palette-primary-base);\n }\n\n .Trigger[data-variant=\"filled\"]:hover .Mark {\n background-color: var(--lea-palette-primary-hover);\n }\n}\n\n/* The default icon: an \"i\" in a circle, drawn from two bars. */\n.Mark {\n position: relative;\n display: inline-flex;\n width: var(--lea-spacing-2);\n height: var(--lea-spacing-2);\n box-sizing: border-box;\n flex-shrink: 0;\n border: 1px solid var(--lea-palette-primary-base);\n border-radius: var(--lea-radius-full);\n color: var(--lea-palette-primary-base);\n transition: background-color var(--lea-duration-fast)\n var(--lea-easing-standard);\n}\n\n.Mark::before,\n.Mark::after {\n position: absolute;\n width: calc(var(--lea-spacing) * 0.25);\n border-radius: var(--lea-radius-full);\n background-color: currentColor;\n content: \"\";\n inset: 50% auto auto 50%;\n}\n\n/* The stem and the dot, placed by eye around the centre of the circle. */\n.Mark::before {\n height: calc(var(--lea-spacing) * 0.625);\n translate: -50% -8%;\n}\n\n.Mark::after {\n height: calc(var(--lea-spacing) * 0.25);\n translate: -50% -190%;\n}\n\n.Trigger[data-variant=\"filled\"] .Mark {\n border-color: transparent;\n background-color: var(--lea-palette-primary-base);\n color: var(--lea-palette-primary-contrast);\n}\n\n.Trigger[data-variant=\"outlined\"] .Mark {\n width: calc(var(--lea-spacing) * 2.25);\n height: calc(var(--lea-spacing) * 2.25);\n border-color: var(--lea-palette-border-strong);\n background-color: var(--lea-palette-bg-base);\n color: var(--lea-palette-text-base);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Popup,\n .Trigger,\n .Mark {\n transition-duration: 0.01ms;\n }\n}\n","\"use client\";\n\nimport { Popover } from \"@base-ui/react/popover\";\nimport { Tooltip as BaseTooltip } from \"@base-ui/react/tooltip\";\nimport {\n useId,\n useState,\n type CSSProperties,\n type KeyboardEvent,\n type ReactElement,\n type ReactNode,\n} from \"react\";\n\nimport styles from \"./tooltip.module.css\";\n\nexport type TooltipTriggerVariant = \"icon\" | \"filled\" | \"outlined\";\nexport type TooltipSurfaceVariant = \"light\" | \"dark\";\nexport type TooltipOpenOn = \"hover\" | \"click\";\n\ntype Side = \"top\" | \"bottom\" | \"left\" | \"right\";\nexport type TooltipPlacement = Side | `${Side}-start` | `${Side}-end`;\n\ntype SharedProps = {\n /** What the tooltip says. Nothing is shown while it is empty. */\n title: ReactNode;\n /**\n * How the tooltip opens.\n * - `\"hover\"`: on mouse hover and on keyboard focus. It does not open on\n * touch, so use it for a hint the control works without.\n * - `\"click\"`: on click, tap, Enter or Space, and on mouse hover too. It\n * stays open until dismissed, so use it for information people need.\n *\n * Defaults to `\"hover\"` around `children` and to `\"click\"` for the\n * built-in trigger.\n */\n openOn?: TooltipOpenOn;\n /**\n * Where the tooltip sits against its trigger. It moves to another side when\n * there is no room.\n * @default \"top\"\n */\n placement?: TooltipPlacement;\n /**\n * Colours of the bubble: black text on white, or white text on black.\n * @default \"light\"\n */\n surfaceVariant?: TooltipSurfaceVariant;\n};\n\ntype WrappedTooltipProps = SharedProps & {\n /**\n * The trigger: one element that takes a `ref` and spreads its props, such\n * as a `Button`. It needs an accessible name of its own.\n */\n children: ReactElement<TriggerElementProps>;\n ariaLabel?: never;\n triggerIcon?: never;\n triggerVariant?: never;\n className?: never;\n style?: never;\n};\n\ntype TriggerTooltipProps = SharedProps & {\n children?: never;\n /** Accessible name for the built-in trigger button. */\n ariaLabel: string;\n /** Trigger icon or content. Defaults to an \"i\" in a circle. */\n triggerIcon?: ReactNode;\n /**\n * Visual style of the built-in trigger button.\n * @default \"icon\"\n */\n triggerVariant?: TooltipTriggerVariant;\n /** Class on the built-in trigger button. */\n className?: string;\n /** Inline style on the built-in trigger button. */\n style?: CSSProperties;\n};\n\nexport type TooltipProps = WrappedTooltipProps | TriggerTooltipProps;\n\ntype TriggerElementProps = {\n id?: string;\n \"aria-describedby\"?: string;\n};\n\n/** MUI showed nothing for these either, so an optional text needs no branch. */\nconst isEmpty = (title: ReactNode) =>\n title == null || title === false || title === \"\";\n\nconst position = (placement: TooltipPlacement) => {\n const [side, align = \"center\"] = placement.split(\"-\") as [\n Side,\n (\"start\" | \"end\")?,\n ];\n return { side, align, sideOffset: OFFSET } as const;\n};\n\nconst TABBABLE =\n 'a[href], button:not([disabled]), input, select, textarea, [tabindex]:not([tabindex=\"-1\"])';\n\n// The distance from the trigger to the bubble; the arrow fills half of it.\nconst OFFSET = 14;\n// Short enough to feel immediate, long enough not to flash while the pointer\n// crosses the trigger on its way elsewhere.\nconst HOVER_DELAY = 100;\n\n/**\n * A small bubble of text anchored to a trigger, on Base UI's `Tooltip` and\n * `Popover`.\n *\n * Use it two ways:\n * - **Around a child:** pass `children` to add a hint to a control you already\n * have. It opens on hover and keyboard focus and describes the control.\n * - **Built-in trigger:** omit `children` and pass `ariaLabel`; a small info\n * button is rendered for you. It opens on click, so it works on touch.\n *\n * ```tsx\n * <Tooltip title=\"Kopier kontonummer\">\n * <IconButton aria-label=\"Kopier\">…</IconButton>\n * </Tooltip>\n *\n * <Tooltip title=\"Beløpet du betaler hver måned.\" ariaLabel=\"Mer informasjon\" />\n * ```\n *\n * `openOn` switches either one: `openOn=\"click\"` around a button makes it a\n * disclosure, and `openOn=\"hover\"` makes the info button a plain hint. On\n * hover the trigger must work without the tooltip, since touch never shows it.\n */\nexport function Tooltip({\n title,\n openOn,\n placement = \"top\",\n surfaceVariant = \"light\",\n children,\n ariaLabel,\n triggerIcon,\n triggerVariant = \"icon\",\n className,\n style,\n}: TooltipProps) {\n const [open, setOpen] = useState(false);\n const generatedId = useId();\n const popupId = useId();\n\n const builtInTrigger = (\n <button\n type=\"button\"\n aria-label={ariaLabel}\n className={[styles.Trigger, className].filter(Boolean).join(\" \")}\n style={style}\n data-variant={triggerVariant}\n >\n {triggerIcon ?? <span aria-hidden className={styles.Mark} />}\n </button>\n );\n\n const trigger = children ?? builtInTrigger;\n // The negative margin of the built-in trigger needs a box of its own to pull\n // against, so it takes the room of the icon wherever it is placed.\n const wrap = (node: ReactNode) =>\n children ? node : <span className={styles.TriggerBox}>{node}</span>;\n\n if (isEmpty(title)) return wrap(trigger);\n\n const resolvedOpenOn = openOn ?? (children ? \"hover\" : \"click\");\n\n if (resolvedOpenOn === \"click\") {\n const triggerId = children?.props.id ?? generatedId;\n\n // Tab past the end of the bubble closes it, which puts focus back on the\n // trigger. Left to Base UI 1.6, focus is passed between its focus guards\n // without end when nothing on the page can take focus after the trigger.\n const closeOnTabOut = (event: KeyboardEvent<HTMLDivElement>) => {\n if (event.key !== \"Tab\" || event.shiftKey) return;\n const tabbable = event.currentTarget.querySelectorAll(TABBABLE);\n const last = tabbable[tabbable.length - 1];\n if (last == null || event.target === last) {\n event.preventDefault();\n setOpen(false);\n }\n };\n\n return (\n <Popover.Root open={open} onOpenChange={setOpen}>\n {wrap(\n <Popover.Trigger\n render={trigger}\n id={triggerId}\n openOnHover\n delay={HOVER_DELAY}\n />,\n )}\n <Popover.Portal>\n <Popover.Positioner\n {...position(placement)}\n className={styles.Positioner}\n >\n <Popover.Popup\n // Named by its trigger: \"Mer informasjon\", then the text.\n aria-labelledby={triggerId}\n onKeyDown={closeOnTabOut}\n className={styles.Popup}\n data-surface={surfaceVariant}\n >\n <Popover.Arrow className={styles.Arrow} />\n <Popover.Description render={<div />}>\n {title}\n </Popover.Description>\n </Popover.Popup>\n </Popover.Positioner>\n </Popover.Portal>\n </Popover.Root>\n );\n }\n\n const describedBy =\n [children?.props[\"aria-describedby\"], open && popupId]\n .filter(Boolean)\n .join(\" \") || undefined;\n\n return (\n <BaseTooltip.Root open={open} onOpenChange={setOpen}>\n {wrap(\n <BaseTooltip.Trigger\n render={trigger}\n delay={HOVER_DELAY}\n // Base UI leaves the tooltip out of the accessibility tree; this\n // makes it the description of its trigger while it shows.\n aria-describedby={describedBy}\n />,\n )}\n <BaseTooltip.Portal>\n <BaseTooltip.Positioner\n {...position(placement)}\n className={styles.Positioner}\n >\n <BaseTooltip.Popup\n id={popupId}\n role=\"tooltip\"\n className={styles.Popup}\n data-surface={surfaceVariant}\n >\n <BaseTooltip.Arrow className={styles.Arrow} />\n {title}\n </BaseTooltip.Popup>\n </BaseTooltip.Positioner>\n </BaseTooltip.Portal>\n </BaseTooltip.Root>\n );\n}\n","/* The website's testimonial avatar: a 48px disc, grey until the photo shows. */\n.Avatar {\n display: inline-flex;\n overflow: hidden;\n width: var(--lea-spacing-6);\n height: var(--lea-spacing-6);\n flex-shrink: 0;\n border-radius: var(--lea-radius-full);\n background-color: var(--lea-palette-bg-subtle);\n vertical-align: middle;\n}\n\n.Image {\n display: block;\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n\n.Fallback {\n display: block;\n width: 100%;\n height: 100%;\n}\n","\"use client\";\n\nimport { Avatar as BaseAvatar } from \"@base-ui/react/avatar\";\nimport type { ReactElement } from \"react\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport styles from \"./avatar.module.css\";\n\nexport type AvatarProps = Omit<BaseAvatar.Root.Props, \"children\"> & {\n /**\n * The photo. Without one, while it loads and when it fails, the avatar is a\n * grey disc.\n */\n src?: string;\n /**\n * What the photo shows, for screen readers. Use `\"\"` when the person's name\n * is written beside it, as in a testimonial, so it is not read twice.\n */\n alt: string;\n /**\n * The image element to render instead of an `<img>`, such as `next/image`:\n * `imageRender={<NextImage src={photo} alt=\"\" width={48} height={48} />}`.\n * Pass the photo in `src` as well: the avatar loads it to know when to\n * show the image.\n */\n imageRender?: ReactElement;\n};\n\n/**\n * A round 48px photo of a person, on Base UI's `Avatar`: the photo when it has\n * loaded, a grey disc until then or without one.\n *\n * ```tsx\n * <Avatar src=\"/kunder/kari.jpg\" alt=\"\" />\n * ```\n *\n * `alt` is required. With a name in it, the disc carries the name while\n * there is no photo; with `\"\"`, the avatar is hidden from screen readers.\n */\nexport function Avatar({\n src,\n alt,\n imageRender,\n className,\n ...rest\n}: AvatarProps) {\n return (\n <BaseAvatar.Root\n {...rest}\n className={mergeClassName(styles.Avatar, className)}\n >\n {src != null && (\n <BaseAvatar.Image\n src={src}\n alt={alt}\n render={imageRender}\n className={styles.Image}\n />\n )}\n <BaseAvatar.Fallback\n className={styles.Fallback}\n role={alt ? \"img\" : undefined}\n aria-label={alt || undefined}\n aria-hidden={alt ? undefined : true}\n />\n </BaseAvatar.Root>\n );\n}\n","/*\n * The website's stars: 20 × 19, 2px apart, filled with the text colour, so\n * they follow the surface they sit on.\n */\n.StarRating {\n display: inline-flex;\n align-items: center;\n gap: calc(var(--lea-spacing) * 0.25);\n vertical-align: middle;\n}\n\n.Star {\n display: block;\n width: 1.25rem;\n height: 1.1875rem;\n flex-shrink: 0;\n fill: currentColor;\n}\n","import { createTranslation } from \"../../../i18n\";\n\n/**\n * The accessible name of `StarRating`. `{rating}` and `{max}` are replaced\n * with the numbers.\n */\nexport const useStarRatingLabels = createTranslation({\n label: {\n no: \"{rating} av {max} stjerner\",\n se: \"{rating} av {max} stjärnor\",\n fi: \"{rating}/{max} tähteä\",\n es: \"{rating} de {max} estrellas\",\n },\n});\n","\"use client\";\n\nimport type { ComponentProps } from \"react\";\n\nimport styles from \"./star-rating.module.css\";\nimport { useStarRatingLabels } from \"./use-star-rating-labels\";\n\nexport type StarRatingProps = Omit<ComponentProps<\"span\">, \"children\"> & {\n /**\n * How many stars, from 0 to `max`. Rounded to a whole star and kept within\n * the range.\n */\n rating: number;\n /**\n * The best rating there is, for the accessible name: \"4 av 5 stjerner\".\n * @default 5\n */\n max?: number;\n};\n\n/** The website's star (leabank.no's `star.svg`), drawn 20 × 19. */\nconst STAR_PATH =\n \"M9.07088 0.612344C9.41462 -0.204115 10.5854 -0.204114 10.9291 0.612346L12.9579 5.43123C13.1029 5.77543 13.4306 6.01061 13.8067 6.0404L19.0727 6.45748C19.9649 6.52814 20.3267 7.62813 19.6469 8.2034L15.6348 11.5987C15.3482 11.8412 15.223 12.2218 15.3106 12.5843L16.5363 17.661C16.744 18.5211 15.7969 19.201 15.033 18.7401L10.5245 16.0196C10.2025 15.8252 9.7975 15.8252 9.47548 16.0196L4.96699 18.7401C4.20311 19.201 3.25596 18.5211 3.46363 17.661L4.68942 12.5843C4.77698 12.2218 4.65182 11.8412 4.36526 11.5987L0.353062 8.2034C-0.326718 7.62813 0.0350679 6.52814 0.927291 6.45748L6.19336 6.0404C6.5695 6.01061 6.89716 5.77543 7.04207 5.43123L9.07088 0.612344Z\";\n\n/**\n * A read-only rating in stars, as on the website's testimonials. Only the\n * stars of the rating are drawn (a 4 is four stars), in the colour of the\n * text around it.\n *\n * ```tsx\n * <StarRating rating={4} />\n * ```\n *\n * It is one image to a screen reader, named \"4 av 5 stjerner\" in the\n * `LeaProvider` language; pass `aria-label` for other words.\n */\nexport function StarRating({\n rating,\n max = 5,\n className,\n ...rest\n}: StarRatingProps) {\n const labels = useStarRatingLabels();\n const best = Math.max(0, Math.round(max));\n const stars = Math.min(Math.max(0, Math.round(rating)), best);\n\n return (\n <span\n role=\"img\"\n aria-label={labels.label\n .replace(\"{rating}\", String(stars))\n .replace(\"{max}\", String(best))}\n {...rest}\n className={[styles.StarRating, className].filter(Boolean).join(\" \")}\n >\n {Array.from({ length: stars }, (_, index) => (\n <svg\n key={index}\n className={styles.Star}\n viewBox=\"0 0 20 19\"\n aria-hidden\n focusable=\"false\"\n >\n <path d={STAR_PATH} />\n </svg>\n ))}\n </span>\n );\n}\n","/*\n * The website's price table: a 1px line under every row (the website's grey,\n * `neutral-600`), no stripes, no column lines. `lg` is 16px text with 16px\n * around it, so a one-line row is 52px and its line; `sm` is 14px text with\n * 8px around it. Without `size` the table is `lg` and turns `sm` below\n * 640px, where the website's does.\n */\n/*\n * The box around the table: it scrolls sideways when the table is wider than\n * its column. While it does, it can take focus (see table.tsx).\n */\n.Scroll {\n overflow-x: auto;\n max-width: 100%;\n}\n\n.Scroll:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n.Table {\n --table-padding: var(--lea-spacing-2);\n --table-font-size: var(--lea-font-step-0);\n\n width: 100%;\n border-collapse: collapse;\n border-spacing: 0;\n color: var(--lea-palette-text-base);\n}\n\n.Table[data-size=\"sm\"] {\n --table-padding: var(--lea-spacing-1);\n --table-font-size: var(--lea-font-step-minus-1);\n}\n\n@media (max-width: 639.95px) {\n .Table:not([data-size]) {\n --table-padding: var(--lea-spacing-1);\n --table-font-size: var(--lea-font-step-minus-1);\n }\n}\n\n.Row {\n border-bottom: 1px solid var(--lea-color-neutral-600);\n}\n\n/*\n * The text of a cell: the size of the table, with the website's \"normal\"\n * line height, which the tight line height matches (16px text in a 20px\n * line). Long words break rather than push the table wider than its column.\n */\n.HeaderCell,\n.Cell {\n padding: var(--table-padding);\n font-size: var(--table-font-size);\n line-height: var(--lea-line-height-tight);\n letter-spacing: 0;\n text-align: center;\n vertical-align: middle;\n overflow-wrap: break-word;\n}\n\n.HeaderCell {\n font-weight: var(--lea-font-weight-semibold);\n}\n\n.Cell {\n font-weight: var(--lea-font-weight-regular);\n}\n\n/* The first column at the start, the last at the end, the rest centred. */\n.HeaderCell:first-child,\n.Cell:first-child {\n text-align: start;\n}\n\n.HeaderCell:last-child,\n.Cell:last-child {\n text-align: end;\n}\n\n/* `align` on a cell wins over its place in the row. */\n.HeaderCell[data-align=\"start\"],\n.Cell[data-align=\"start\"] {\n text-align: start;\n}\n\n.HeaderCell[data-align=\"center\"],\n.Cell[data-align=\"center\"] {\n text-align: center;\n}\n\n.HeaderCell[data-align=\"end\"],\n.Cell[data-align=\"end\"] {\n text-align: end;\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport {\n createContext,\n useContext,\n useEffect,\n useRef,\n useState,\n type Ref,\n} from \"react\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport styles from \"./table.module.css\";\n\nexport type TableSize = \"sm\" | \"lg\";\nexport type TableCellAlign = \"start\" | \"center\" | \"end\";\n\n/** Which part of the table a cell is in, for the `scope` of a header cell. */\nconst TableSectionContext = createContext<\"head\" | \"body\" | undefined>(\n undefined,\n);\n\nexport type TableProps = Omit<useRender.ComponentProps<\"table\">, \"ref\"> & {\n /** The rendered element: the `<table>`, or the element passed to `render`. */\n ref?: Ref<HTMLTableElement>;\n /**\n * `\"lg\"`: 16px text and 16px around it, rows 53px tall with their line.\n * `\"sm\"`: 14px text and 8px around it, for a narrow column. Left out, the\n * table is `lg` and turns `sm` below 640px, as on the website.\n */\n size?: TableSize;\n};\n\n/**\n * A plain data table, as the website's price tables: a 1px line under every\n * row, no stripes and no column lines. The first column is aligned to the\n * start, the last to the end and the ones between to the centre; text wraps\n * inside its cell.\n *\n * ```tsx\n * <Table aria-label=\"Priseksempler\">\n * <TableHead>\n * <TableRow>\n * <TableHeaderCell>Beløp</TableHeaderCell>\n * <TableHeaderCell>Pr. måned</TableHeaderCell>\n * </TableRow>\n * </TableHead>\n * <TableBody>\n * <TableRow>\n * <TableCell>50 000 kr</TableCell>\n * <TableCell>1 060 kr</TableCell>\n * </TableRow>\n * </TableBody>\n * </Table>\n * ```\n *\n * The header row is optional: the live price tables have none. The parts\n * render the native table elements, so they nest only as a table's do. Name\n * the table with `aria-label`, or `aria-labelledby` pointing at the heading\n * above it.\n *\n * The table sits in a box of its own that scrolls sideways when the table is\n * wider than its column, as on the website. While it scrolls, the box can be\n * focused, so it scrolls from the keyboard too, and carries the table's name.\n * `className`, `style` and `ref` are the table's.\n */\nexport function Table({ render, ref, className, size, ...rest }: TableProps) {\n const scrollRef = useRef<HTMLDivElement>(null);\n const [scrollable, setScrollable] = useState(false);\n\n useEffect(() => {\n const box = scrollRef.current;\n if (!box || typeof ResizeObserver === \"undefined\") return;\n\n const measure = () => setScrollable(box.scrollWidth > box.clientWidth);\n const observer = new ResizeObserver(measure);\n\n observer.observe(box);\n if (box.firstElementChild) observer.observe(box.firstElementChild);\n measure();\n\n return () => observer.disconnect();\n }, []);\n\n const table = useRender({\n render,\n ref,\n defaultTagName: \"table\",\n props: {\n ...rest,\n \"data-size\": size,\n className: mergeClassName(styles.Table, className)({}),\n },\n });\n\n return (\n <div\n ref={scrollRef}\n className={styles.Scroll}\n {...(scrollable && {\n role: \"region\",\n tabIndex: 0,\n \"aria-label\": rest[\"aria-label\"],\n \"aria-labelledby\": rest[\"aria-labelledby\"],\n })}\n >\n {table}\n </div>\n );\n}\n\nexport type TableHeadProps = Omit<\n useRender.ComponentProps<\"thead\">,\n \"ref\"\n> & {\n /** The rendered element: the `<thead>`, or the element passed to `render`. */\n ref?: Ref<HTMLTableSectionElement>;\n};\n\n/** The header row group, `<thead>`. Leave it out for a table without one. */\nexport function TableHead({ render, ref, ...rest }: TableHeadProps) {\n const element = useRender({\n render,\n ref,\n defaultTagName: \"thead\",\n props: rest,\n });\n\n return (\n <TableSectionContext.Provider value=\"head\">\n {element}\n </TableSectionContext.Provider>\n );\n}\n\nexport type TableBodyProps = Omit<\n useRender.ComponentProps<\"tbody\">,\n \"ref\"\n> & {\n /** The rendered element: the `<tbody>`, or the element passed to `render`. */\n ref?: Ref<HTMLTableSectionElement>;\n};\n\n/** The rows of data, `<tbody>`. */\nexport function TableBody({ render, ref, ...rest }: TableBodyProps) {\n const element = useRender({\n render,\n ref,\n defaultTagName: \"tbody\",\n props: rest,\n });\n\n return (\n <TableSectionContext.Provider value=\"body\">\n {element}\n </TableSectionContext.Provider>\n );\n}\n\nexport type TableRowProps = Omit<useRender.ComponentProps<\"tr\">, \"ref\"> & {\n /** The rendered element: the `<tr>`, or the element passed to `render`. */\n ref?: Ref<HTMLTableRowElement>;\n};\n\n/** A row, `<tr>`, with the 1px line under it. */\nexport function TableRow({ render, ref, className, ...rest }: TableRowProps) {\n return useRender({\n render,\n ref,\n defaultTagName: \"tr\",\n props: {\n ...rest,\n className: mergeClassName(styles.Row, className)({}),\n },\n });\n}\n\ntype CellAlignProps = {\n /**\n * Where the text sits in the cell. Left out, the first cell of a row is at\n * the start, the last at the end and the others in the centre. (It stands\n * in for HTML's obsolete `align` attribute, and is written as\n * `data-align`.)\n */\n align?: TableCellAlign;\n};\n\nexport type TableHeaderCellProps = Omit<\n useRender.ComponentProps<\"th\">,\n \"ref\" | \"align\"\n> &\n CellAlignProps & {\n /** The rendered element: the `<th>`, or the element passed to `render`. */\n ref?: Ref<HTMLTableCellElement>;\n };\n\n/**\n * A header cell, `<th>`, in semibold. In `TableHead` it heads its column\n * (`scope=\"col\"`). At the start of a row in `TableBody` it heads the row\n * (`scope=\"row\"`), so a screen reader reads the label with each value of a\n * label / value table.\n */\nexport function TableHeaderCell({\n render,\n ref,\n className,\n align,\n scope,\n ...rest\n}: TableHeaderCellProps) {\n const section = useContext(TableSectionContext);\n\n return useRender({\n render,\n ref,\n defaultTagName: \"th\",\n props: {\n scope: scope ?? (section === \"body\" ? \"row\" : \"col\"),\n ...rest,\n \"data-align\": align,\n className: mergeClassName(styles.HeaderCell, className)({}),\n },\n });\n}\n\nexport type TableCellProps = Omit<\n useRender.ComponentProps<\"td\">,\n \"ref\" | \"align\"\n> &\n CellAlignProps & {\n /** The rendered element: the `<td>`, or the element passed to `render`. */\n ref?: Ref<HTMLTableCellElement>;\n };\n\n/** A data cell, `<td>`. */\nexport function TableCell({\n render,\n ref,\n className,\n align,\n ...rest\n}: TableCellProps) {\n return useRender({\n render,\n ref,\n defaultTagName: \"td\",\n props: {\n ...rest,\n \"data-align\": align,\n className: mergeClassName(styles.Cell, className)({}),\n },\n });\n}\n","/*\n * Text from the CMS, as the website's FreeBlock styles it, on Paloma's text\n * styles: headings h1 to h4 on `display-md`, `heading-xl`, `heading-md` and\n * `heading-sm`, paragraphs and lists on `body-lg` (the website's 18px), 16px\n * after each block, more around quotes.\n *\n * Every rule is for a bare element (`:not([class])`), the way the CMS writes\n * it, so a Paloma component placed inside (a `Text`, a `Link`, an `Alert`)\n * keeps its own look.\n */\n.RichText {\n color: var(--lea-palette-text-base);\n font-size: var(--lea-text-body-lg-font-size);\n font-weight: var(--lea-text-body-lg-font-weight);\n line-height: var(--lea-text-body-lg-line-height);\n letter-spacing: var(--lea-text-body-lg-letter-spacing);\n overflow-wrap: break-word;\n}\n\n/*\n * No space above the first block or below the last, so the text sits flush\n * in a Panel. Specific enough to beat the block rules below.\n */\n.RichText > :first-child:not([class]) {\n margin-top: 0;\n}\n\n.RichText > :last-child:not([class]) {\n margin-bottom: 0;\n}\n\n/* Headings */\n.RichText :is(h1, h2, h3, h4, h5, h6):not([class]) {\n margin: 0 0 var(--lea-spacing-2);\n color: inherit;\n}\n\n.RichText h1:not([class]) {\n font-size: var(--lea-text-display-md-font-size);\n font-weight: var(--lea-text-display-md-font-weight);\n line-height: var(--lea-text-display-md-line-height);\n letter-spacing: var(--lea-text-display-md-letter-spacing);\n}\n\n.RichText h2:not([class]) {\n font-size: var(--lea-text-heading-xl-font-size);\n font-weight: var(--lea-text-heading-xl-font-weight);\n line-height: var(--lea-text-heading-xl-line-height);\n letter-spacing: var(--lea-text-heading-xl-letter-spacing);\n}\n\n.RichText h3:not([class]) {\n font-size: var(--lea-text-heading-md-font-size);\n font-weight: var(--lea-text-heading-md-font-weight);\n line-height: var(--lea-text-heading-md-line-height);\n letter-spacing: var(--lea-text-heading-md-letter-spacing);\n}\n\n.RichText h4:not([class]) {\n font-size: var(--lea-text-heading-sm-font-size);\n font-weight: var(--lea-text-heading-sm-font-weight);\n line-height: var(--lea-text-heading-sm-line-height);\n letter-spacing: var(--lea-text-heading-sm-letter-spacing);\n}\n\n.RichText :is(h5, h6):not([class]) {\n font-size: var(--lea-text-heading-xs-font-size);\n font-weight: var(--lea-text-heading-xs-font-weight);\n line-height: var(--lea-text-heading-xs-line-height);\n letter-spacing: var(--lea-text-heading-xs-letter-spacing);\n}\n\n/*\n * Smaller headings where the website's step down: h1 below 1024px, h2 to h4\n * below 768px. The website takes h1 to 28px below 1024px, under its 40px h2;\n * here h1 steps down twice, so it stays the largest heading at every width.\n */\n@media (max-width: 1023.95px) {\n .RichText h1:not([class]) {\n font-size: var(--lea-text-display-sm-font-size);\n font-weight: var(--lea-text-display-sm-font-weight);\n line-height: var(--lea-text-display-sm-line-height);\n letter-spacing: var(--lea-text-display-sm-letter-spacing);\n }\n}\n\n@media (max-width: 767.95px) {\n .RichText h1:not([class]) {\n font-size: var(--lea-text-heading-lg-font-size);\n font-weight: var(--lea-text-heading-lg-font-weight);\n line-height: var(--lea-text-heading-lg-line-height);\n letter-spacing: var(--lea-text-heading-lg-letter-spacing);\n }\n\n .RichText h2:not([class]) {\n font-size: var(--lea-text-heading-md-font-size);\n font-weight: var(--lea-text-heading-md-font-weight);\n line-height: var(--lea-text-heading-md-line-height);\n letter-spacing: var(--lea-text-heading-md-letter-spacing);\n }\n\n .RichText h3:not([class]) {\n font-size: var(--lea-text-heading-sm-font-size);\n font-weight: var(--lea-text-heading-sm-font-weight);\n line-height: var(--lea-text-heading-sm-line-height);\n letter-spacing: var(--lea-text-heading-sm-letter-spacing);\n }\n\n .RichText h4:not([class]) {\n font-size: var(--lea-text-heading-xs-font-size);\n font-weight: var(--lea-text-heading-xs-font-weight);\n line-height: var(--lea-text-heading-xs-line-height);\n letter-spacing: var(--lea-text-heading-xs-letter-spacing);\n }\n}\n\n/* Paragraphs */\n.RichText p:not([class]) {\n margin: 0 0 var(--lea-spacing-2);\n}\n\n/* Marks. The font has no italic face, so the browser slants the regular. */\n.RichText :is(strong, b):not([class]) {\n font-weight: var(--lea-font-weight-semibold);\n}\n\n.RichText :is(em, i):not([class]) {\n font-style: italic;\n}\n\n.RichText u:not([class]) {\n text-decoration: underline;\n}\n\n.RichText s:not([class]) {\n text-decoration: line-through;\n}\n\n/*\n * Links: purple and underlined, so they stand apart from the words around\n * them by more than colour. Hover thickens the line, as a plain `Link`.\n */\n.RichText a:not([class]) {\n color: var(--lea-palette-text-accent);\n text-decoration: underline;\n text-decoration-thickness: 0.0625rem;\n /* Clears the descenders of the font, as `Link`; there is no token for it. */\n text-underline-offset: 0.3rem;\n}\n\n.RichText a:not([class]):focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n@media (hover: hover) {\n .RichText a:not([class]):hover {\n text-decoration-thickness: 0.125rem;\n }\n}\n\n/* Lists: 16px above and below, 32px indent, 8px between the items. */\n.RichText :is(ul, ol):not([class]) {\n padding-inline-start: var(--lea-spacing-4);\n margin: var(--lea-spacing-2) 0;\n}\n\n.RichText ul:not([class]) {\n list-style-type: disc;\n}\n\n.RichText ol:not([class]) {\n list-style-type: decimal;\n}\n\n.RichText li:not([class]) {\n margin-bottom: var(--lea-spacing-1);\n}\n\n/* A quote: a 2px grey line at the start, 32px above and below. */\n.RichText blockquote:not([class]) {\n padding-inline-start: var(--lea-spacing-2);\n border-inline-start: 2px solid var(--lea-color-neutral-600);\n margin: var(--lea-spacing-4) 0;\n}\n\n/* Code, in the website's Courier New at 0.9em, on grey. */\n.RichText code:not([class]) {\n padding: var(--lea-spacing-0-5) var(--lea-spacing-1);\n border-radius: var(--lea-radius-sm);\n background-color: var(--lea-palette-bg-subtle);\n font-family: var(--lea-font-family-mono);\n font-size: 0.9em;\n}\n\n.RichText pre:not([class]) {\n overflow-x: auto;\n padding: var(--lea-spacing-2);\n border-radius: var(--lea-radius-xl);\n margin: var(--lea-spacing-2) 0;\n background-color: var(--lea-palette-bg-subtle);\n font-family: var(--lea-font-family-mono);\n}\n\n.RichText pre:not([class]) code:not([class]) {\n padding: 0;\n border-radius: 0;\n background-color: transparent;\n}\n\n/* Images fill the width, with the website's 64px corners. */\n.RichText img:not([class]) {\n display: block;\n width: 100%;\n height: auto;\n border-radius: var(--lea-radius-huge);\n margin: var(--lea-spacing-2) 0;\n object-fit: cover;\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport type { Ref } from \"react\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport styles from \"./rich-text.module.css\";\n\nexport type RichTextProps = Omit<useRender.ComponentProps<\"div\">, \"ref\"> & {\n /** The rendered element: the `<div>`, or the element passed to `render`. */\n ref?: Ref<HTMLElement>;\n};\n\n/**\n * Styles text from the CMS: headings, paragraphs, bold, italic, underline\n * and strikethrough, lists, links, quotes, code and images, on Paloma's text\n * styles and the website's spacing. It styles the elements inside it and\n * renders nothing of its own but a `div`.\n *\n * ```tsx\n * <RichText>{renderBlocks(blocks)}</RichText>\n * ```\n *\n * It does not sanitise. Markup from the CMS as React elements (as the\n * website's `renderBlocks` makes them) is safe as it is; an HTML string has\n * to be sanitised first (the website uses DOMPurify) and goes in through\n * `dangerouslySetInnerHTML`.\n *\n * Only bare elements are styled, the way the CMS writes them: an element with\n * a class of its own, such as a Paloma `Text` or `Link`, keeps its own look.\n * Headings step down on narrow screens as on the website; set `text-align`\n * with `className` or `style` to centre the text.\n */\nexport function RichText({ render, ref, className, ...rest }: RichTextProps) {\n return useRender({\n render,\n ref,\n defaultTagName: \"div\",\n props: {\n ...rest,\n className: mergeClassName(styles.RichText, className)({}),\n },\n });\n}\n","/*\n * The website's featured photo (HeroServiceGrid, FlexiblePromoBlock): `huge`\n * corners that crop the photo, no border, no shadow. From 1024px it fills the\n * height of its row (600px in the website's grids); below that it is\n * 1 : 0.7. The action sits 32px in from the bottom right corner.\n */\n.FeaturedImage {\n position: relative;\n overflow: hidden;\n width: 100%;\n border-radius: var(--lea-radius-huge);\n /* Behind the photo while it loads. */\n background-color: var(--lea-palette-bg-muted);\n}\n\n@media (min-width: 1024px) {\n .FeaturedImage {\n height: 100%;\n }\n}\n\n@media (max-width: 1023.95px) {\n .FeaturedImage {\n aspect-ratio: 1 / 0.7;\n }\n}\n\n.Image {\n display: block;\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n\n.Action {\n position: absolute;\n inset-block-end: var(--lea-spacing-4);\n inset-inline-end: var(--lea-spacing-4);\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport type { ComponentProps, ReactElement, ReactNode } from \"react\";\n\nimport styles from \"./featured-image.module.css\";\n\nexport type FeaturedImageProps = Omit<ComponentProps<\"div\">, \"children\"> & {\n /** The photo. */\n src: string;\n /**\n * What the photo shows, for screen readers; `\"\"` when it is decoration\n * and the text beside it says it all.\n */\n alt: string;\n /**\n * The image element to render instead of the plain `<img>`, such as\n * `next/image` with `fill`: `imageRender={<NextImage src={photo} alt=\"\"\n * fill sizes=\"(min-width: 1024px) 50vw, 100vw\" />}`, or an `<img>` with\n * attributes of its own (`loading=\"lazy\"` below the fold, `srcSet`). Its\n * own props win over `src` and `alt`.\n */\n imageRender?: ReactElement;\n /**\n * A call to action on the photo, 32px from its bottom right corner: an\n * `ActionButton`, `size=\"lg\"` on wide screens and `\"sm\"` on phones.\n */\n action?: ReactNode;\n};\n\n/**\n * A large photo with the website's 64px corners, and optionally a call to\n * action on it, as in the website's hero grid and promo block.\n *\n * ```tsx\n * <FeaturedImage\n * src=\"/bilder/familie.jpg\"\n * alt=\"En familie på kjøkkenet\"\n * action={\n * <ActionButton render={<NextLink href=\"/lan\" />}>Se våre lån</ActionButton>\n * }\n * />\n * ```\n *\n * From 1024px it fills the height its row gives it (the photo is cropped to\n * fit); below that it keeps the website's 1 : 0.7. The action comes after\n * the photo in the reading order.\n */\nexport function FeaturedImage({\n src,\n alt,\n imageRender,\n action,\n className,\n ...rest\n}: FeaturedImageProps) {\n const image = useRender({\n render: imageRender,\n defaultTagName: \"img\",\n props: { src, alt, className: styles.Image },\n });\n\n return (\n <div\n {...rest}\n className={[styles.FeaturedImage, className].filter(Boolean).join(\" \")}\n >\n {image}\n {action != null && <div className={styles.Action}>{action}</div>}\n </div>\n );\n}\n","/*\n * The website's video block: a 16 : 9 video with `huge` corners, black until\n * the first frame shows, and the elevated play / pause button centred 24px\n * under it (the website's 64px from the video's edge to the button's, less\n * the 40px button). The button takes its place in the flow, so nothing\n * after the player runs under it.\n */\n.VideoPlayer {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--lea-spacing-3);\n width: 100%;\n}\n\n.Video {\n display: block;\n width: 100%;\n aspect-ratio: 16 / 9;\n border-radius: var(--lea-radius-huge);\n background-color: var(--lea-palette-bg-contrast);\n object-fit: cover;\n}\n","import { createTranslation } from \"../../../i18n\";\n\n/** The names of `VideoPlayer`'s button, for a paused and a playing video. */\nexport const useVideoPlayerLabels = createTranslation({\n play: {\n no: \"Spill av video\",\n se: \"Spela upp video\",\n fi: \"Toista video\",\n es: \"Reproducir vídeo\",\n },\n pause: {\n no: \"Sett video på pause\",\n se: \"Pausa video\",\n fi: \"Keskeytä video\",\n es: \"Pausar vídeo\",\n },\n});\n","\"use client\";\n\nimport { Pause, Play } from \"lucide-react\";\nimport { useEffect, useRef, useState, type ComponentProps } from \"react\";\n\nimport { IconButton } from \"../../buttons/icon-button/icon-button\";\nimport styles from \"./video-player.module.css\";\nimport { useVideoPlayerLabels } from \"./use-video-player-labels\";\n\nconst REDUCED_MOTION = \"(prefers-reduced-motion: reduce)\";\n\nexport type VideoPlayerProps = Omit<ComponentProps<\"div\">, \"children\"> & {\n /** The video file. It plays without sound, so it should need none. */\n src: string;\n /** A still to show before the video starts, and while it is paused. */\n poster?: string;\n};\n\n/**\n * A silent video that plays in a loop, with the website's 64px corners and a\n * play / pause button centred under it, 16 : 9 at the width it is given.\n *\n * ```tsx\n * <VideoPlayer src=\"/video/lea-bank.mp4\" poster=\"/video/lea-bank.jpg\" />\n * ```\n *\n * It starts on its own once the page has loaded, unless the visitor has\n * asked for reduced motion: then it waits for the button. The button is named\n * \"Spill av video\" or \"Sett video på pause\" in the `LeaProvider` language.\n * The video itself is hidden from screen readers, as decoration: anything it\n * tells has to be in the text around it.\n */\nexport function VideoPlayer({\n src,\n poster,\n className,\n ...rest\n}: VideoPlayerProps) {\n const labels = useVideoPlayerLabels();\n const videoRef = useRef<HTMLVideoElement>(null);\n const [playing, setPlaying] = useState(false);\n\n useEffect(() => {\n const video = videoRef.current;\n if (!video) return;\n\n const reducedMotion = window.matchMedia(REDUCED_MOTION);\n\n // Started here rather than with `autoPlay`, so that reduced motion is\n // known before the first frame moves. A browser that blocks it leaves\n // the video paused, with the button to start it.\n if (!reducedMotion.matches) {\n video.muted = true;\n video.play().catch(() => {});\n }\n\n const stopOnReducedMotion = (event: MediaQueryListEvent) => {\n if (event.matches) video.pause();\n };\n\n reducedMotion.addEventListener(\"change\", stopOnReducedMotion);\n\n return () =>\n reducedMotion.removeEventListener(\"change\", stopOnReducedMotion);\n }, []);\n\n const toggle = () => {\n const video = videoRef.current;\n if (!video) return;\n\n if (video.paused) {\n video.play().catch(() => {});\n } else {\n video.pause();\n }\n };\n\n return (\n <div\n {...rest}\n className={[styles.VideoPlayer, className].filter(Boolean).join(\" \")}\n >\n <video\n ref={videoRef}\n className={styles.Video}\n src={src}\n poster={poster}\n muted\n loop\n playsInline\n aria-hidden\n onPlay={() => setPlaying(true)}\n onPause={() => setPlaying(false)}\n />\n <IconButton\n variant=\"elevated\"\n color=\"secondary\"\n aria-label={playing ? labels.pause : labels.play}\n onClick={toggle}\n >\n {playing ? <Pause aria-hidden /> : <Play aria-hidden />}\n </IconButton>\n </div>\n );\n}\n","/*\n * The website's round arrow (Figma `.ArrowCircle`): 48px with a 24px arrow,\n * or 32px with a 20px arrow. The outline takes the colour of the text around\n * it, so it is white on a dark card. It has no hover of its own: the card\n * moves it.\n */\n.ArrowCircle {\n --arrow-circle-size: var(--lea-spacing-6);\n --arrow-circle-arrow: var(--lea-spacing-3);\n\n display: inline-flex;\n width: var(--arrow-circle-size);\n height: var(--arrow-circle-size);\n box-sizing: border-box;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n border-radius: var(--lea-radius-full);\n}\n\n.ArrowCircle[data-size=\"sm\"] {\n --arrow-circle-size: var(--lea-spacing-4);\n --arrow-circle-arrow: var(--lea-spacing-2-5);\n}\n\n.ArrowCircle[data-variant=\"outline\"] {\n border: 2px solid currentColor;\n}\n\n.ArrowCircle[data-variant=\"filled\"] {\n background-color: var(--lea-palette-bg-contrast);\n color: var(--lea-palette-text-contrast);\n}\n\n.Arrow {\n display: block;\n width: var(--arrow-circle-arrow);\n height: var(--arrow-circle-arrow);\n}\n","import { ArrowRight } from \"lucide-react\";\n\nimport styles from \"./arrow-circle.module.css\";\n\nexport type ArrowCircleProps = {\n /**\n * `\"outline\"`: a 2px ring and the arrow in the text colour, as on a\n * `LinkCard`. `\"filled\"`: a near-black disc with a white arrow, the arrow of\n * the website's call-to-action button.\n * @default \"outline\"\n */\n variant?: \"outline\" | \"filled\";\n /**\n * `\"lg\"` is 48px with a 24px arrow, `\"sm\"` 32px with a 20px arrow.\n * @default \"lg\"\n */\n size?: \"sm\" | \"lg\";\n className?: string;\n};\n\n/**\n * The round arrow of the website's cards (Figma `.ArrowCircle`). Decoration:\n * hidden from screen readers, and the card it sits in moves it on hover.\n * Internal: not exported from the package.\n */\nexport function ArrowCircle({\n variant = \"outline\",\n size = \"lg\",\n className,\n}: ArrowCircleProps) {\n return (\n <span\n aria-hidden\n data-variant={variant}\n data-size={size}\n className={[styles.ArrowCircle, className].filter(Boolean).join(\" \")}\n >\n <ArrowRight className={styles.Arrow} />\n </span>\n );\n}\n","/*\n * The website's SimpleCard: `huge` corners, 32px inside, the title at the top\n * and the arrow at the bottom, at least 240px tall. Hover tints the card with\n * the website's own purple (`bg.hover`, or `bg.contrast-hover` on the dark\n * surfaces) and slides the arrow 8px, over `fast`, as on leabank.no.\n */\n.LinkCard {\n --link-card-bg: var(--lea-palette-bg-base);\n --link-card-hover: var(--lea-palette-bg-hover);\n --link-card-text: var(--lea-palette-text-base);\n\n display: flex;\n /* The website's min-height: 240px. */\n min-height: calc(var(--lea-spacing) * 30);\n box-sizing: border-box;\n flex-direction: column;\n justify-content: space-between;\n gap: var(--lea-spacing-4);\n padding: var(--lea-spacing-4);\n border-radius: var(--lea-radius-huge);\n background-color: var(--link-card-bg);\n color: var(--link-card-text);\n text-decoration: none;\n transition: background-color var(--lea-duration-fast)\n var(--lea-easing-standard);\n -webkit-tap-highlight-color: transparent;\n}\n\n.LinkCard[data-surface=\"subtle\"] {\n --link-card-bg: var(--lea-palette-bg-subtle);\n}\n\n.LinkCard[data-surface=\"muted\"] {\n --link-card-bg: var(--lea-palette-bg-muted);\n}\n\n.LinkCard[data-surface=\"contrast\"] {\n --link-card-bg: var(--lea-palette-bg-contrast);\n --link-card-hover: var(--lea-palette-bg-contrast-hover);\n --link-card-text: var(--lea-palette-text-contrast);\n}\n\n.LinkCard[data-surface=\"contrast-accent\"] {\n --link-card-bg: var(--lea-palette-bg-contrast-accent);\n --link-card-hover: var(--lea-palette-bg-contrast-hover);\n --link-card-text: var(--lea-palette-text-contrast);\n}\n\n/*\n * A white ring inside a near-black one, so the focus shows on whichever\n * section the card sits on, dark or light.\n */\n.LinkCard:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n box-shadow: 0 0 0 0.125rem var(--lea-palette-bg-base);\n}\n\n.Title {\n min-width: 0;\n margin: 0;\n font-size: var(--lea-text-title-md-font-size);\n font-weight: var(--lea-text-title-md-font-weight);\n line-height: var(--lea-text-title-md-line-height);\n letter-spacing: var(--lea-text-title-md-letter-spacing);\n overflow-wrap: break-word;\n}\n\n.Arrow {\n align-self: flex-start;\n transition: translate var(--lea-duration-fast) var(--lea-easing-standard);\n}\n\n@media (hover: hover) {\n .LinkCard:hover {\n background-color: var(--link-card-hover);\n }\n\n .LinkCard:hover .Arrow {\n translate: var(--lea-spacing-1) 0;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .LinkCard,\n .Arrow {\n transition-duration: 0.01ms;\n }\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport type { ReactElement, ReactNode, Ref } from \"react\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport type { SurfaceRoles } from \"../../color-roles\";\nimport { ArrowCircle } from \"./arrow-circle\";\nimport styles from \"./link-card.module.css\";\n\nexport type LinkCardSurface = SurfaceRoles<\n \"base\" | \"subtle\" | \"muted\" | \"contrast\" | \"contrast-accent\"\n>;\n\n/** A link card always leads somewhere: an `href`, or a router link. */\ntype LinkCardTarget =\n | {\n /** Where the card leads. */\n href: string;\n render?: never;\n }\n | {\n href?: never;\n /**\n * A router link to render instead of the `<a>`, carrying its own\n * destination: `render={<NextLink href=\"/lan\" />}`.\n */\n render: ReactElement;\n };\n\nexport type LinkCardProps = Omit<\n useRender.ComponentProps<\"a\">,\n \"ref\" | \"title\" | \"children\" | \"href\" | \"render\"\n> &\n LinkCardTarget & {\n /** The rendered element: the `<a>`, or the link passed to `render`. */\n ref?: Ref<HTMLElement>;\n /** The text of the card, which is also the name of the link. */\n title: ReactNode;\n /**\n * The element for the title, a `<p>` by default. Pass a heading where the\n * page outline needs one: `titleRender={<h3 />}`.\n */\n titleRender?: ReactElement;\n /**\n * The colour of the card, after its `bg` token: `\"base\"` (white),\n * `\"subtle\"` (grey), `\"muted\"` (beige), `\"contrast\"` (near-black) or\n * `\"contrast-accent\"` (deep purple). On the last two the text and the\n * arrow are white.\n * @default \"base\"\n */\n surface?: LinkCardSurface;\n };\n\n/**\n * The website's navigation card: a title and a round arrow, and the whole card\n * is one link, as in the grids of links to Lån, Sparing and Forsikring.\n *\n * ```tsx\n * <LinkCard href=\"/lan\" title=\"Lån\" surface=\"muted\" />\n * <LinkCard render={<NextLink href=\"/sparing\" />} title=\"Sparing\" surface=\"contrast\" />\n * ```\n *\n * On hover the card takes the website's purple tint (a dark one on the dark\n * surfaces) and the arrow slides to the right. It fills the cell its grid\n * gives it, at least 240px tall, with the title at the top and the arrow at\n * the bottom. The link is named by the title; the arrow is decoration.\n */\nexport function LinkCard({\n title,\n titleRender,\n surface = \"base\",\n render,\n ref,\n className,\n ...rest\n}: LinkCardProps) {\n const titleElement = useRender({\n render: titleRender,\n defaultTagName: \"p\",\n props: { className: styles.Title, children: title },\n });\n\n return useRender({\n render,\n ref,\n defaultTagName: \"a\",\n props: {\n ...rest,\n \"data-surface\": surface,\n className: mergeClassName(styles.LinkCard, className)({}),\n children: (\n <>\n {titleElement}\n <ArrowCircle className={styles.Arrow} />\n </>\n ),\n },\n });\n}\n","/*\n * The website's ui/Card. Horizontal: a coloured card with `huge` corners, two\n * equal columns from 1024px (text 64px inside, centred on a 480px minimum;\n * the photo inside 64px with `5xl` corners, or edge to edge as a background),\n * one column below with 32px inside. Vertical: no colour of its own, the\n * photo at 1 : 0.6 and centred text 32px from it. Text, the price example and\n * the photo's frame follow the surface, so they are white on the dark cards.\n */\n.MediaCard {\n --media-card-bg: var(--lea-palette-bg-base);\n --media-card-text: var(--lea-palette-text-base);\n\n display: grid;\n box-sizing: border-box;\n grid-template-columns: minmax(0, 1fr);\n overflow: hidden;\n border-radius: var(--lea-radius-huge);\n background-color: var(--media-card-bg);\n color: var(--media-card-text);\n text-decoration: none;\n -webkit-tap-highlight-color: transparent;\n}\n\n.MediaCard[data-surface=\"subtle\"] {\n --media-card-bg: var(--lea-palette-bg-subtle);\n}\n\n.MediaCard[data-surface=\"muted\"] {\n --media-card-bg: var(--lea-palette-bg-muted);\n}\n\n.MediaCard[data-surface=\"contrast\"] {\n --media-card-bg: var(--lea-palette-bg-contrast);\n --media-card-text: var(--lea-palette-text-contrast);\n}\n\n.MediaCard[data-surface=\"contrast-accent\"] {\n --media-card-bg: var(--lea-palette-bg-contrast-accent);\n --media-card-text: var(--lea-palette-text-contrast);\n}\n\n/* The whole card as a link: a white ring inside a near-black one, so the\n focus shows on a dark section as on a light one. */\n.MediaCard[data-link]:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n box-shadow: 0 0 0 0.125rem var(--lea-palette-bg-base);\n}\n\n.Content {\n display: flex;\n min-width: 0;\n box-sizing: border-box;\n flex-direction: column;\n justify-content: center;\n gap: var(--lea-spacing-4);\n padding: var(--lea-spacing-8);\n}\n\n.Text {\n display: flex;\n flex-direction: column;\n gap: var(--lea-spacing-2);\n}\n\n.Title {\n margin: 0;\n font-size: var(--lea-text-heading-md-font-size);\n font-weight: var(--lea-text-heading-md-font-weight);\n line-height: var(--lea-text-heading-md-line-height);\n letter-spacing: var(--lea-text-heading-md-letter-spacing);\n overflow-wrap: break-word;\n}\n\n.Description {\n margin: 0;\n font-size: var(--lea-text-body-lg-font-size);\n font-weight: var(--lea-text-body-lg-font-weight);\n line-height: var(--lea-text-body-lg-line-height);\n letter-spacing: var(--lea-text-body-lg-letter-spacing);\n}\n\n.Action {\n display: flex;\n flex-wrap: wrap;\n}\n\n.Price {\n margin: 0;\n font-size: var(--lea-text-body-sm-font-size);\n font-weight: var(--lea-text-body-sm-font-weight);\n line-height: var(--lea-text-body-sm-line-height);\n letter-spacing: var(--lea-text-body-sm-letter-spacing);\n}\n\n/* A flex row, so the photo stretches to the height of its column from 1024px\n (528 × 352 in a 480px card, as on leabank.no) and keeps 1 : 0.6 below. */\n.Media {\n display: flex;\n min-width: 0;\n box-sizing: border-box;\n padding: var(--lea-spacing-4);\n}\n\n.MediaCard[data-image-position=\"start\"] .Media {\n order: -1;\n}\n\n.Image {\n display: block;\n width: 100%;\n aspect-ratio: 1 / 0.6;\n border-radius: var(--lea-radius-5xl);\n object-fit: cover;\n}\n\n.MediaCard[data-image-as-background] .Media {\n padding: 0;\n}\n\n.MediaCard[data-image-as-background] .Image {\n border-radius: 0;\n}\n\n@media (min-width: 1024px) {\n .MediaCard[data-orientation=\"horizontal\"][data-has-image] {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n }\n\n .MediaCard[data-orientation=\"horizontal\"] .Content {\n /* The website's min-height: 480px. */\n min-height: calc(var(--lea-spacing) * 60);\n }\n\n .MediaCard[data-orientation=\"horizontal\"] .Title {\n font-size: var(--lea-text-heading-xl-font-size);\n font-weight: var(--lea-text-heading-xl-font-weight);\n line-height: var(--lea-text-heading-xl-line-height);\n letter-spacing: var(--lea-text-heading-xl-letter-spacing);\n }\n\n .MediaCard[data-orientation=\"horizontal\"]:not([data-image-as-background])\n .Media {\n padding: var(--lea-spacing-8);\n }\n}\n\n@media (max-width: 767.95px) {\n .MediaCard[data-orientation=\"horizontal\"] .Content {\n padding: var(--lea-spacing-4);\n }\n\n .MediaCard[data-orientation=\"horizontal\"] .Description {\n font-size: var(--lea-text-body-md-font-size);\n font-weight: var(--lea-text-body-md-font-weight);\n line-height: var(--lea-text-body-md-line-height);\n letter-spacing: var(--lea-text-body-md-letter-spacing);\n }\n}\n\n/*\n * Vertical: a photo and centred text, transparent on its section and in the\n * section's text colour. As wide as its column, at most the website's 650px.\n */\n.MediaCard[data-orientation=\"vertical\"] {\n display: flex;\n width: 100%;\n max-width: calc(var(--lea-spacing) * 81.25);\n flex-direction: column;\n gap: var(--lea-spacing-4);\n margin-inline: auto;\n overflow: visible;\n /* Not drawn, as there is no fill: it rounds the focus ring like the photo. */\n border-radius: var(--lea-radius-5xl);\n background-color: transparent;\n color: inherit;\n}\n\n.MediaCard[data-orientation=\"vertical\"] .Content {\n align-items: center;\n gap: var(--lea-spacing-2);\n padding: 0;\n text-align: center;\n}\n\n.MediaCard[data-orientation=\"vertical\"] .Action {\n justify-content: center;\n}\n\n.MediaCard[data-orientation=\"vertical\"] .Media {\n padding: 0;\n}\n\n.MediaCard[data-orientation=\"vertical\"] .Description {\n font-size: var(--lea-text-body-md-font-size);\n font-weight: var(--lea-text-body-md-font-weight);\n line-height: var(--lea-text-body-md-line-height);\n letter-spacing: var(--lea-text-body-md-letter-spacing);\n}\n\n/* A vertical link card has no fill of its own: the ring sits 8px out, in the\n section's text colour, which shows on the section by definition. */\n.MediaCard[data-orientation=\"vertical\"][data-link]:focus-visible {\n outline-color: currentColor;\n outline-offset: var(--lea-spacing-1);\n box-shadow: none;\n}\n","import { isValidElement, type ReactElement } from \"react\";\n\n/**\n * A `render` element carrying `href` (an `<a>`, Next.js `Link`) or `to`\n * (React Router `Link`) makes a website card one link, as `ButtonRoot` reads\n * a link. Internal: not exported from the package.\n */\nexport const isLinkElement = (\n render: unknown,\n): render is ReactElement<{ href?: unknown; to?: unknown }> =>\n isValidElement<{ href?: unknown; to?: unknown }>(render) &&\n (render.props.href != null || render.props.to != null);\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { useId, type ReactElement, type ReactNode, type Ref } from \"react\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport type { SurfaceRoles } from \"../../color-roles\";\nimport styles from \"./media-card.module.css\";\nimport { isLinkElement } from \"../website-card\";\n\nexport type MediaCardSurface = SurfaceRoles<\n \"base\" | \"subtle\" | \"muted\" | \"contrast\" | \"contrast-accent\"\n>;\nexport type MediaCardOrientation = \"horizontal\" | \"vertical\";\nexport type MediaCardImagePosition = \"start\" | \"end\";\n\n/** `alt` is required with a photo, `\"\"` when it is decoration. */\ntype MediaCardImage =\n | {\n /** The photo. Without one, the card is text only. */\n src: string;\n /**\n * What the photo shows, for screen readers; `\"\"` when it is decoration\n * and the text says it all.\n */\n alt: string;\n /**\n * The image element to render instead of the plain `<img>`, such as\n * `next/image` with `fill`, or an `<img loading=\"lazy\">`. Its own props\n * win over `src` and `alt`.\n */\n imageRender?: ReactElement;\n }\n | { src?: undefined; alt?: undefined; imageRender?: undefined };\n\nexport type MediaCardProps = Omit<\n useRender.ComponentProps<\"div\">,\n \"ref\" | \"title\" | \"children\"\n> &\n MediaCardImage & {\n /**\n * The rendered element: the `<div>`, or the element passed to `render`.\n * A `render` element with `href` or `to` makes the whole card one link,\n * named by its title: `render={<NextLink href=\"/lan\" />}`.\n */\n ref?: Ref<HTMLElement>;\n /** The title of the card. */\n title: ReactNode;\n /**\n * The element for the title, an `<h2>` by default:\n * `titleRender={<h3 />}`.\n */\n titleRender?: ReactElement;\n /** The text under the title. */\n text?: ReactNode;\n /**\n * `\"horizontal\"`: text and photo side by side in a coloured card, one\n * above the other below 1024px. `\"vertical\"`: no colour of its own, the\n * photo above or below centred text, for a row of cards.\n * @default \"horizontal\"\n */\n orientation?: MediaCardOrientation;\n /**\n * Where the photo sits: `\"start\"` before the text (on the left, or on\n * top), `\"end\"` after it. Left out, it is after the text on a horizontal\n * card and above it on a vertical one, as on the website.\n */\n imagePosition?: MediaCardImagePosition;\n /**\n * Horizontal cards only: the photo fills its half of the card edge to\n * edge, instead of sitting inside it with round corners.\n * @default false\n */\n imageAsBackground?: boolean;\n /**\n * Horizontal cards only: the colour of the card, after its `bg` token.\n * On `\"contrast\"` and `\"contrast-accent\"` the text is white; give a\n * `Link` in `action` `color=\"contrast\"` there.\n * @default \"base\"\n */\n surface?: MediaCardSurface;\n /**\n * The call to action under the text: an `ActionButton`, or a `Link`\n * with `arrow`. Left out when the card itself is a link, which cannot\n * hold another.\n */\n action?: ReactNode;\n /** The price example under the action, in the small print. */\n priceExample?: ReactNode;\n };\n\n/**\n * The website's picture-and-text card. Horizontal, it is a coloured card with\n * the text in one half and the photo in the other, as the product cards on\n * leabank.no's home page; vertical, it is a photo with centred text under it,\n * for a row of three.\n *\n * ```tsx\n * <MediaCard\n * surface=\"subtle\"\n * title=\"Forbrukslån\"\n * text=\"Lån fra 10 000 til 500 000 kroner uten sikkerhet.\"\n * src=\"/bilder/familie.jpg\"\n * alt=\"\"\n * action={<ActionButton render={<NextLink href=\"/lan\" />}>Søk om forbrukslån</ActionButton>}\n * priceExample=\"Eff. rente 12,5 %, 150 000 kr o/5 år …\"\n * />\n * ```\n *\n * The horizontal card is two equal columns from 1024px (the photo fills its\n * column), and one column below, where the photo is 1 : 0.6. A `render`\n * element with `href` or `to` makes the whole card one link, named by its\n * title and described by its text; it then has no `action`.\n */\nexport function MediaCard({\n title,\n titleRender,\n text,\n src,\n alt,\n imageRender,\n orientation = \"horizontal\",\n imagePosition,\n imageAsBackground = false,\n surface = \"base\",\n action,\n priceExample,\n render,\n ref,\n className,\n ...rest\n}: MediaCardProps) {\n const id = useId();\n const isLink = isLinkElement(render);\n const titleId = `${id}-title`;\n const textId = `${id}-text`;\n const horizontal = orientation === \"horizontal\";\n\n const titleElement = useRender({\n render: titleRender,\n defaultTagName: \"h2\",\n props: {\n className: styles.Title,\n id: isLink ? titleId : undefined,\n children: title,\n },\n });\n\n const image = useRender({\n enabled: src != null,\n render: imageRender,\n defaultTagName: \"img\",\n props: { src, alt, className: styles.Image },\n });\n\n return useRender({\n render,\n ref,\n defaultTagName: \"div\",\n props: {\n ...(isLink && {\n \"aria-labelledby\": titleId,\n \"aria-describedby\": text != null ? textId : undefined,\n }),\n ...rest,\n \"data-orientation\": orientation,\n \"data-image-position\": imagePosition ?? (horizontal ? \"end\" : \"start\"),\n \"data-image-as-background\":\n (horizontal && imageAsBackground) || undefined,\n \"data-surface\": horizontal ? surface : undefined,\n \"data-has-image\": image != null || undefined,\n \"data-link\": isLink || undefined,\n className: mergeClassName(styles.MediaCard, className)({}),\n children: (\n <>\n <div className={styles.Content}>\n <div className={styles.Text}>\n {titleElement}\n {text != null && (\n <p\n className={styles.Description}\n id={isLink ? textId : undefined}\n >\n {text}\n </p>\n )}\n </div>\n {!isLink && action != null && (\n <div className={styles.Action}>{action}</div>\n )}\n {priceExample != null && (\n <p className={styles.Price}>{priceExample}</p>\n )}\n </div>\n {image != null && <div className={styles.Media}>{image}</div>}\n </>\n ),\n },\n });\n}\n","/*\n * The website's ServiceCardsBlock (Display = Grid): `huge` corners, 32px\n * inside, a photo with `5xl` corners. Stacked, the photo takes the height the\n * text leaves (32px above the text); in a row, it is a 256px square 16px\n * before the text, from 1024px. Text is white on the dark surfaces.\n */\n.ServiceCard {\n --service-card-bg: var(--lea-palette-bg-base);\n --service-card-text: var(--lea-palette-text-base);\n\n display: flex;\n box-sizing: border-box;\n flex-direction: column;\n gap: var(--lea-spacing-4);\n padding: var(--lea-spacing-4);\n overflow: hidden;\n border-radius: var(--lea-radius-huge);\n background-color: var(--service-card-bg);\n color: var(--service-card-text);\n text-decoration: none;\n -webkit-tap-highlight-color: transparent;\n}\n\n.ServiceCard[data-surface=\"subtle\"] {\n --service-card-bg: var(--lea-palette-bg-subtle);\n}\n\n.ServiceCard[data-surface=\"muted\"] {\n --service-card-bg: var(--lea-palette-bg-muted);\n}\n\n.ServiceCard[data-surface=\"contrast\"] {\n --service-card-bg: var(--lea-palette-bg-contrast);\n --service-card-text: var(--lea-palette-text-contrast);\n}\n\n.ServiceCard[data-surface=\"contrast-accent\"] {\n --service-card-bg: var(--lea-palette-bg-contrast-accent);\n --service-card-text: var(--lea-palette-text-contrast);\n}\n\n/* The whole card as a link: a white ring inside a near-black one, so the\n focus shows on a dark section as on a light one. */\n.ServiceCard[data-link]:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n box-shadow: 0 0 0 0.125rem var(--lea-palette-bg-base);\n}\n\n/* Takes the room the text leaves, and shrinks to it in a fixed cell. */\n.Image {\n display: block;\n width: 100%;\n min-height: 0;\n flex: 1 1 auto;\n border-radius: var(--lea-radius-5xl);\n object-fit: cover;\n}\n\n.Content {\n display: flex;\n min-width: 0;\n flex-shrink: 0;\n flex-direction: column;\n gap: var(--lea-spacing-2);\n}\n\n.Icon {\n display: flex;\n width: var(--lea-spacing-5);\n height: var(--lea-spacing-5);\n}\n\n.Icon > * {\n display: block;\n width: 100%;\n height: 100%;\n object-fit: contain;\n}\n\n.Title {\n margin: 0;\n font-size: var(--lea-text-heading-xl-font-size);\n font-weight: var(--lea-text-heading-xl-font-weight);\n line-height: var(--lea-text-heading-xl-line-height);\n letter-spacing: var(--lea-text-heading-xl-letter-spacing);\n overflow-wrap: break-word;\n}\n\n.Text {\n margin: 0;\n font-size: var(--lea-text-body-lg-font-size);\n font-weight: var(--lea-text-body-lg-font-weight);\n line-height: var(--lea-text-body-lg-line-height);\n letter-spacing: var(--lea-text-body-lg-letter-spacing);\n}\n\n@media (min-width: 1024px) {\n .ServiceCard[data-layout=\"row\"] {\n flex-direction: row;\n align-items: center;\n gap: var(--lea-spacing-2);\n }\n\n .ServiceCard[data-layout=\"row\"] .Image {\n /* 256px: the height of the website's 320px row inside its padding. */\n width: calc(var(--lea-spacing) * 32);\n flex: 0 0 auto;\n aspect-ratio: 1;\n }\n\n .ServiceCard[data-layout=\"row\"] .Content {\n flex: 1 1 0;\n }\n}\n\n@media (max-width: 639.95px) {\n .Title {\n font-size: var(--lea-text-heading-md-font-size);\n font-weight: var(--lea-text-heading-md-font-weight);\n line-height: var(--lea-text-heading-md-line-height);\n letter-spacing: var(--lea-text-heading-md-letter-spacing);\n }\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { useId, type ReactElement, type ReactNode, type Ref } from \"react\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport type { SurfaceRoles } from \"../../color-roles\";\nimport styles from \"./service-card.module.css\";\nimport { isLinkElement } from \"../website-card\";\n\nexport type ServiceCardSurface = SurfaceRoles<\n \"base\" | \"subtle\" | \"muted\" | \"contrast\" | \"contrast-accent\"\n>;\nexport type ServiceCardLayout = \"stacked\" | \"row\";\n\n/** `alt` is required with a photo, `\"\"` when it is decoration. */\ntype ServiceCardImage =\n | {\n /** The photo. Without one, the card is text only. */\n src: string;\n /**\n * What the photo shows, for screen readers; `\"\"` when it is decoration\n * and the text says it all.\n */\n alt: string;\n /**\n * The image element to render instead of the plain `<img>`, such as\n * `next/image` with `fill`. Its own props win over `src` and `alt`.\n */\n imageRender?: ReactElement;\n }\n | { src?: undefined; alt?: undefined; imageRender?: undefined };\n\nexport type ServiceCardProps = Omit<\n useRender.ComponentProps<\"div\">,\n \"ref\" | \"title\" | \"children\"\n> &\n ServiceCardImage & {\n /**\n * The rendered element: the `<div>`, or the element passed to `render`.\n * A `render` element with `href` or `to` makes the whole card one link,\n * named by its title, as on the website: `render={<NextLink href=\"/lan\" />}`.\n */\n ref?: Ref<HTMLElement>;\n /** The title of the card. */\n title: ReactNode;\n /**\n * The element for the title, an `<h2>` by default:\n * `titleRender={<h3 />}`.\n */\n titleRender?: ReactElement;\n /** The text under the title. */\n text?: ReactNode;\n /**\n * A 40px icon above the title, such as a Lucide icon or an `<img>`.\n * Mark it `aria-hidden`, or give an `<img>` `alt=\"\"`.\n */\n icon?: ReactNode;\n /**\n * `\"stacked\"`: the photo fills the card above the text, for the large\n * and small cells of the website's grid. `\"row\"`: a square photo before\n * the text, for the wide cells (320px tall); stacked below 1024px.\n * @default \"stacked\"\n */\n layout?: ServiceCardLayout;\n /**\n * The colour of the card, after its `bg` token. On `\"contrast\"` and\n * `\"contrast-accent\"` the text is white.\n * @default \"base\"\n */\n surface?: ServiceCardSurface;\n };\n\n/**\n * A card of the website's service grid: a photo, an optional icon, a title and\n * a line of text, in one of the section colours. It fills the grid cell it is\n * given, and the photo takes the room the text leaves.\n *\n * ```tsx\n * <ServiceCard\n * render={<NextLink href=\"/lan/forbrukslan\" />}\n * title=\"Forbrukslån\"\n * text=\"Lån fra 10 000 til 500 000 kroner uten sikkerhet.\"\n * src=\"/bilder/familie.jpg\"\n * alt=\"\"\n * surface=\"muted\"\n * />\n * ```\n *\n * On the website the whole card is a link: pass it in `render`, and the link\n * is named by the title and described by the text.\n */\nexport function ServiceCard({\n title,\n titleRender,\n text,\n icon,\n src,\n alt,\n imageRender,\n layout = \"stacked\",\n surface = \"base\",\n render,\n ref,\n className,\n ...rest\n}: ServiceCardProps) {\n const id = useId();\n const isLink = isLinkElement(render);\n const titleId = `${id}-title`;\n const textId = `${id}-text`;\n\n const titleElement = useRender({\n render: titleRender,\n defaultTagName: \"h2\",\n props: {\n className: styles.Title,\n id: isLink ? titleId : undefined,\n children: title,\n },\n });\n\n const image = useRender({\n enabled: src != null,\n render: imageRender,\n defaultTagName: \"img\",\n props: { src, alt, className: styles.Image },\n });\n\n return useRender({\n render,\n ref,\n defaultTagName: \"div\",\n props: {\n ...(isLink && {\n \"aria-labelledby\": titleId,\n \"aria-describedby\": text != null ? textId : undefined,\n }),\n ...rest,\n \"data-layout\": layout,\n \"data-surface\": surface,\n \"data-link\": isLink || undefined,\n className: mergeClassName(styles.ServiceCard, className)({}),\n children: (\n <>\n {image}\n <div className={styles.Content}>\n {icon != null && <span className={styles.Icon}>{icon}</span>}\n {titleElement}\n {text != null && (\n <p className={styles.Text} id={isLink ? textId : undefined}>\n {text}\n </p>\n )}\n </div>\n </>\n ),\n },\n });\n}\n","/*\n * The website's testimonial card: white, `5xl` corners, 64px inside (32px\n * below 768px). Stars 16px above the quote, the person 32px under it: a 48px\n * avatar, the name in semibold and a line in `text.subtle`, which reads on the\n * card's white (4.6 : 1).\n */\n.TestimonialCard {\n display: flex;\n box-sizing: border-box;\n flex-direction: column;\n gap: var(--lea-spacing-4);\n margin: 0;\n padding: var(--lea-spacing-8);\n border-radius: var(--lea-radius-5xl);\n background-color: var(--lea-palette-bg-base);\n color: var(--lea-palette-text-base);\n}\n\n@media (max-width: 767.95px) {\n .TestimonialCard {\n padding: var(--lea-spacing-4);\n }\n}\n\n.Top {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--lea-spacing-2);\n}\n\n.Quote {\n display: flex;\n margin: 0;\n flex-direction: column;\n gap: var(--lea-spacing-2);\n font-size: var(--lea-text-body-lg-font-size);\n font-weight: var(--lea-text-body-lg-font-weight);\n line-height: var(--lea-text-body-lg-line-height);\n letter-spacing: var(--lea-text-body-lg-letter-spacing);\n}\n\n.Quote > * {\n margin: 0;\n}\n\n/* The website adds the quotation marks around the quote; the copy has none. */\n.Quote > :first-child::before {\n content: \"\\201C\";\n}\n\n.Quote > :last-child::after {\n content: \"\\201D\";\n}\n\n.Author {\n display: flex;\n align-items: center;\n gap: var(--lea-spacing-2);\n}\n\n.Avatar {\n flex-shrink: 0;\n}\n\n.Person {\n display: flex;\n min-width: 0;\n flex-direction: column;\n}\n\n.Name {\n font-size: var(--lea-text-heading-xs-font-size);\n font-weight: var(--lea-text-heading-xs-font-weight);\n line-height: var(--lea-text-heading-xs-line-height);\n letter-spacing: var(--lea-text-heading-xs-letter-spacing);\n}\n\n.Meta {\n color: var(--lea-palette-text-subtle);\n font-size: var(--lea-text-body-sm-font-size);\n font-weight: var(--lea-text-body-sm-font-weight);\n line-height: var(--lea-text-body-sm-line-height);\n letter-spacing: var(--lea-text-body-sm-letter-spacing);\n}\n","\"use client\";\n\nimport type { ComponentProps, ReactElement, ReactNode } from \"react\";\n\nimport { Avatar } from \"../avatar/avatar\";\nimport { StarRating } from \"../star-rating/star-rating\";\nimport styles from \"./testimonial-card.module.css\";\n\nexport type TestimonialCardProps = Omit<\n ComponentProps<\"figure\">,\n \"children\"\n> & {\n /**\n * What the customer said, without quotation marks: the card adds them. A\n * string becomes one paragraph; pass `<p>` elements for several.\n */\n quote: ReactNode;\n /** Who said it. */\n name: ReactNode;\n /** A line under the name, such as the position and the company. */\n meta?: ReactNode;\n /** Stars above the quote, from 0 to 5. Left out, no stars are shown. */\n rating?: number;\n /**\n * A photo of the person. Without one, the avatar is a grey disc. It is\n * decoration: the name is beside it.\n */\n avatarSrc?: string;\n /**\n * The image element for the photo instead of an `<img>`, such as\n * `next/image`, as `Avatar`'s `imageRender`. Pass `avatarSrc` as well.\n */\n avatarImageRender?: ReactElement;\n};\n\n/**\n * A customer review, as in the website's carousel of reviews: stars, the\n * quote and who said it, on a white card with 32px corners.\n *\n * ```tsx\n * <TestimonialCard\n * rating={5}\n * quote=\"Rask og enkel prosess. Jeg fikk svar samme dag.\"\n * name=\"Kari Nordmann\"\n * meta=\"Kunde, Oslo\"\n * avatarSrc=\"/kunder/kari.jpg\"\n * />\n * ```\n *\n * It is a `figure`: the quote is a `blockquote`, and the person a\n * `figcaption`. 64px inside, 32px below 768px. It takes the width it is\n * given and grows with the quote.\n */\nexport function TestimonialCard({\n quote,\n name,\n meta,\n rating,\n avatarSrc,\n avatarImageRender,\n className,\n ...rest\n}: TestimonialCardProps) {\n return (\n <figure\n {...rest}\n className={[styles.TestimonialCard, className].filter(Boolean).join(\" \")}\n >\n <div className={styles.Top}>\n {rating != null && <StarRating rating={rating} />}\n <blockquote className={styles.Quote}>\n {typeof quote === \"string\" || typeof quote === \"number\" ? (\n <p>{quote}</p>\n ) : (\n quote\n )}\n </blockquote>\n </div>\n <figcaption className={styles.Author}>\n <Avatar\n src={avatarSrc}\n alt=\"\"\n imageRender={avatarImageRender}\n className={styles.Avatar}\n />\n <span className={styles.Person}>\n <span className={styles.Name}>{name}</span>\n {meta != null && <span className={styles.Meta}>{meta}</span>}\n </span>\n </figcaption>\n </figure>\n );\n}\n","/*\n * The website's blog list item and career item: a white card that is one\n * link, 32px inside. A post has `huge` corners and its 320 × 240 photo\n * (`5xl`) 32px before the text, stacked below 768px with 16px around the\n * text; a job has `5xl` corners and no photo. The excerpt is `text.subtle`\n * on the white (4.6 : 1), cut at two lines; the facts are `Chip`s, `muted`\n * and `lg`, 8px apart.\n */\n.ArticleCard {\n display: flex;\n box-sizing: border-box;\n align-items: center;\n gap: var(--lea-spacing-4);\n padding: var(--lea-spacing-4);\n overflow: hidden;\n border-radius: var(--lea-radius-huge);\n background-color: var(--lea-palette-bg-base);\n color: var(--lea-palette-text-base);\n text-decoration: none;\n -webkit-tap-highlight-color: transparent;\n}\n\n.ArticleCard[data-variant=\"job\"] {\n flex-direction: column;\n align-items: stretch;\n gap: var(--lea-spacing-2);\n border-radius: var(--lea-radius-5xl);\n}\n\n/* A white ring inside a near-black one, so the focus shows on whichever\n section the card sits on. */\n.ArticleCard:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n box-shadow: 0 0 0 0.125rem var(--lea-palette-bg-base);\n}\n\n.Image {\n display: block;\n /* The website's 320 × 240 photo. */\n width: calc(var(--lea-spacing) * 40);\n height: calc(var(--lea-spacing) * 30);\n flex-shrink: 0;\n border-radius: var(--lea-radius-5xl);\n object-fit: cover;\n}\n\n.Content {\n display: flex;\n min-width: 0;\n flex: 1 1 0;\n flex-direction: column;\n gap: var(--lea-spacing-2);\n}\n\n.Title {\n margin: 0;\n font-size: var(--lea-text-heading-md-font-size);\n font-weight: var(--lea-text-heading-md-font-weight);\n line-height: var(--lea-text-heading-md-line-height);\n letter-spacing: var(--lea-text-heading-md-letter-spacing);\n overflow-wrap: break-word;\n}\n\n.Excerpt {\n display: -webkit-box;\n margin: 0;\n overflow: hidden;\n color: var(--lea-palette-text-subtle);\n font-size: var(--lea-text-body-md-font-size);\n font-weight: var(--lea-text-body-md-font-weight);\n line-height: var(--lea-text-body-md-line-height);\n letter-spacing: var(--lea-text-body-md-letter-spacing);\n -webkit-box-orient: vertical;\n -webkit-line-clamp: 2;\n line-clamp: 2;\n}\n\n.Meta {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--lea-spacing-1);\n}\n\n@media (hover: hover) {\n .ArticleCard:hover .Title {\n text-decoration: underline;\n }\n}\n\n@media (max-width: 767.95px) {\n .ArticleCard[data-variant=\"post\"] {\n flex-direction: column;\n align-items: stretch;\n }\n\n .ArticleCard[data-variant=\"post\"] .Image {\n width: 100%;\n height: auto;\n aspect-ratio: 1 / 0.6;\n }\n\n .ArticleCard[data-variant=\"post\"] .Content {\n padding: var(--lea-spacing-2);\n }\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { useId, type ReactElement, type ReactNode, type Ref } from \"react\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport styles from \"./article-card.module.css\";\nimport { Chip } from \"../chip/chip\";\n\nexport type ArticleCardVariant = \"post\" | \"job\";\n\n/** An article card always leads somewhere: an `href`, or a router link. */\ntype ArticleCardTarget =\n | {\n /** Where the card leads: the article or the job opening. */\n href: string;\n render?: never;\n }\n | {\n href?: never;\n /**\n * A router link to render instead of the `<a>`, carrying its own\n * destination: `render={<NextLink href=\"/okonomiguiden/smalan\" />}`.\n */\n render: ReactElement;\n };\n\n/** `alt` is required with a photo, `\"\"` when it is decoration. */\ntype ArticleCardImage =\n | {\n /** The article's photo. Job openings have none. */\n src: string;\n /**\n * What the photo shows, for screen readers; `\"\"` when it is decoration\n * and the title says it all.\n */\n alt: string;\n /**\n * The image element to render instead of the plain `<img>`, such as\n * `next/image` with `fill`, or an `<img loading=\"lazy\">`. Its own props\n * win over `src` and `alt`.\n */\n imageRender?: ReactElement;\n }\n | { src?: undefined; alt?: undefined; imageRender?: undefined };\n\nexport type ArticleCardProps = Omit<\n useRender.ComponentProps<\"a\">,\n \"ref\" | \"title\" | \"children\" | \"href\" | \"render\"\n> &\n ArticleCardTarget &\n ArticleCardImage & {\n /** The rendered element: the `<a>`, or the link passed to `render`. */\n ref?: Ref<HTMLElement>;\n /** The title of the article, which also names the link. */\n title: ReactNode;\n /**\n * The element for the title, an `<h3>` by default, under the list's own\n * heading: `titleRender={<h2 />}`.\n */\n titleRender?: ReactElement;\n /** A short summary under the title, cut off after two lines. */\n excerpt?: ReactNode;\n /**\n * The facts under the excerpt, each shown as a beige pill: the category\n * and the date of a post, or the place, the employment type and the\n * closing date of a job. Pass them formatted.\n */\n meta?: ReactNode[];\n /**\n * `\"post\"`: an article, with its photo beside the text (above it below\n * 768px) and 64px corners. `\"job\"`: a job opening, text only, with 32px\n * corners.\n * @default \"post\"\n */\n variant?: ArticleCardVariant;\n };\n\n/**\n * An item in the website's list of articles or of job openings: a white card\n * that is one link, with the title, a two-line excerpt and the facts as\n * pills. On hover the title is underlined.\n *\n * ```tsx\n * <ul>\n * <li>\n * <ArticleCard\n * render={<NextLink href=\"/okonomiguiden/smalan\" />}\n * title=\"Slik refinansierer du dyre smålån\"\n * excerpt=\"Har du flere smålån og kredittkort? …\"\n * meta={[\"Lån\", \"12. mars 2026\"]}\n * src=\"/bilder/smalan.jpg\"\n * alt=\"\"\n * />\n * </li>\n * </ul>\n * ```\n *\n * The link is named by the title and described by the excerpt and the\n * facts. Put the cards in a list of the page's own; they take the width they\n * are given.\n */\nexport function ArticleCard({\n title,\n titleRender,\n excerpt,\n meta,\n src,\n alt,\n imageRender,\n variant = \"post\",\n render,\n ref,\n className,\n ...rest\n}: ArticleCardProps) {\n const id = useId();\n const titleId = `${id}-title`;\n const excerptId = `${id}-excerpt`;\n const metaId = `${id}-meta`;\n const hasMeta = meta != null && meta.length > 0;\n const describedBy =\n [excerpt != null && excerptId, hasMeta && metaId]\n .filter(Boolean)\n .join(\" \") || undefined;\n\n const titleElement = useRender({\n render: titleRender,\n defaultTagName: \"h3\",\n props: { className: styles.Title, id: titleId, children: title },\n });\n\n const image = useRender({\n enabled: src != null,\n render: imageRender,\n defaultTagName: \"img\",\n props: { src, alt, className: styles.Image },\n });\n\n return useRender({\n render,\n ref,\n defaultTagName: \"a\",\n props: {\n \"aria-labelledby\": titleId,\n \"aria-describedby\": describedBy,\n ...rest,\n \"data-variant\": variant,\n className: mergeClassName(styles.ArticleCard, className)({}),\n children: (\n <>\n {image}\n <div className={styles.Content}>\n {titleElement}\n {excerpt != null && (\n <p className={styles.Excerpt} id={excerptId}>\n {excerpt}\n </p>\n )}\n {hasMeta && (\n <span className={styles.Meta} id={metaId}>\n {meta.map((item, index) => (\n <Chip key={index} label={item} color=\"muted\" size=\"lg\" />\n ))}\n </span>\n )}\n </div>\n </>\n ),\n },\n });\n}\n","/*\n * The website's RowWithDivider: the text and a white logo tile, 64px apart\n * and 64px above and below from 1024px, stacked 16px apart below; a 1px\n * `border.base` line underneath. The tile floats over the section with the\n * website's `shadow-soft`, `huge` corners, 220px tall and at most 360px wide.\n */\n.PartnerRow {\n display: grid;\n box-sizing: border-box;\n grid-template-columns: minmax(0, 1fr);\n align-items: center;\n gap: var(--lea-spacing-2);\n padding-block: var(--lea-spacing-2);\n border-bottom: 1px solid var(--lea-palette-border-base);\n}\n\n@media (min-width: 1024px) {\n .PartnerRow {\n gap: var(--lea-spacing-8);\n padding-block: var(--lea-spacing-8);\n }\n\n .PartnerRow[data-has-logo] {\n /* The website's minmax(240px, 360px) logo column. */\n grid-template-columns:\n minmax(0, 1fr)\n minmax(calc(var(--lea-spacing) * 30), calc(var(--lea-spacing) * 45));\n }\n}\n\n.Content {\n min-width: 0;\n}\n\n.Tile {\n display: flex;\n width: 100%;\n max-width: calc(var(--lea-spacing) * 45);\n /* The website's 220px tile. */\n height: calc(var(--lea-spacing) * 27.5);\n box-sizing: border-box;\n align-items: center;\n justify-content: center;\n overflow: hidden;\n border-radius: var(--lea-radius-huge);\n background-color: var(--lea-palette-bg-base);\n box-shadow: var(--lea-shadow-soft);\n}\n\n.Logo {\n display: block;\n max-width: 100%;\n height: 100%;\n object-fit: contain;\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport type { ReactElement, Ref } from \"react\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport styles from \"./partner-row.module.css\";\n\n/** `alt` is required with a logo. */\ntype PartnerRowLogo =\n | {\n /** The partner's logo, shown on a white tile. */\n src: string;\n /**\n * The partner's name, for screen readers; `\"\"` when the text beside\n * the logo already names the partner.\n */\n alt: string;\n /**\n * The image element to render instead of the plain `<img>`, such as\n * `next/image`, or an `<img loading=\"lazy\">`. Its own props win over\n * `src` and `alt`.\n */\n imageRender?: ReactElement;\n }\n | { src?: undefined; alt?: undefined; imageRender?: undefined };\n\nexport type PartnerRowProps = Omit<useRender.ComponentProps<\"div\">, \"ref\"> &\n PartnerRowLogo & {\n /** The rendered element: the `<div>`, or the element passed to `render`. */\n ref?: Ref<HTMLElement>;\n };\n\n/**\n * One partner in the website's list of partners: text about the partner, and\n * the logo on a white tile beside it, over a 1px line.\n *\n * ```tsx\n * <PartnerRow src=\"/logoer/partner.svg\" alt=\"\">\n * <RichText>\n * <h3>Partner AS</h3>\n * <p>Vi samarbeider med …</p>\n * </RichText>\n * </PartnerRow>\n * ```\n *\n * The text is the children: usually `RichText` with the CMS content. Two\n * columns 64px apart from 1024px (the tile is 240 to 360px wide and 220px\n * tall), one column below. It takes the width it is given; the list\n * (`PartnerList`) sets it. Pass `render={<li />}` inside a list.\n */\nexport function PartnerRow({\n src,\n alt,\n imageRender,\n render,\n ref,\n className,\n children,\n ...rest\n}: PartnerRowProps) {\n const logo = useRender({\n enabled: src != null,\n render: imageRender,\n defaultTagName: \"img\",\n props: { src, alt, className: styles.Logo },\n });\n\n return useRender({\n render,\n ref,\n defaultTagName: \"div\",\n props: {\n ...rest,\n \"data-has-logo\": logo != null || undefined,\n className: mergeClassName(styles.PartnerRow, className)({}),\n children: (\n <>\n <div className={styles.Content}>{children}</div>\n {logo != null && <div className={styles.Tile}>{logo}</div>}\n </>\n ),\n },\n });\n}\n","/*\n * The four brand colours. Blue and orange have no semantic token of their\n * own, so they are steps of their scales: a brand stripe is not a status, and\n * must not follow `status.info` or `status.warning` when those change.\n */\n.BrandDivider {\n display: flex;\n width: 100%;\n height: var(--lea-spacing);\n flex-shrink: 0;\n background: linear-gradient(to right,\n var(--lea-color-blue-400) 25%,\n var(--lea-palette-tertiary-base) 25%,\n var(--lea-palette-tertiary-base) 50%,\n var(--lea-color-orange-400) 50%,\n var(--lea-color-orange-400) 75%,\n var(--lea-palette-primary-base) 75%\n );\n}\n\n.BrandDivider[data-size=\"sm\"] {\n height: calc(var(--lea-spacing) * 0.5);\n}\n\n.BrandDivider[data-size=\"md\"] {\n height: calc(var(--lea-spacing) * 1);\n}\n\n.BrandDivider[data-size=\"lg\"] {\n height: calc(var(--lea-spacing) * 1.5);\n}\n\n","import styles from \"./brand-divider.module.css\";\n\n/**\n *\n * Brand Divider in Lea colors\n *\n */\n\nexport type BrandDividerProps = {\n /**\n * The size of the divider.\n */\n size?: \"sm\" | \"md\" | \"lg\";\n};\n\nexport const BrandDivider = ({ size = \"sm\" }: BrandDividerProps) => {\n return (\n <div aria-hidden=\"true\" className={styles.BrandDivider} data-size={size} />\n );\n};\n","/*\n * Shared by Alert and GlobalAlert. Design tokens come from src/theme/tokens.css,\n * so these render styled without a provider.\n *\n * Each colour sets local properties that the variants read, which keeps the\n * variant x colour matrix to one rule per cell.\n */\n\n.Alert {\n --alert-base: var(--lea-palette-status-success-base);\n --alert-subtle: var(--lea-palette-status-success-subtle);\n /* Text and icons on the solid fill of the `filled` variant. */\n --alert-on-base: var(--lea-palette-text-contrast);\n\n /* The height of one line of text, so the icon centres on the first line. */\n --alert-line: calc(\n var(--lea-text-body-md-font-size) * var(--lea-text-body-md-line-height)\n );\n --alert-message-padding: calc(var(--lea-spacing) * 0.7);\n\n box-sizing: border-box;\n padding: var(--lea-spacing) calc(var(--lea-spacing) * 2);\n border-radius: var(--lea-radius-xl);\n color: var(--lea-palette-text-base);\n font-size: var(--lea-text-body-md-font-size);\n font-weight: var(--lea-text-body-md-font-weight);\n line-height: var(--lea-text-body-md-line-height);\n letter-spacing: var(--lea-text-body-md-letter-spacing);\n}\n\n.Layout {\n display: flex;\n align-items: flex-start;\n gap: calc(var(--lea-spacing) * 1.5);\n}\n\n.Icon {\n box-sizing: content-box;\n display: flex;\n height: var(--alert-line);\n flex-shrink: 0;\n align-items: center;\n padding-block: var(--alert-message-padding);\n color: var(--alert-base);\n /* The icon's size: it is drawn at 1em. */\n font-size: var(--lea-font-step-2);\n}\n\n.Message {\n display: flex;\n min-width: 0;\n flex: 1;\n flex-direction: column;\n padding-block: var(--alert-message-padding);\n overflow-wrap: anywhere;\n}\n\n.Title {\n font-weight: var(--lea-font-weight-medium);\n}\n\n.Action {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n margin-inline-start: auto;\n}\n\n/* Colours */\n\n.Alert[data-color=\"info\"] {\n --alert-base: var(--lea-palette-status-info-base);\n --alert-subtle: var(--lea-palette-status-info-subtle);\n}\n\n.Alert[data-color=\"warning\"] {\n --alert-base: var(--lea-palette-status-warning-base);\n --alert-subtle: var(--lea-palette-status-warning-subtle);\n /* White does not read on amber. */\n --alert-on-base: var(--lea-palette-text-base);\n}\n\n.Alert[data-color=\"error\"] {\n --alert-base: var(--lea-palette-status-error-base);\n --alert-subtle: var(--lea-palette-status-error-subtle);\n}\n\n.Alert[data-color=\"primary\"] {\n --alert-base: var(--lea-palette-primary-base);\n --alert-subtle: var(--lea-palette-primary-subtle);\n}\n\n.Alert[data-color=\"neutral\"] {\n --alert-base: var(--lea-color-neutral-700);\n --alert-subtle: var(--lea-color-neutral-100);\n}\n\n/* Variants */\n\n.Alert[data-variant=\"standard\"] {\n background-color: var(--alert-subtle);\n}\n\n.Alert[data-variant=\"filled\"] {\n background-color: var(--alert-base);\n color: var(--alert-on-base);\n}\n\n.Alert[data-variant=\"filled\"] .Icon,\n.Alert[data-variant=\"filled\"] .Close {\n color: var(--alert-on-base);\n}\n\n.Alert[data-variant=\"outlined\"] {\n border: 1px solid var(--alert-base);\n background-color: transparent;\n}\n\n/* Sizes */\n\n.Alert[data-size=\"sm\"] {\n --alert-line: calc(\n var(--lea-text-body-sm-font-size) * var(--lea-text-body-sm-line-height)\n );\n\n padding: calc(var(--lea-spacing) * 0.5) calc(var(--lea-spacing) * 1.5);\n font-size: var(--lea-text-body-sm-font-size);\n font-weight: var(--lea-text-body-sm-font-weight);\n line-height: var(--lea-text-body-sm-line-height);\n letter-spacing: var(--lea-text-body-sm-letter-spacing);\n}\n\n.Alert[data-size=\"sm\"] .Layout,\n.Alert[data-size=\"sm\"].Layout {\n gap: var(--lea-spacing);\n}\n\n.Alert[data-size=\"sm\"] .Action {\n margin-block: calc(var(--lea-spacing) * -0.25);\n}\n\n/*\n * GlobalAlert: edge to edge, with the side padding coming from the inner\n * Container so the content lines up with the page.\n */\n\n.Alert[data-global] {\n width: 100%;\n padding-inline: 0;\n border-radius: 0;\n}\n\n.Alert[data-global][data-variant=\"outlined\"] {\n border-inline: 0;\n}\n\n.Layout[data-fluid] {\n max-width: none;\n}\n","\"use client\";\n\nimport { CircleAlert, CircleCheck, Info, TriangleAlert } from \"lucide-react\";\nimport type {\n ComponentPropsWithRef,\n ReactNode,\n SyntheticEvent,\n} from \"react\";\n\nimport { CloseButton } from \"../buttons/close-button/close-button\";\nimport type { ColorRoles } from \"../color-roles\";\nimport styles from \"./alert.module.css\";\n\nexport type AlertSeverity = ColorRoles<\n \"success\" | \"info\" | \"warning\" | \"error\"\n>;\nexport type AlertColor = AlertSeverity | ColorRoles<\"primary\" | \"neutral\">;\nexport type AlertVariant = \"standard\" | \"filled\" | \"outlined\";\nexport type AlertSize = \"sm\" | \"lg\";\n\nexport type AlertProps = Omit<ComponentPropsWithRef<\"div\">, \"color\"> & {\n /**\n * Semantic meaning — picks the icon and how screen readers announce it.\n * @default \"success\", or \"info\" with a brand `color`\n */\n severity?: AlertSeverity;\n /**\n * Colour scheme. Defaults to the colour of `severity`. `\"primary\"` (the\n * brand purple) and `\"neutral\"` (grey) carry no status meaning.\n */\n color?: AlertColor;\n /**\n * `\"standard\"` is a tinted fill, `\"filled\"` a solid one, `\"outlined\"` a\n * border only.\n * @default \"standard\"\n */\n variant?: AlertVariant;\n /**\n * `\"sm\"` for tighter padding and smaller text.\n * @default \"lg\"\n */\n size?: AlertSize;\n /** Replaces the severity icon. Pass `false` to hide it. */\n icon?: ReactNode | false;\n /** Content for the end of the alert, e.g. a button. Replaces the close button. */\n action?: ReactNode;\n /** Renders a close button that calls this when pressed. */\n onClose?: (event: SyntheticEvent) => void;\n /** Accessible name of the close button. Defaults to a translated \"Close\". */\n closeLabel?: string;\n};\n\nconst brandColours: AlertColor[] = [\"primary\", \"neutral\"];\n\n/** The icon of each severity. Shared with `Toast`. Internal. */\nexport const alertIcons: Record<AlertSeverity, ReactNode> = {\n success: <CircleCheck size=\"1em\" aria-hidden />,\n info: <Info size=\"1em\" aria-hidden />,\n warning: <TriangleAlert size=\"1em\" aria-hidden />,\n error: <CircleAlert size=\"1em\" aria-hidden />,\n};\n\n/**\n * The severity when none is given: `info` with a brand `color`, otherwise\n * `success`. Shared with `Toast`, so the two never disagree. Internal.\n */\nexport const resolveSeverity = (\n severity: AlertSeverity | undefined,\n color: AlertColor | undefined,\n): AlertSeverity =>\n severity ?? (color && brandColours.includes(color) ? \"info\" : \"success\");\n\n/**\n * Resolves the props shared by `Alert` and `GlobalAlert` into the root's\n * attributes and its inner markup. Internal — not exported from the package.\n */\nexport function resolveAlert({\n severity,\n color,\n variant = \"standard\",\n size = \"lg\",\n icon,\n action,\n onClose,\n closeLabel,\n role,\n children,\n ...rest\n}: AlertProps) {\n const resolvedSeverity = resolveSeverity(severity, color);\n\n const rootProps = {\n ...rest,\n role:\n role ??\n (resolvedSeverity === \"error\" || resolvedSeverity === \"warning\"\n ? \"alert\"\n : \"status\"),\n \"data-color\": color ?? resolvedSeverity,\n \"data-variant\": variant,\n \"data-size\": size,\n };\n\n const resolvedIcon = icon === undefined ? alertIcons[resolvedSeverity] : icon;\n const resolvedAction =\n action ??\n (onClose && (\n <CloseButton\n size=\"sm\"\n label={closeLabel}\n onClick={onClose}\n className={styles.Close}\n />\n ));\n\n const content = (\n <>\n {resolvedIcon !== false && resolvedIcon != null && (\n <div className={styles.Icon}>{resolvedIcon}</div>\n )}\n <div className={styles.Message}>{children}</div>\n {resolvedAction && <div className={styles.Action}>{resolvedAction}</div>}\n </>\n );\n\n return { rootProps, content };\n}\n","\"use client\";\n\nimport type { ComponentPropsWithRef } from \"react\";\n\nimport { type AlertProps, resolveAlert } from \"../alert-parts\";\nimport styles from \"../alert.module.css\";\n\nexport type {\n AlertColor,\n AlertProps,\n AlertSeverity,\n AlertSize,\n AlertVariant,\n} from \"../alert-parts\";\n\n/**\n * A message with Lea Bank's status colours. The ARIA `role` is derived from\n * `severity`: `error` / `warning` announce assertively (`role=\"alert\"`) while\n * `info` / `success` announce politely (`role=\"status\"`). Pass an explicit\n * `role` to override.\n *\n * With `onClose`, a close button is rendered with a translated accessible\n * name — override it per-instance with `closeLabel`.\n *\n * ```tsx\n * <Alert color=\"primary\" size=\"sm\">\n * Lånet forutsetter at du løser skulder for totalt 80 000 kr.\n * </Alert>\n * ```\n */\nexport function Alert({ className, ...props }: AlertProps) {\n const { rootProps, content } = resolveAlert(props);\n\n return (\n <div\n {...rootProps}\n className={[styles.Alert, styles.Layout, className]\n .filter(Boolean)\n .join(\" \")}\n >\n {content}\n </div>\n );\n}\n\nexport type AlertTitleProps = ComponentPropsWithRef<\"div\">;\n\n/** A heading above the alert's message. */\nexport function AlertTitle({ className, ...rest }: AlertTitleProps) {\n return (\n <div\n {...rest}\n className={[styles.Title, className].filter(Boolean).join(\" \")}\n />\n );\n}\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n * `--lea-spacing` is the 0.5rem base unit, multiplied to match `spacing(n)`.\n */\n\n.Backdrop {\n position: fixed;\n z-index: 1300;\n background-color: var(--lea-palette-bg-overlay);\n inset: 0;\n opacity: 1;\n transition: opacity var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n.Backdrop[data-starting-style],\n.Backdrop[data-ending-style] {\n opacity: 0;\n}\n\n.Viewport {\n position: fixed;\n z-index: 1300;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: calc(var(--lea-spacing) * 2);\n inset: 0;\n /*\n * The viewport covers the window so the popup can be centred, but only the\n * popup should swallow clicks — presses that land here are outside presses\n * and must dismiss the dialog.\n */\n pointer-events: none;\n}\n\n.Popup {\n display: flex;\n overflow: hidden;\n width: 100%;\n max-height: 100%;\n flex-direction: column;\n border-radius: var(--lea-radius-4xl);\n background-color: var(--lea-palette-bg-base);\n box-shadow: var(--lea-shadow-xl);\n color: var(--lea-palette-text-base);\n opacity: 1;\n pointer-events: auto;\n transform: scale(1);\n transition:\n opacity var(--lea-duration-normal) var(--lea-easing-standard),\n transform var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n.Popup[data-starting-style],\n.Popup[data-ending-style] {\n opacity: 0;\n transform: scale(0.95);\n}\n\n.Popup[data-size=\"xs\"] {\n max-width: 27.75rem;\n}\n\n.Popup[data-size=\"sm\"] {\n max-width: 37.5rem;\n}\n\n.Popup[data-size=\"md\"] {\n max-width: 56.25rem;\n}\n\n.Popup[data-size=\"lg\"] {\n max-width: 75rem;\n}\n\n.Header {\n display: flex;\n flex-shrink: 0;\n align-items: flex-start;\n justify-content: space-between;\n gap: var(--lea-spacing);\n padding: calc(var(--lea-spacing) * 3) calc(var(--lea-spacing) * 3)\n calc(var(--lea-spacing) * 2);\n}\n\n.Title {\n margin: 0;\n color: var(--lea-palette-text-base);\n font-size: var(--lea-text-heading-sm-font-size);\n font-weight: var(--lea-text-heading-sm-font-weight);\n line-height: var(--lea-text-heading-sm-line-height);\n letter-spacing: var(--lea-text-heading-sm-letter-spacing);\n}\n\n.Description {\n margin: 0 0 calc(var(--lea-spacing) * 2);\n color: inherit;\n}\n\n/*\n * Set when the description is the last thing in the body. A `:last-child`\n * selector would not do: body content is often a bare string, and text nodes\n * don't count as children.\n */\n.Description[data-last] {\n margin-bottom: 0;\n}\n\n.Content {\n overflow-y: auto;\n flex: 1;\n padding: 0 calc(var(--lea-spacing) * 3) calc(var(--lea-spacing) * 3);\n color: var(--lea-palette-text-base);\n}\n\n/* The footer's top padding takes over the gap, mirroring the header. */\n.Content:has(+ .Footer) {\n padding-bottom: 0;\n}\n\n.Footer {\n display: flex;\n flex-shrink: 0;\n flex-wrap: wrap;\n align-items: center;\n justify-content: flex-end;\n gap: var(--lea-spacing);\n padding: calc(var(--lea-spacing) * 2) calc(var(--lea-spacing) * 3)\n calc(var(--lea-spacing) * 2);\n background-color: var(--lea-palette-bg-base);\n}\n\n.CloseButton {\n display: inline-flex;\n width: 2.5rem;\n height: 2.5rem;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n padding: 0;\n border: 2px solid var(--lea-palette-border-base);\n border-radius: var(--lea-radius-full);\n margin-inline-end: calc(var(--lea-spacing) * -0.5);\n background-color: transparent;\n color: var(--lea-palette-text-base);\n cursor: pointer;\n transition:\n border-color var(--lea-duration-emphasis) var(--lea-easing-spring),\n background-color var(--lea-duration-emphasis) var(--lea-easing-spring),\n color var(--lea-duration-emphasis) var(--lea-easing-spring);\n}\n\n@media (hover: hover) {\n .CloseButton:hover {\n border-color: var(--lea-palette-primary-base);\n background-color: var(--lea-palette-primary-base);\n color: var(--lea-palette-primary-contrast);\n }\n}\n\n.CloseButton:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n/*\n * Fills the screen rather than floating as a card. `100dvh` follows the mobile\n * browser chrome as it collapses, and the safe-area inset keeps the content\n * clear of the home indicator.\n */\n@media (max-width: 599.95px) {\n .Viewport:has(.Popup[data-full-screen-on-mobile]) {\n padding: 0;\n }\n\n .Popup[data-full-screen-on-mobile] {\n width: 100vw;\n max-width: none;\n height: 100dvh;\n max-height: none;\n border-radius: 0;\n }\n\n .Popup[data-full-screen-on-mobile] .Content {\n padding-bottom: calc(\n var(--lea-spacing) * 3 + env(safe-area-inset-bottom)\n );\n }\n\n .Popup[data-full-screen-on-mobile] .Content:has(+ .Footer) {\n padding-bottom: 0;\n }\n\n .Popup[data-full-screen-on-mobile] .Footer {\n padding-bottom: calc(\n var(--lea-spacing) * 3 + env(safe-area-inset-bottom)\n );\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Backdrop,\n .Popup,\n .CloseButton {\n transition-duration: 0.01ms;\n }\n}\n","\"use client\";\n\nimport { Dialog as BaseDialog } from \"@base-ui/react/dialog\";\nimport { X } from \"lucide-react\";\nimport type { ComponentPropsWithRef } from \"react\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport styles from \"./dialog.module.css\";\nimport { useCloseLabel } from \"../use-close-label\";\n\n/**\n * Maximum width of the popup. The names match the breakpoint scale the library\n * used before, so `size=\"xs\"` is still the compact confirm dialog.\n */\nexport type Size = \"xs\" | \"sm\" | \"md\" | \"lg\";\n\n/**\n * Groups all parts of the dialog and owns the open state. Renders no element\n * of its own. Control it with `open` + `onOpenChange`, or leave both off and\n * let a `Dialog.Trigger` drive it.\n */\nexport const Root = BaseDialog.Root;\nexport type RootProps<Payload = unknown> = BaseDialog.Root.Props<Payload>;\n\n/**\n * Opens the dialog. Renders a bare `<button>`, so pass a styled control\n * through `render` — `<Dialog.Trigger render={<Button />}>Open</Dialog.Trigger>`.\n */\nexport const Trigger = BaseDialog.Trigger;\nexport type TriggerProps<Payload = unknown> = BaseDialog.Trigger.Props<Payload>;\n\n/** Moves the backdrop and popup to the end of `<body>`. */\nexport const Portal = BaseDialog.Portal;\nexport type PortalProps = BaseDialog.Portal.Props;\n\nexport type BackdropProps = BaseDialog.Backdrop.Props;\n\n/** The dimmed overlay behind the popup. */\nexport function Backdrop({ className, ...rest }: BackdropProps) {\n return (\n <BaseDialog.Backdrop\n {...rest}\n className={mergeClassName(styles.Backdrop, className)}\n />\n );\n}\n\nexport type ViewportProps = BaseDialog.Viewport.Props;\n\n/** Centres the popup in the window and keeps a gutter at the screen edges. */\nexport function Viewport({ className, ...rest }: ViewportProps) {\n return (\n <BaseDialog.Viewport\n {...rest}\n className={mergeClassName(styles.Viewport, className)}\n />\n );\n}\n\nexport type PopupProps = BaseDialog.Popup.Props & {\n /**\n * Maximum width of the surface.\n * @default \"sm\"\n */\n size?: Size;\n /**\n * Fills the screen on narrow viewports (below 600px) instead of floating as\n * a card. Use it for flows that need the whole screen, such as a multi-step\n * form or a long consent list.\n */\n fullScreenOnMobile?: boolean;\n};\n\n/**\n * The dialog surface. Lays its children out in a column so a `Dialog.Content`\n * can scroll while the header stays put.\n */\nexport function Popup({\n size = \"sm\",\n fullScreenOnMobile,\n className,\n ...rest\n}: PopupProps) {\n return (\n <BaseDialog.Popup\n {...rest}\n data-size={size}\n data-full-screen-on-mobile={fullScreenOnMobile || undefined}\n className={mergeClassName(styles.Popup, className)}\n />\n );\n}\n\nexport type HeaderProps = ComponentPropsWithRef<\"div\">;\n\n/**\n * The title row. Anything after the `Dialog.Title` sits on the same line,\n * which is where `Dialog.CloseButton` belongs. Stays fixed while\n * `Dialog.Content` scrolls.\n */\nexport function Header({ className, ...rest }: HeaderProps) {\n return (\n <div\n {...rest}\n className={[styles.Header, className].filter(Boolean).join(\" \")}\n />\n );\n}\n\nexport type TitleProps = BaseDialog.Title.Props;\n\n/**\n * The heading. Base UI links it to the popup as the accessible name, so no id\n * wiring is needed.\n */\nexport function Title({ className, ...rest }: TitleProps) {\n return (\n <BaseDialog.Title\n {...rest}\n className={mergeClassName(styles.Title, className)}\n />\n );\n}\n\nexport type DescriptionProps = BaseDialog.Description.Props;\n\n/** Supporting text, linked to the popup as its accessible description. */\nexport function Description({ className, ...rest }: DescriptionProps) {\n return (\n <BaseDialog.Description\n {...rest}\n className={mergeClassName(styles.Description, className)}\n />\n );\n}\n\nexport type ContentProps = ComponentPropsWithRef<\"div\">;\n\n/** The body. Scrolls on its own once the popup runs out of height. */\nexport function Content({ className, ...rest }: ContentProps) {\n return (\n <div\n {...rest}\n className={[styles.Content, className].filter(Boolean).join(\" \")}\n />\n );\n}\n\nexport type FooterProps = ComponentPropsWithRef<\"div\">;\n\n/**\n * The action row, placed after `Dialog.Content`. Stays pinned to the bottom of\n * the popup while `Dialog.Content` scrolls, and lines its children up at the\n * end.\n */\nexport function Footer({ className, ...rest }: FooterProps) {\n return (\n <div\n {...rest}\n className={[styles.Footer, className].filter(Boolean).join(\" \")}\n />\n );\n}\n\n/**\n * Closes the dialog. Renders a bare `<button>` for text actions — use\n * `render` for a styled one: `<Dialog.Close render={<Button />}>Cancel</Dialog.Close>`.\n * For the X in the header corner, use `Dialog.CloseButton`.\n */\nexport const Close = BaseDialog.Close;\nexport type CloseProps = BaseDialog.Close.Props;\n\nexport type CloseButtonProps = Omit<\n BaseDialog.Close.Props,\n \"children\" | \"aria-label\"\n> & {\n /** Accessible name. Defaults to a translated \"Close\". */\n label?: string;\n};\n\n/**\n * The round X in the header corner. It renders its own icon and always has an\n * accessible name, so it cannot be built without one.\n */\nexport function CloseButton({ label, className, ...rest }: CloseButtonProps) {\n const t = useCloseLabel();\n\n return (\n <BaseDialog.Close\n {...rest}\n aria-label={label ?? t.close}\n className={mergeClassName(styles.CloseButton, className)}\n >\n <X size={20} aria-hidden />\n </BaseDialog.Close>\n );\n}\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n * `--lea-spacing` is the 0.5rem base unit, multiplied to match `spacing(n)`.\n *\n * Positioning keys off `data-swipe-direction`, which Base UI sets on the popup\n * from the root's `swipeDirection` — the same value `Drawer.Root` derives from\n * `side`, so the panel can never sit on one edge and swipe towards another.\n */\n\n.Backdrop {\n position: fixed;\n z-index: 1300;\n background-color: var(--lea-palette-bg-overlay);\n inset: 0;\n /* Fades in step with the swipe, so the panel feels attached to the finger. */\n opacity: calc(1 - var(--drawer-swipe-progress, 0));\n transition: opacity var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n.Backdrop[data-starting-style],\n.Backdrop[data-ending-style] {\n opacity: 0;\n}\n\n.Viewport {\n position: fixed;\n z-index: 1300;\n inset: 0;\n /* Only the panel should swallow clicks; presses elsewhere dismiss it. */\n pointer-events: none;\n}\n\n.Popup {\n position: absolute;\n display: flex;\n flex-direction: column;\n background-color: var(--lea-palette-bg-base);\n box-shadow: var(--lea-shadow-xl);\n color: var(--lea-palette-text-base);\n pointer-events: auto;\n transition: transform var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n.Popup[data-swipe-direction=\"left\"],\n.Popup[data-swipe-direction=\"right\"] {\n top: 0;\n width: min(var(--drawer-size, 22.5rem), 100vw);\n height: 100%;\n transform: translateX(var(--drawer-swipe-movement-x, 0px));\n}\n\n.Popup[data-swipe-direction=\"left\"] {\n left: 0;\n}\n\n.Popup[data-swipe-direction=\"right\"] {\n right: 0;\n}\n\n.Popup[data-swipe-direction=\"up\"],\n.Popup[data-swipe-direction=\"down\"] {\n left: 0;\n width: 100%;\n /* Sheets size to their content; `--drawer-size` caps them lower if needed. */\n max-height: var(--drawer-size, 90dvh);\n transform: translateY(\n calc(\n var(--drawer-snap-point-offset, 0px) + var(--drawer-swipe-movement-y, 0px)\n )\n );\n}\n\n/* Sheets round the edge that faces the content, the way a card would. */\n.Popup[data-swipe-direction=\"up\"] {\n top: 0;\n border-radius: 0 0 var(--drawer-radius, var(--lea-radius-4xl))\n var(--drawer-radius, var(--lea-radius-4xl));\n}\n\n.Popup[data-swipe-direction=\"down\"] {\n bottom: 0;\n border-radius: var(--drawer-radius, var(--lea-radius-4xl))\n var(--drawer-radius, var(--lea-radius-4xl)) 0 0;\n padding-bottom: env(safe-area-inset-bottom);\n}\n\n.Popup[data-starting-style][data-swipe-direction=\"left\"],\n.Popup[data-ending-style][data-swipe-direction=\"left\"] {\n transform: translateX(-100%);\n}\n\n.Popup[data-starting-style][data-swipe-direction=\"right\"],\n.Popup[data-ending-style][data-swipe-direction=\"right\"] {\n transform: translateX(100%);\n}\n\n.Popup[data-starting-style][data-swipe-direction=\"up\"],\n.Popup[data-ending-style][data-swipe-direction=\"up\"] {\n transform: translateY(-100%);\n}\n\n.Popup[data-starting-style][data-swipe-direction=\"down\"],\n.Popup[data-ending-style][data-swipe-direction=\"down\"] {\n transform: translateY(100%);\n}\n\n/* A flick should settle faster than a slow drag; a live drag must not lag. */\n.Popup[data-ending-style],\n.Backdrop[data-ending-style] {\n transition-duration: calc(\n var(--drawer-swipe-strength, 1) * var(--lea-duration-normal)\n );\n}\n\n.Popup[data-swiping],\n.Backdrop[data-swiping] {\n transition-duration: 0ms;\n}\n\n.Handle {\n width: calc(var(--lea-spacing) * 5);\n height: calc(var(--lea-spacing) * 0.5);\n flex-shrink: 0;\n border-radius: var(--lea-radius-sm);\n margin: calc(var(--lea-spacing) * 1.5) auto calc(var(--lea-spacing) * 0.5);\n background-color: var(--lea-palette-border-base);\n}\n\n/* A top sheet is dragged upwards, so its handle belongs on the bottom edge. */\n.Popup[data-swipe-direction=\"up\"] .Handle {\n margin: calc(var(--lea-spacing) * 0.5) auto calc(var(--lea-spacing) * 1.5);\n}\n\n.Header {\n display: flex;\n flex-shrink: 0;\n align-items: flex-start;\n justify-content: space-between;\n gap: var(--lea-spacing);\n padding: calc(var(--lea-spacing) * 3) calc(var(--lea-spacing) * 3)\n calc(var(--lea-spacing) * 2);\n}\n\n.Title {\n margin: 0;\n color: var(--lea-palette-text-base);\n font-size: var(--lea-text-heading-sm-font-size);\n font-weight: var(--lea-text-heading-sm-font-weight);\n line-height: var(--lea-text-heading-sm-line-height);\n letter-spacing: var(--lea-text-heading-sm-letter-spacing);\n}\n\n.Description {\n margin: 0 0 calc(var(--lea-spacing) * 2);\n color: inherit;\n}\n\n/*\n * Set when the description is the last thing in the body. A `:last-child`\n * selector would not do: body content is often a bare string, and text nodes\n * don't count as children.\n */\n.Description[data-last] {\n margin-bottom: 0;\n}\n\n.Content {\n overflow-y: auto;\n flex: 1;\n padding: 0 calc(var(--lea-spacing) * 3) calc(var(--lea-spacing) * 3);\n color: var(--lea-palette-text-base);\n}\n\n.CloseButton {\n display: inline-flex;\n width: 2.5rem;\n height: 2.5rem;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n padding: 0;\n border: 2px solid var(--lea-palette-border-base);\n border-radius: var(--lea-radius-full);\n margin-inline-end: calc(var(--lea-spacing) * -0.5);\n background-color: transparent;\n color: var(--lea-palette-text-base);\n cursor: pointer;\n transition: border-color var(--lea-duration-emphasis) var(--lea-easing-spring),\n background-color var(--lea-duration-emphasis) var(--lea-easing-spring),\n color var(--lea-duration-emphasis) var(--lea-easing-spring);\n}\n\n@media (hover: hover) {\n .CloseButton:hover {\n border-color: var(--lea-palette-primary-base);\n background-color: var(--lea-palette-primary-base);\n color: var(--lea-palette-primary-contrast);\n }\n}\n\n.CloseButton:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Backdrop,\n .Popup,\n .CloseButton {\n transition-duration: 0.01ms;\n }\n}\n","\"use client\";\n\nimport { Drawer as BaseDrawer } from \"@base-ui/react/drawer\";\nimport { X } from \"lucide-react\";\nimport type { ComponentPropsWithRef } from \"react\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport styles from \"./drawer.module.css\";\nimport { useCloseLabel } from \"../use-close-label\";\n\n/** Screen edge the panel slides in from. */\nexport type Side = \"left\" | \"right\" | \"top\" | \"bottom\";\n\n/**\n * The panel is dismissed by swiping it back towards the edge it came from, so\n * the gesture direction follows from the side.\n */\nconst SWIPE_DIRECTION = {\n left: \"left\",\n right: \"right\",\n top: \"up\",\n bottom: \"down\",\n} as const;\n\nexport type RootProps<Payload = unknown> = Omit<\n BaseDrawer.Root.Props<Payload>,\n \"swipeDirection\"\n> & {\n /**\n * Screen edge the panel slides in from. Also sets the swipe-to-dismiss\n * direction, so the two can never disagree.\n * @default \"right\"\n */\n side?: Side;\n};\n\n/**\n * Groups all parts of the drawer and owns the open state. Renders no element\n * of its own. Control it with `open` + `onOpenChange`, or leave both off and\n * let a `Drawer.Trigger` drive it.\n */\nexport function Root<Payload = unknown>({\n side = \"right\",\n ...rest\n}: RootProps<Payload>) {\n return <BaseDrawer.Root {...rest} swipeDirection={SWIPE_DIRECTION[side]} />;\n}\n\n/**\n * Opens the drawer. Renders a bare `<button>`, so pass a styled control\n * through `render` — `<Drawer.Trigger render={<Button />}>Open</Drawer.Trigger>`.\n */\nexport const Trigger = BaseDrawer.Trigger;\nexport type TriggerProps<Payload = unknown> = BaseDrawer.Trigger.Props<Payload>;\n\n/** Moves the backdrop and popup to the end of `<body>`. */\nexport const Portal = BaseDrawer.Portal;\nexport type PortalProps = BaseDrawer.Portal.Props;\n\nexport type BackdropProps = BaseDrawer.Backdrop.Props;\n\n/** The dimmed overlay behind the panel. Fades out as the panel is swiped away. */\nexport function Backdrop({ className, ...rest }: BackdropProps) {\n return (\n <BaseDrawer.Backdrop\n {...rest}\n className={mergeClassName(styles.Backdrop, className)}\n />\n );\n}\n\nexport type ViewportProps = BaseDrawer.Viewport.Props;\n\n/**\n * Positioning container for the panel. Also the element Base UI offsets when\n * the on-screen keyboard appears.\n */\nexport function Viewport({ className, ...rest }: ViewportProps) {\n return (\n <BaseDrawer.Viewport\n {...rest}\n className={mergeClassName(styles.Viewport, className)}\n />\n );\n}\n\nexport type PopupProps = BaseDrawer.Popup.Props;\n\n/**\n * The panel surface, pinned to the edge given by `side` on `Drawer.Root`.\n * Side panels are `min(22.5rem, 100vw)` wide and sheets are at most 90% of the\n * screen height; override either with the `--drawer-size` custom property.\n */\nexport function Popup({ className, ...rest }: PopupProps) {\n return (\n <BaseDrawer.Popup\n {...rest}\n className={mergeClassName(styles.Popup, className)}\n />\n );\n}\n\nexport type HandleProps = ComponentPropsWithRef<\"div\">;\n\n/**\n * The grab bar at the top of a bottom sheet. Decorative — the panel is\n * swipe-dismissable anywhere, so this is a hint rather than a control.\n */\nexport function Handle({ className, ...rest }: HandleProps) {\n return (\n <div\n {...rest}\n aria-hidden\n className={[styles.Handle, className].filter(Boolean).join(\" \")}\n />\n );\n}\n\nexport type HeaderProps = ComponentPropsWithRef<\"div\">;\n\n/**\n * The title row. Anything after the `Drawer.Title` sits on the same line,\n * which is where `Drawer.CloseButton` belongs. Stays fixed while\n * `Drawer.Content` scrolls.\n */\nexport function Header({ className, ...rest }: HeaderProps) {\n return (\n <div\n {...rest}\n className={[styles.Header, className].filter(Boolean).join(\" \")}\n />\n );\n}\n\nexport type TitleProps = BaseDrawer.Title.Props;\n\n/**\n * The heading. Base UI links it to the panel as the accessible name, so no id\n * wiring is needed.\n */\nexport function Title({ className, ...rest }: TitleProps) {\n return (\n <BaseDrawer.Title\n {...rest}\n className={mergeClassName(styles.Title, className)}\n />\n );\n}\n\nexport type DescriptionProps = BaseDrawer.Description.Props;\n\n/** Supporting text, linked to the panel as its accessible description. */\nexport function Description({ className, ...rest }: DescriptionProps) {\n return (\n <BaseDrawer.Description\n {...rest}\n className={mergeClassName(styles.Description, className)}\n />\n );\n}\n\nexport type ContentProps = BaseDrawer.Content.Props;\n\n/**\n * The body. Scrolls on its own once the panel runs out of height, and lets a\n * mouse select text inside it without starting a swipe.\n */\nexport function Content({ className, ...rest }: ContentProps) {\n return (\n <BaseDrawer.Content\n {...rest}\n className={mergeClassName(styles.Content, className)}\n />\n );\n}\n\n/**\n * Closes the drawer. Renders a bare `<button>` for text actions — use\n * `render` for a styled one: `<Drawer.Close render={<Button />}>Cancel</Drawer.Close>`.\n * For the X in the header corner, use `Drawer.CloseButton`.\n */\nexport const Close = BaseDrawer.Close;\nexport type CloseProps = BaseDrawer.Close.Props;\n\nexport type CloseButtonProps = Omit<\n BaseDrawer.Close.Props,\n \"children\" | \"aria-label\"\n> & {\n /** Accessible name. Defaults to a translated \"Close\". */\n label?: string;\n};\n\n/**\n * The round X in the header corner. It renders its own icon and always has an\n * accessible name, so it cannot be built without one.\n */\nexport function CloseButton({ label, className, ...rest }: CloseButtonProps) {\n const t = useCloseLabel();\n\n return (\n <BaseDrawer.Close\n {...rest}\n aria-label={label ?? t.close}\n className={mergeClassName(styles.CloseButton, className)}\n >\n <X size={20} aria-hidden />\n </BaseDrawer.Close>\n );\n}\n",".Container {\n box-sizing: border-box;\n width: 100%;\n max-width: var(--lea-breakpoint-lg);\n padding-inline: calc(var(--lea-spacing) * 2);\n margin-inline: auto;\n}\n\n.Container[data-size=\"sm\"] {\n max-width: var(--lea-breakpoint-sm);\n}\n\n.Container[data-size=\"md\"] {\n max-width: var(--lea-breakpoint-md);\n}\n\n.Container[data-size=\"xl\"] {\n max-width: var(--lea-breakpoint-xl);\n}\n\n@media (min-width: 600px) {\n .Container {\n padding-inline: calc(var(--lea-spacing) * 3);\n }\n}\n\n@media (min-width: 900px) {\n .Container {\n padding-inline: calc(var(--lea-spacing) * 4);\n }\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\n\nimport type { Breakpoint } from \"../../../theme/tokens/breakpoints\";\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport styles from \"./container.module.css\";\n\nexport type ContainerSize = Exclude<Breakpoint, \"xs\">;\n\nexport type ContainerProps = Omit<useRender.ComponentProps<\"div\">, \"ref\"> & {\n /**\n * Maximum width, named after the breakpoint it matches: `\"md\"` stops\n * growing at the `md` breakpoint width.\n * @default \"lg\"\n */\n size?: ContainerSize;\n};\n\n/**\n * Centres page content, caps its width and keeps it clear of the screen edge.\n * The side padding grows with the viewport: `spacing(2)` on phones,\n * `spacing(3)` from `sm` and `spacing(4)` from `md`.\n *\n * ```tsx\n * <Container render={<main />}>\n * <Text variant=\"h1\">Mine lån</Text>\n * </Container>\n * ```\n */\nexport function Container({\n size = \"lg\",\n render,\n className,\n ...rest\n}: ContainerProps) {\n return useRender({\n render,\n props: {\n ...rest,\n \"data-size\": size,\n className: mergeClassName(styles.Container, className)({}),\n },\n });\n}\n","\"use client\";\n\nimport { Container } from \"../../layout/container/container\";\nimport { type AlertProps, resolveAlert } from \"../alert-parts\";\nimport styles from \"../alert.module.css\";\n\nexport type GlobalAlertProps = AlertProps & {\n /**\n * Lets the content span the full width instead of stopping at the `lg`\n * breakpoint. The side padding stays.\n * @default false\n */\n fluid?: boolean;\n innerClassName?: string;\n};\n\n/**\n * An `Alert` that spans the whole page width, for messages about the whole\n * page or session. Place it directly above or below `AppHeader`. It takes the\n * same props and styling as `Alert`, without the rounded corners, and its\n * content follows the `Container` breakpoints and side padding.\n *\n * ```tsx\n * <AppHeader />\n * <GlobalAlert severity=\"warning\" onClose={dismiss}>\n * <AlertTitle>Planned maintenance</AlertTitle>\n * Online banking is unavailable tonight from 22:00 to 23:00.\n * </GlobalAlert>\n * ```\n */\nexport function GlobalAlert({\n fluid = false,\n className,\n innerClassName,\n ...props\n}: GlobalAlertProps) {\n const { rootProps, content } = resolveAlert(props);\n\n return (\n <div\n {...rootProps}\n data-global=\"\"\n className={[styles.Alert, className].filter(Boolean).join(\" \")}\n >\n <Container\n className={[styles.Layout, innerClassName].filter(Boolean).join(\" \")}\n data-fluid={fluid || undefined}\n >\n {content}\n </Container>\n </div>\n );\n}\n","\"use client\";\n\nimport type { ReactNode } from \"react\";\n\nimport * as Dialog from \"./dialog\";\n\nexport type SimpleDialogProps = Omit<Dialog.RootProps, \"children\"> & {\n /** Heading. Becomes the dialog's accessible name. */\n title: ReactNode;\n /** Optional supporting text. Becomes the accessible description. */\n description?: ReactNode;\n /** Body content rendered below the title / description. */\n children?: ReactNode;\n /** Accessible name for the close button. Defaults to a translated \"Close\". */\n closeLabel?: string;\n /**\n * Maximum width of the surface.\n * @default \"sm\"\n */\n size?: Dialog.Size;\n /** Fills the screen below 600px instead of floating as a card. */\n fullScreenOnMobile?: boolean;\n /** Hide close button. */\n hideCloseButton?: boolean;\n};\n\n/**\n * The ready-made modal: pass `title`, an optional `description` and\n * `children`, and it renders the Lea chrome — a white, large-radius surface\n * over a dark overlay, a title row with a close button, and a scrollable body.\n *\n * Reach for `Dialog` (the parts) when you need a different layout.\n *\n * ```tsx\n * <SimpleDialog\n * open={open}\n * onOpenChange={setOpen}\n * title=\"Update your details\"\n * description=\"Keep your contact information up to date.\"\n * >\n * <Input label=\"Phone\" />\n * </SimpleDialog>\n * ```\n */\nexport function SimpleDialog({\n title,\n description,\n children,\n closeLabel,\n size,\n fullScreenOnMobile,\n hideCloseButton,\n ...rest\n}: SimpleDialogProps) {\n return (\n <Dialog.Root {...rest}>\n <Dialog.Portal>\n <Dialog.Backdrop />\n <Dialog.Viewport>\n <Dialog.Popup size={size} fullScreenOnMobile={fullScreenOnMobile}>\n <Dialog.Header>\n <Dialog.Title>{title}</Dialog.Title>\n {!hideCloseButton && <Dialog.CloseButton label={closeLabel} />}\n </Dialog.Header>\n <Dialog.Content>\n {description != null && (\n <Dialog.Description data-last={children == null || undefined}>\n {description}\n </Dialog.Description>\n )}\n {children}\n </Dialog.Content>\n </Dialog.Popup>\n </Dialog.Viewport>\n </Dialog.Portal>\n </Dialog.Root>\n );\n}\n","\"use client\";\n\nimport type { ReactNode } from \"react\";\n\nimport * as Drawer from \"./drawer\";\n\nexport type SimpleDrawerProps = Omit<Drawer.RootProps, \"children\"> & {\n /** Heading. Becomes the drawer's accessible name. */\n title: ReactNode;\n /** Optional supporting text. Becomes the accessible description. */\n description?: ReactNode;\n /** Body content rendered below the title / description. */\n children?: ReactNode;\n /** Accessible name for the close button. Defaults to a translated \"Close\". */\n closeLabel?: string;\n};\n\n/**\n * The ready-made side panel: pass `title`, an optional `description` and\n * `children`, and it renders the Lea chrome — a white surface that slides in\n * from the edge over a dark overlay, a header row with a close button, and a\n * scrollable body. Swipe-to-dismiss comes for free, towards `side`.\n *\n * Bottom and top sheets (`side=\"bottom\"` / `\"top\"`) get a grab handle and\n * round the edge that faces the content.\n *\n * Reach for `Drawer` (the parts) when you need a different layout.\n *\n * ```tsx\n * <SimpleDrawer open={open} onOpenChange={setOpen} title=\"Filters\">\n * <FilterForm />\n * </SimpleDrawer>\n * ```\n */\nexport function SimpleDrawer({\n title,\n description,\n children,\n closeLabel,\n side = \"right\",\n ...rest\n}: SimpleDrawerProps) {\n return (\n <Drawer.Root {...rest} side={side}>\n <Drawer.Portal>\n <Drawer.Backdrop />\n <Drawer.Viewport>\n <Drawer.Popup>\n {/* The handle sits on the edge the sheet is dragged towards. */}\n {side === \"bottom\" && <Drawer.Handle />}\n <Drawer.Header>\n <Drawer.Title>{title}</Drawer.Title>\n <Drawer.CloseButton label={closeLabel} />\n </Drawer.Header>\n <Drawer.Content>\n {description != null && (\n <Drawer.Description data-last={children == null || undefined}>\n {description}\n </Drawer.Description>\n )}\n {children}\n </Drawer.Content>\n {side === \"top\" && <Drawer.Handle />}\n </Drawer.Popup>\n </Drawer.Viewport>\n </Drawer.Portal>\n </Drawer.Root>\n );\n}\n","/*\n * A toast is an Alert that floats: its colours, padding, icon and text come\n * from alert.module.css, which sits on the same elements. These rules only\n * place and stack it, and set no property that alert.module.css also sets.\n *\n * The stacking follows Base UI's own example. Base UI writes the index of each\n * toast and the measured heights to the custom properties `--toast-*`.\n */\n\n.Viewport {\n position: fixed;\n /* Above Dialog and Drawer (1300), below Tooltip (1500). */\n z-index: 1400;\n inset-block-end: calc(var(--lea-spacing-2) + env(safe-area-inset-bottom));\n inset-inline: var(--lea-spacing-2);\n}\n\n/* The gutters follow Container's. */\n@media (min-width: 600px) {\n .Viewport {\n width: 22.5rem;\n inset-block-end: calc(var(--lea-spacing-3) + env(safe-area-inset-bottom));\n inset-inline: auto var(--lea-spacing-3);\n }\n}\n\n@media (min-width: 900px) {\n .Viewport {\n inset-block-end: calc(var(--lea-spacing-4) + env(safe-area-inset-bottom));\n inset-inline-end: var(--lea-spacing-4);\n }\n}\n\n.Toast {\n /* How far each toast behind the front one peeks out above it. */\n --toast-peek: var(--lea-spacing);\n /* The space between toasts once the stack spreads out. */\n --toast-gap: var(--lea-spacing);\n --toast-scale: calc(max(0, 1 - (var(--toast-index) * 0.1)));\n --toast-shrink: calc(1 - var(--toast-scale));\n /* Collapsed, every toast takes the height of the front one. */\n --toast-stack-height: var(--toast-frontmost-height, var(--toast-height));\n --toast-spread-y: calc(\n var(--toast-offset-y) * -1 + (var(--toast-index) * var(--toast-gap) * -1) +\n var(--toast-swipe-movement-y)\n );\n\n position: absolute;\n z-index: calc(1000 - var(--toast-index));\n width: 100%;\n height: var(--toast-stack-height);\n box-shadow: var(--lea-shadow-lg);\n cursor: default;\n inset-block-end: 0;\n inset-inline-end: 0;\n transform: translateX(var(--toast-swipe-movement-x))\n translateY(\n calc(\n var(--toast-swipe-movement-y) - (var(--toast-index) * var(--toast-peek)) -\n (var(--toast-shrink) * var(--toast-stack-height))\n )\n )\n scale(var(--toast-scale));\n transform-origin: bottom center;\n transition:\n transform var(--lea-duration-emphasis) var(--lea-easing-standard),\n height var(--lea-duration-emphasis) var(--lea-easing-standard),\n opacity var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n/* Fills the gap between spread-out toasts, so the pointer stays on the stack. */\n.Toast::after {\n position: absolute;\n width: 100%;\n height: calc(var(--toast-gap) + 1px);\n content: \"\";\n inset-block-start: 100%;\n inset-inline-start: 0;\n}\n\n.Toast[data-expanded] {\n height: var(--toast-height);\n transform: translateX(var(--toast-swipe-movement-x))\n translateY(var(--toast-spread-y));\n}\n\n.Toast[data-starting-style],\n.Toast[data-ending-style] {\n transform: translateY(150%);\n}\n\n.Toast[data-limited],\n.Toast[data-ending-style] {\n opacity: 0;\n}\n\n/* A swiped toast leaves in the direction it was swiped. */\n\n.Toast[data-ending-style][data-swipe-direction=\"up\"] {\n transform: translateY(calc(var(--toast-swipe-movement-y) - 150%));\n}\n\n.Toast[data-ending-style][data-swipe-direction=\"down\"] {\n transform: translateY(calc(var(--toast-swipe-movement-y) + 150%));\n}\n\n.Toast[data-ending-style][data-swipe-direction=\"left\"] {\n transform: translateX(calc(var(--toast-swipe-movement-x) - 150%))\n translateY(var(--toast-spread-y));\n}\n\n.Toast[data-ending-style][data-swipe-direction=\"right\"] {\n transform: translateX(calc(var(--toast-swipe-movement-x) + 150%))\n translateY(var(--toast-spread-y));\n}\n\n.Toast:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n/* The toasts behind the front one show only their edge until the stack spreads. */\n.Content {\n overflow: hidden;\n /*\n * Room for the focus ring of the buttons (2px wide, 2px off), which the\n * overflow would otherwise cut off. The margin takes the room back, so the\n * layout is Alert's.\n */\n padding: var(--lea-spacing-0-5);\n margin: calc(var(--lea-spacing-0-5) * -1);\n transition: opacity var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n.Content[data-behind] {\n opacity: 0;\n}\n\n.Content[data-expanded] {\n opacity: 1;\n}\n\n.Description {\n margin: 0;\n}\n\n/* Under the message, so it never crowds the close button on a phone. */\n.ToastAction {\n align-self: flex-start;\n margin-block-start: var(--lea-spacing);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Toast,\n .Content {\n transition-duration: 0.01ms;\n }\n}\n","\"use client\";\n\nimport {\n Toast as BaseToast,\n type ToastManager as BaseToastManager,\n type ToastManagerAddOptions,\n} from \"@base-ui/react/toast\";\nimport {\n createContext,\n type MouseEvent,\n type ReactNode,\n useContext,\n useState,\n} from \"react\";\n\nimport { createTranslation } from \"../../../i18n\";\nimport { Button } from \"../../buttons/button/button\";\nimport { CloseButton } from \"../../buttons/close-button/close-button\";\nimport {\n type AlertColor,\n type AlertSeverity,\n alertIcons,\n resolveSeverity,\n} from \"../alert-parts\";\nimport alertStyles from \"../alert.module.css\";\nimport styles from \"./toast.module.css\";\n\nexport type ToastSeverity = AlertSeverity;\nexport type ToastColor = AlertColor;\n\nexport type ToastOptions = {\n /**\n * The heading. Required: the toast is announced as a dialog named by its\n * title, and a dialog without a name is not accessible.\n */\n title: ReactNode;\n /** The message under the title. */\n description?: ReactNode;\n /**\n * Semantic meaning — picks the icon and how urgently screen readers\n * announce it. The same values as `Alert`.\n * @default \"success\", or \"info\" with a brand `color`\n */\n severity?: ToastSeverity;\n /**\n * Colour scheme. Defaults to the colour of `severity`. `\"primary\"` (the\n * brand purple) and `\"neutral\"` (grey) carry no status meaning.\n */\n color?: ToastColor;\n /**\n * Milliseconds before the toast closes by itself. `0` keeps it until it is\n * closed. The timer pauses while the pointer is over the toasts, while they\n * have focus and while the window is hidden.\n * @default the provider's `timeout`, 5000\n */\n timeout?: number;\n /**\n * How screen readers announce it: `\"high\"` interrupts, `\"low\"` waits.\n * @default \"high\" for `error` and `warning`, otherwise \"low\"\n */\n priority?: \"low\" | \"high\";\n /**\n * A button under the message, e.g. \"Angre\". The toast may close before\n * someone reaches it, so it must not be the only way to do the thing.\n */\n action?: {\n label: string;\n onClick: (event: MouseEvent<HTMLButtonElement>) => void;\n };\n /** Called when the toast starts to close, by the timer or by a person. */\n onClose?: () => void;\n /** Called once the toast is gone, after its exit animation. */\n onRemove?: () => void;\n /**\n * Identifies the toast. Adding a toast with the id of one on screen\n * replaces it and restarts its timer.\n */\n id?: string;\n};\n\nexport type ToastManager = {\n /** Shows a toast and returns its id. */\n add: (options: ToastOptions) => string;\n /** Closes the toast with this id, or every toast when called without one. */\n close: (id?: string) => void;\n};\n\ntype ToastData = { severity: ToastSeverity; color: ToastColor };\n\nconst toBaseOptions = ({\n severity,\n color,\n priority,\n action,\n ...rest\n}: ToastOptions): ToastManagerAddOptions<ToastData> => {\n const resolvedSeverity = resolveSeverity(severity, color);\n\n return {\n ...rest,\n type: resolvedSeverity,\n priority:\n priority ??\n (resolvedSeverity === \"error\" || resolvedSeverity === \"warning\"\n ? \"high\"\n : \"low\"),\n actionProps: action && {\n children: action.label,\n onClick: action.onClick,\n },\n data: { severity: resolvedSeverity, color: color ?? resolvedSeverity },\n };\n};\n\n/** The Base UI manager behind each of ours, for `ToastProvider`. */\nconst baseManagers = new WeakMap<ToastManager, BaseToastManager<ToastData>>();\n\n/**\n * Creates a toast manager to show toasts from outside React, such as an API\n * client. Pass it to `ToastProvider` as `toastManager`, and call `add` on it\n * anywhere. Create it once, in a module of its own:\n *\n * ```ts\n * export const toastManager = createToastManager();\n *\n * toastManager.add({ title: \"Sesjonen er utløpt\", severity: \"warning\" });\n * ```\n */\nexport function createToastManager(): ToastManager {\n const base = BaseToast.createToastManager<ToastData>();\n const manager: ToastManager = {\n add: (options) => base.add(toBaseOptions(options)),\n close: (id) => base.close(id),\n };\n baseManagers.set(manager, base);\n return manager;\n}\n\nconst ToastManagerContext = createContext<ToastManager | null>(null);\n\n/**\n * Returns the manager of the nearest `ToastProvider`, to show and close\n * toasts. The component calling it does not re-render when toasts change.\n *\n * ```tsx\n * const toast = useToastManager();\n * toast.add({ title: \"Endringene er lagret\" });\n * ```\n */\nexport function useToastManager(): ToastManager {\n const manager = useContext(ToastManagerContext);\n if (!manager) {\n throw new Error(\"useToastManager must be used inside a ToastProvider.\");\n }\n return manager;\n}\n\nconst useToastLabels = createTranslation({\n notifications: {\n no: \"Varsler\",\n se: \"Aviseringar\",\n fi: \"Ilmoitukset\",\n es: \"Notificaciones\",\n },\n});\n\nexport type ToastProviderProps = {\n children?: ReactNode;\n /**\n * Milliseconds before a toast closes by itself, unless the toast sets its\n * own. `0` keeps toasts until they are closed.\n * @default 5000\n */\n timeout?: number;\n /**\n * How many toasts are shown at once. Older ones are hidden beyond it.\n * @default 3\n */\n limit?: number;\n /**\n * A manager from `createToastManager`, to show toasts from outside React.\n * Without one, the provider makes its own.\n */\n toastManager?: ToastManager;\n};\n\n/**\n * Shows toasts: short messages that float at the bottom of the screen and\n * close by themselves. They have the statuses and colours of `Alert`. Render\n * it once, around the app, and show toasts with `useToastManager`, or with a\n * manager from `createToastManager` outside React.\n *\n * Toasts stack, newest in front, and spread out on hover. Keyboard users\n * reach them with F6. Every toast has a close button, and errors and\n * warnings are announced as soon as they appear.\n *\n * ```tsx\n * <ToastProvider>\n * <App />\n * </ToastProvider>\n * ```\n */\nexport function ToastProvider({\n children,\n timeout,\n limit,\n toastManager,\n}: ToastProviderProps) {\n const [ownManager] = useState(createToastManager);\n const manager = toastManager ?? ownManager;\n const t = useToastLabels();\n\n return (\n <ToastManagerContext.Provider value={manager}>\n <BaseToast.Provider\n timeout={timeout}\n limit={limit}\n toastManager={baseManagers.get(manager)}\n >\n {children}\n <BaseToast.Portal>\n <BaseToast.Viewport\n aria-label={t.notifications}\n className={styles.Viewport}\n >\n <ToastList />\n </BaseToast.Viewport>\n </BaseToast.Portal>\n </BaseToast.Provider>\n </ToastManagerContext.Provider>\n );\n}\n\nfunction ToastList() {\n const { toasts } = BaseToast.useToastManager<ToastData>();\n\n return toasts.map((toast) => <ToastItem key={toast.id} toast={toast} />);\n}\n\nfunction ToastItem({\n toast,\n}: {\n toast: BaseToast.Root.ToastObject<ToastData>;\n}) {\n const severity = toast.data?.severity ?? \"success\";\n\n return (\n <BaseToast.Root\n toast={toast}\n data-color={toast.data?.color ?? severity}\n data-variant=\"standard\"\n data-size=\"lg\"\n className={`${alertStyles.Alert} ${styles.Toast}`}\n >\n <BaseToast.Content className={`${alertStyles.Layout} ${styles.Content}`}>\n <div className={alertStyles.Icon}>{alertIcons[severity]}</div>\n <div className={alertStyles.Message}>\n {/* A `div`, as `AlertTitle` is: the toast sits outside the page's\n heading outline. */}\n <BaseToast.Title render={<div />} className={alertStyles.Title} />\n <BaseToast.Description className={styles.Description} />\n <BaseToast.Action\n render={<Button size=\"sm\" variant=\"outline\" color=\"secondary\" />}\n className={styles.ToastAction}\n />\n </div>\n <div className={alertStyles.Action}>\n <BaseToast.Close render={<CloseButton size=\"sm\" />} />\n </div>\n </BaseToast.Content>\n </BaseToast.Root>\n );\n}\n","/*\n * The website's loading blocks: `neutral-100`, 16px corners, fading between\n * 40 % and full opacity. 1.5s is the website's pulse: a loop has no duration\n * token (the button spinner turns in 0.8s).\n */\n.Skeleton {\n display: block;\n width: 100%;\n height: var(--lea-spacing-3);\n border-radius: var(--lea-radius-4xl);\n background-color: var(--lea-color-neutral-100);\n animation: skeleton-pulse 1.5s var(--lea-easing-standard) infinite;\n}\n\n@keyframes skeleton-pulse {\n 0%,\n 100% {\n opacity: 0.4;\n }\n\n 50% {\n opacity: 1;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Skeleton {\n animation: none;\n }\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport type { Ref } from \"react\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport styles from \"./skeleton.module.css\";\n\nexport type SkeletonProps = Omit<\n useRender.ComponentProps<\"div\">,\n \"ref\" | \"children\"\n> & {\n /** The rendered element: the `<div>`, or the element passed to `render`. */\n ref?: Ref<HTMLElement>;\n};\n\n/**\n * A grey block that pulses where content is on its way, as the website's\n * loading page. Size it to the content it stands for, with `style` or a class:\n *\n * ```tsx\n * <div role=\"status\" aria-label=\"Laster innhold\">\n * <Skeleton style={{ width: \"40%\", height: 80 }} />\n * <Skeleton style={{ height: 480 }} />\n * </div>\n * ```\n *\n * It is hidden from screen readers. Put the blocks in an element with\n * `role=\"status\"` and a name, so a screen reader hears that the page is\n * loading. Under reduced motion it stays still.\n */\nexport function Skeleton({ render, ref, className, ...rest }: SkeletonProps) {\n return useRender({\n render,\n ref,\n defaultTagName: \"div\",\n props: {\n \"aria-hidden\": true,\n ...rest,\n className: mergeClassName(styles.Skeleton, className)({}),\n },\n });\n}\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n *\n * The track, the min / max row and the helper text are styled in\n * slider-parts.module.css, which `Slider` shares.\n */\n\n.AmountSlider {\n display: flex;\n width: 100%;\n flex-direction: column;\n color: var(--lea-palette-text-base);\n font-size: var(--lea-text-body-md-font-size);\n font-weight: var(--lea-text-body-md-font-weight);\n line-height: var(--lea-text-body-md-line-height);\n letter-spacing: var(--lea-text-body-md-letter-spacing);\n}\n\n.Label {\n margin-block-end: calc(var(--lea-spacing) * 1.5);\n font-size: var(--lea-text-heading-md-font-size);\n font-weight: var(--lea-text-heading-md-font-weight);\n line-height: var(--lea-text-heading-md-line-height);\n letter-spacing: var(--lea-text-heading-md-letter-spacing);\n text-align: center;\n}\n\n/* The same box as the library's other fields: their height, radius and border. */\n.Input {\n width: 100%;\n /* 54px, the height of every other single-line field. */\n height: calc(var(--lea-spacing) * 7);\n box-sizing: border-box;\n padding-block: 0;\n padding-inline: calc(var(--lea-spacing) * 2);\n border: 1px solid transparent;\n border-radius: var(--lea-radius-xl);\n margin: 0;\n background-color: transparent;\n color: var(--lea-palette-primary-base);\n font-family: inherit;\n /* No text style has this size at medium weight, so the size alone. */\n font-size: var(--lea-font-step-8);\n font-variant-numeric: tabular-nums;\n font-weight: var(--lea-font-weight-medium);\n text-align: center;\n}\n\n/*\n * The 2px border of a state is drawn as an inset outline over the 1px one, so\n * the field never changes size between states.\n */\n@media (hover: hover) {\n .Input:hover:not(:focus, [data-disabled], [data-invalid]) {\n outline: 2px solid var(--lea-palette-border-strong);\n outline-offset: -2px;\n }\n}\n\n.Input:focus {\n outline: 2px solid var(--lea-palette-border-accent);\n outline-offset: -2px;\n}\n\n.Input:active {\n outline: 2px solid var(--lea-palette-border-accent);\n outline-offset: -2px;\n background-color: var(--lea-color-purple-50);\n}\n\n.Input[data-invalid] {\n outline: 2px solid var(--lea-palette-status-error-base);\n outline-offset: -2px;\n}\n\n.Input[data-disabled] {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n.Slider {\n display: flex;\n flex-direction: column;\n margin-block-start: var(--lea-spacing);\n}\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so these parts render correctly without a provider.\n *\n * `Slider`'s `color=\"contrast\"` sets `--slider-rail`, `--slider-fill`,\n * `--slider-thumb-ring`, `--slider-focus` and `--slider-secondary` on the\n * slider; left unset (`AmountSlider`, `color=\"primary\"`), the fallbacks are\n * the purple look.\n */\n\n.Control {\n --slider-thumb-size: calc(var(--lea-spacing) * 3);\n /* The same thickness as the LinearFormStepper bar. */\n --slider-track-size: calc(var(--lea-spacing) * 0.75);\n /*\n * Base UI centres the thumb on the value, so at min and max half of it\n * hangs past the control. The control is inset by that half to keep the\n * thumb inside the slider's own box.\n */\n --slider-inset: calc(var(--slider-thumb-size) / 2);\n --slider-thumb-hover: color-mix(\n in srgb,\n var(--lea-palette-primary-base) calc(var(--lea-opacity-hover) * 100%),\n var(--lea-palette-bg-base)\n );\n --slider-thumb-pressed: color-mix(\n in srgb,\n var(--lea-palette-primary-base) calc(var(--lea-opacity-pressed) * 100%),\n var(--lea-palette-bg-base)\n );\n\n position: relative;\n display: flex;\n /* Taller than the thumb, so the touch target is 44px. */\n height: calc(var(--lea-spacing) * 5.5);\n align-items: center;\n margin-inline: var(--slider-inset);\n cursor: pointer;\n touch-action: none;\n}\n\n/* Keeps the rounded ends of the track, which sit in the inset, clickable. */\n.Control::before {\n position: absolute;\n content: \"\";\n inset-block: 0;\n inset-inline: calc(var(--slider-inset) * -1);\n}\n\n.Control[data-disabled] {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n.Track {\n position: relative;\n width: 100%;\n height: var(--slider-track-size);\n}\n\n/*\n * The visible rail runs the full width of the slider, past the control.\n * purple-200 rather than purple-subtle, which is the same colour as the\n * bg-accent surface and would make the rail disappear on it.\n */\n.Track::before {\n position: absolute;\n border-radius: var(--lea-radius-full);\n background-color: var(--slider-rail, var(--lea-color-purple-200));\n content: \"\";\n inset-block: 0;\n inset-inline: calc(var(--slider-inset) * -1);\n}\n\n.Indicator {\n background-color: var(--slider-fill, var(--lea-palette-primary-base));\n}\n\n/* Fills the rounded start of the rail, which lies before the control. */\n.Indicator::before {\n position: absolute;\n width: var(--slider-inset);\n background-color: inherit;\n border-end-start-radius: var(--lea-radius-full);\n border-start-start-radius: var(--lea-radius-full);\n content: \"\";\n inset-block: 0;\n inset-inline-end: 100%;\n}\n\n.Thumb {\n width: var(--slider-thumb-size);\n height: var(--slider-thumb-size);\n box-sizing: border-box;\n border: 2px solid var(--slider-thumb-ring, var(--lea-palette-primary-base));\n border-radius: var(--lea-radius-full);\n background-color: var(--lea-palette-bg-base);\n transition: background-color var(--lea-duration-normal)\n var(--lea-easing-standard);\n}\n\n@media (hover: hover) {\n .Thumb:not([data-disabled]):hover {\n background-color: var(--slider-thumb-hover);\n }\n}\n\n.Thumb[data-dragging]:not([data-disabled]) {\n background-color: var(--slider-thumb-pressed);\n}\n\n/* Focus lands on the visually hidden range input inside the thumb. */\n.Thumb:has(:focus-visible) {\n outline: 2px solid var(--slider-focus, var(--lea-palette-text-base));\n outline-offset: 0.125rem;\n}\n\n/*\n * neutral-600 rather than text-subtle for the secondary text: a slider has no\n * background of its own, and text-subtle falls below 4.5:1 on the tinted\n * surfaces it can sit on.\n */\n.Range {\n display: flex;\n justify-content: space-between;\n gap: calc(var(--lea-spacing) * 2);\n color: var(--slider-secondary, var(--lea-color-neutral-600));\n font-size: var(--lea-text-body-sm-font-size);\n font-weight: var(--lea-text-body-sm-font-weight);\n line-height: var(--lea-text-body-sm-line-height);\n letter-spacing: var(--lea-text-body-sm-letter-spacing);\n font-variant-numeric: tabular-nums;\n}\n\n.HelperText {\n margin: 0;\n margin-block-start: var(--lea-spacing);\n color: var(--slider-secondary, var(--lea-color-neutral-600));\n font-size: var(--lea-text-body-sm-font-size);\n font-weight: var(--lea-text-body-sm-font-weight);\n line-height: var(--lea-text-body-sm-line-height);\n letter-spacing: var(--lea-text-body-sm-letter-spacing);\n}\n\n.HelperText[data-invalid] {\n color: var(--lea-palette-status-error-base);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Thumb {\n transition-duration: 0.01ms;\n }\n}\n","\"use client\";\n\nimport { Field } from \"@base-ui/react/field\";\nimport { Slider as BaseSlider } from \"@base-ui/react/slider\";\nimport { useMemo, type ReactNode } from \"react\";\n\nimport styles from \"./slider-parts.module.css\";\n\n/*\n * Internal parts shared by `Slider` and `AmountSlider`, so the track, the\n * min / max row and the helper text look and behave the same in both. Not\n * part of the public API, so this file isn't re-exported from `index.ts`.\n */\n\n/**\n * `render` for a `Field.Root` that is only there to hand a control its\n * validity state, which is how Base UI puts `aria-invalid` on the slider's\n * nested range input. It makes the field render no element of its own.\n */\nexport const renderWithoutElement: Field.Root.Props[\"render\"] = (props) => (\n <>{props.children}</>\n);\n\ntype SliderTrackProps = {\n \"aria-label\"?: string;\n \"aria-describedby\"?: string;\n};\n\n/**\n * The clickable control with its track, fill and thumb. The thumb is rendered\n * here so it can never be left out. Must sit inside a Base UI `Slider.Root`.\n */\nexport function SliderTrack({\n \"aria-label\": ariaLabel,\n \"aria-describedby\": ariaDescribedBy,\n}: SliderTrackProps) {\n return (\n <BaseSlider.Control className={styles.Control}>\n <BaseSlider.Track className={styles.Track}>\n <BaseSlider.Indicator className={styles.Indicator} />\n <BaseSlider.Thumb\n className={styles.Thumb}\n aria-label={ariaLabel}\n aria-describedby={ariaDescribedBy}\n />\n </BaseSlider.Track>\n </BaseSlider.Control>\n );\n}\n\ntype SliderRangeProps = {\n min: number;\n max: number;\n format?: Intl.NumberFormatOptions;\n locale?: Intl.LocalesArgument;\n};\n\n/** `min` and `max` under each end of the track, written like the value. */\nexport function SliderRange({ min, max, format, locale }: SliderRangeProps) {\n const formatter = useMemo(\n () => new Intl.NumberFormat(locale, format),\n [locale, format],\n );\n\n return (\n // The range input already exposes its bounds, so these are visual only.\n <div className={styles.Range} aria-hidden>\n <span>{formatter.format(min)}</span>\n <span>{formatter.format(max)}</span>\n </div>\n );\n}\n\ntype SliderHelperTextProps = {\n /** Id the controls point at with `aria-describedby`. */\n id: string;\n error: boolean;\n children: ReactNode;\n};\n\n/** Helper text below the slider. Announced as an alert while in error. */\nexport function SliderHelperText({\n id,\n error,\n children,\n}: SliderHelperTextProps) {\n return (\n <p\n id={id}\n className={styles.HelperText}\n data-invalid={error ? \"\" : undefined}\n role={error ? \"alert\" : undefined}\n >\n {children}\n </p>\n );\n}\n","\"use client\";\n\nimport { Field } from \"@base-ui/react/field\";\nimport { NumberField } from \"@base-ui/react/number-field\";\nimport { Slider as BaseSlider } from \"@base-ui/react/slider\";\nimport {\n useId,\n useState,\n type ComponentPropsWithRef,\n type ReactNode,\n} from \"react\";\n\nimport styles from \"./amount-slider.module.css\";\nimport {\n renderWithoutElement,\n SliderHelperText,\n SliderRange,\n SliderTrack,\n} from \"../slider-parts\";\n\nexport type AmountSliderProps = Omit<\n ComponentPropsWithRef<\"div\">,\n \"children\" | \"defaultValue\" | \"onChange\"\n> & {\n /**\n * The question above the amount, e.g. \"Hur mycket vill du låna?\". It labels\n * the amount field and names the slider, so it is required.\n */\n label: ReactNode;\n /** The lowest amount that can be chosen. */\n min: number;\n /** The highest amount that can be chosen. */\n max: number;\n /**\n * How far the slider, and the arrow keys in the amount field, move the\n * amount. A typed amount is kept as typed, also between two steps.\n * @default 1\n */\n step?: number;\n /** The amount. Use with `onValueChange` to control the component. */\n value?: number;\n /**\n * The amount when first rendered, for uncontrolled use.\n * @default min\n */\n defaultValue?: number;\n /**\n * Called with the new amount when the slider moves, and when an amount\n * within `min` and `max` is typed.\n */\n onValueChange?: (value: number) => void;\n /** How the amount, `min` and `max` are written, e.g. as a currency. */\n format?: Intl.NumberFormatOptions;\n /** The locale the amount is written and typed in. Defaults to the runtime locale. */\n locale?: Intl.LocalesArgument;\n /** Submits the amount under this name when inside a form. */\n name?: string;\n /** Disables both the amount field and the slider. */\n disabled?: boolean;\n /** Helper / error text shown below the slider and linked via `aria-describedby`. */\n helperText?: ReactNode;\n /** Puts the amount field, the slider and the helper text into the error state. */\n error?: boolean;\n};\n\n/**\n * An amount chosen by dragging a slider or by typing it, for questions like\n * \"how much do you want to borrow?\". The amount field and the slider share one\n * value: moving the slider rewrites the amount, and typing moves the thumb.\n *\n * The `label` is a real `<label>` for the amount field and also names the\n * slider, and the helper text describes both. A typed amount outside `min`\n * and `max` is brought back into range when the field loses focus, and an\n * emptied field goes back to the last amount.\n *\n * Use `Slider` instead when the value does not need to be typed.\n *\n * ```tsx\n * <AmountSlider\n * label=\"Hur mycket vill du låna?\"\n * min={20000}\n * max={600000}\n * step={5000}\n * defaultValue={200000}\n * locale=\"sv-SE\"\n * format={{ style: \"currency\", currency: \"SEK\", maximumFractionDigits: 0 }}\n * onValueChange={(amount) => setAmount(amount)}\n * />\n * ```\n */\nexport function AmountSlider({\n label,\n min,\n max,\n step = 1,\n value: valueProp,\n defaultValue,\n onValueChange,\n format,\n locale,\n name,\n disabled = false,\n helperText,\n error = false,\n className,\n ...rest\n}: AmountSliderProps) {\n const labelId = useId();\n const inputId = useId();\n const helperTextId = useId();\n const describedBy = helperText != null ? helperTextId : undefined;\n\n const [uncontrolledValue, setUncontrolledValue] = useState(\n defaultValue ?? min,\n );\n const value = valueProp ?? uncontrolledValue;\n\n // What the amount field holds while it differs from the amount: nothing, or\n // a number outside the range that hasn't been brought back in yet.\n // `undefined` means the field simply shows the amount.\n const [draft, setDraft] = useState<number | null>();\n\n const commit = (next: number) => {\n setDraft(undefined);\n if (valueProp === undefined) setUncontrolledValue(next);\n if (next !== value) onValueChange?.(next);\n };\n\n return (\n <div\n {...rest}\n className={[styles.AmountSlider, className].filter(Boolean).join(\" \")}\n >\n <label id={labelId} htmlFor={inputId} className={styles.Label}>\n {label}\n </label>\n {/*\n * The number field stays outside the slider's `Field.Root`: inside one,\n * Base UI gives the input the field's own id until after mount, which\n * leaves the label pointing at nothing on the server.\n */}\n <NumberField.Root\n id={inputId}\n name={name}\n value={draft !== undefined ? draft : value}\n // Base UI would otherwise clamp on every keystroke, sending the\n // slider to `min` on the first digit of a longer amount. The amount\n // is clamped here instead, once the field loses focus.\n allowOutOfRange\n onValueChange={(next) => {\n if (next != null && next >= min && next <= max) commit(next);\n else setDraft(next);\n }}\n onValueCommitted={(next) => {\n if (next == null) setDraft(undefined);\n else commit(Math.min(Math.max(next, min), max));\n }}\n min={min}\n max={max}\n step={step}\n format={format}\n locale={locale}\n disabled={disabled}\n >\n <NumberField.Input\n className={styles.Input}\n aria-describedby={describedBy}\n aria-invalid={error || undefined}\n data-invalid={error ? \"\" : undefined}\n />\n </NumberField.Root>\n <Field.Root invalid={error} render={renderWithoutElement}>\n <BaseSlider.Root\n value={value}\n onValueChange={commit}\n min={min}\n max={max}\n step={step}\n format={format}\n locale={locale}\n disabled={disabled}\n aria-labelledby={labelId}\n className={styles.Slider}\n >\n <SliderTrack aria-describedby={describedBy} />\n <SliderRange min={min} max={max} format={format} locale={locale} />\n </BaseSlider.Root>\n </Field.Root>\n {helperText != null && (\n <SliderHelperText id={helperTextId} error={error}>\n {helperText}\n </SliderHelperText>\n )}\n </div>\n );\n}\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n *\n * Shared by Autocomplete and EmailInput. The box, its border states, the\n * label and the text field come from input.module.css, and the list from\n * option-list.module.css; this file adds the buttons at the end of the field\n * and the chips of a multiple control.\n */\n\n/*\n * `.Field` in front of a selector lifts it over the rules of input.module.css\n * for the same element, whichever of the two stylesheets loads last.\n */\n.Field .Box {\n /* White, where Input shows the surface it sits on. As it always was. */\n background-color: var(--lea-palette-bg-base);\n}\n\n/* The field and its open list read as one shape: square where they meet. */\n.Box[data-popup-side=\"bottom\"] {\n border-end-end-radius: 0;\n border-end-start-radius: 0;\n}\n\n.Box[data-popup-side=\"top\"] {\n border-start-end-radius: 0;\n border-start-start-radius: 0;\n}\n\n/* The buttons follow the text, so it runs up to them. */\n.Field .Inner:not(:last-child) .Control {\n padding-inline-end: var(--lea-spacing-0-5);\n}\n\n/* A multiple control has a value when it has chips, whatever the input holds. */\n.Field .Box[data-filled] .Label {\n max-width: calc(\n (100% - var(--input-inset-start) - var(--input-padding-inline)) / 0.75\n );\n scale: 0.75;\n translate: 0 calc(var(--lea-spacing) * 0.75);\n}\n\n.Buttons {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n align-self: center;\n /* The arrow itself ends 14px from the edge, where the text of a field starts. */\n margin-inline-end: calc(var(--lea-spacing) * 1.25);\n}\n\n.Button {\n display: inline-flex;\n width: 1.75rem;\n height: 1.75rem;\n align-items: center;\n justify-content: center;\n padding: 0;\n border: 0;\n border-radius: var(--lea-radius-full);\n background: none;\n color: var(--lea-palette-text-base);\n cursor: pointer;\n font: inherit;\n}\n\n.Button:disabled,\n.Button[data-disabled] {\n color: var(--lea-palette-text-muted);\n cursor: not-allowed;\n}\n\n/* Out of the tab order, so only reachable by a script; shown all the same. */\n.Button:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n.Trigger svg {\n width: 1.25rem;\n height: 1.25rem;\n transition: rotate var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n.Trigger[data-popup-open] svg {\n rotate: 180deg;\n}\n\n.Clear svg {\n width: 1.125rem;\n height: 1.125rem;\n}\n\n/*\n * With a mouse, the clear button waits for the pointer or for focus, so a\n * form at rest is not a column of crosses. A touch screen has neither, and\n * shows it whenever there is something to clear.\n */\n@media (pointer: fine) {\n .Box:not(:hover, :focus-within) .Clear {\n visibility: hidden;\n }\n}\n\n.Box[data-disabled] .Clear {\n display: none;\n}\n\n/* The chips of a multiple control, and its input after them. */\n.Chips {\n display: flex;\n min-width: 0;\n flex: 1;\n flex-wrap: wrap;\n align-items: center;\n padding-inline: var(--input-inset-start) var(--lea-spacing-0-5);\n padding-block: var(--input-padding-top) calc(var(--lea-spacing) * 0.75);\n gap: var(--lea-spacing-0-5);\n}\n\n.ChipInput {\n /* Shares the last line of chips while 4rem is left on it, then takes a line of its own. */\n min-width: 4rem;\n flex: 1;\n padding: 0;\n padding-inline-start: var(--lea-spacing-0-5);\n border: 0;\n margin: 0;\n background: none;\n color: inherit;\n font: inherit;\n letter-spacing: inherit;\n outline: none;\n}\n\n.ChipInput:disabled {\n cursor: not-allowed;\n opacity: 1;\n /* Safari dims disabled text on top of the colour set here. */\n -webkit-text-fill-color: currentcolor;\n}\n\n.ChipInput::placeholder {\n color: var(--lea-palette-text-muted);\n opacity: 1;\n}\n\n/* While the label rests over the field the placeholder is hidden, so the two never overlap. */\n.Box[data-has-label]:not(:focus-within, [data-filled]) .ChipInput::placeholder {\n color: transparent;\n}\n\n/*\n * The list fades in, and goes at once. While a list is on its way out, Base UI\n * keeps showing the options of the text it was closed with; a key pressed in\n * that time brought the list back with those, not with what was typed.\n */\n.Popup[data-ending-style] {\n transition: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Trigger svg {\n transition-duration: 0.01ms;\n }\n}\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so these parts render correctly without a provider.\n *\n * The sizes and distances match the label and helper text of the fields that\n * are still built on MUI, so the two sit side by side in a form without a\n * visible seam.\n */\n\n.GroupLabel {\n display: inline-flex;\n width: fit-content;\n align-items: center;\n padding-block-end: var(--lea-spacing-1-5);\n margin: 0;\n color: var(--lea-palette-text-base);\n font-size: var(--lea-text-label-lg-font-size);\n font-weight: var(--lea-text-label-lg-font-weight);\n gap: var(--lea-spacing-1);\n letter-spacing: var(--lea-text-label-lg-letter-spacing);\n line-height: var(--lea-text-label-lg-line-height);\n}\n\n.GroupLabel[data-invalid] {\n color: var(--lea-palette-status-error-base);\n}\n\n.GroupLabel[data-disabled] {\n color: var(--lea-palette-text-muted);\n}\n\n.HelperText {\n /* 14px, where the text inside a field starts. */\n margin-inline: calc(var(--lea-spacing) * 1.75);\n margin-block: var(--lea-spacing-0-5) 0;\n /*\n * neutral-600 rather than text-subtle: these controls have no background of\n * their own, and text-subtle falls below 4.5:1 on the tinted surfaces they\n * can sit on.\n */\n color: var(--lea-color-neutral-600);\n font-size: var(--lea-text-body-sm-font-size);\n font-weight: var(--lea-text-body-sm-font-weight);\n letter-spacing: var(--lea-text-body-sm-letter-spacing);\n line-height: var(--lea-text-body-sm-line-height);\n}\n\n.HelperText[data-invalid] {\n color: var(--lea-palette-status-error-base);\n}\n\n/*\n * A value that is allowed but unusual. amber-700, not the warning base, which\n * is 2:1 on white: amber-700 is 5.9:1. It colours the text only; the field\n * keeps its border, because the value is valid.\n */\n.HelperText[data-warning] {\n color: var(--lea-color-amber-700);\n}\n","import { createTranslation } from \"../../i18n\";\n\n/**\n * Translated accessible name for the info trigger a control renders next to\n * its label when given a `tooltip`.\n */\nexport const useTooltipTriggerLabels = createTranslation({\n moreInformation: {\n no: \"Mer informasjon\",\n se: \"Mer information\",\n fi: \"Lisätietoja\",\n es: \"Más información\",\n },\n});\n","\"use client\";\n\nimport { InfoIcon } from \"lucide-react\";\nimport type { ReactNode } from \"react\";\n\nimport { Tooltip } from \"../data-display\";\nimport styles from \"./field-parts.module.css\";\nimport { useTooltipTriggerLabels } from \"./use-tooltip-trigger-labels\";\n\n/*\n * Internal parts shared by the form controls that no longer wrap MUI\n * (`Input`, `Checkbox`, the radio groups), so their label, asterisk and helper\n * text look and behave the same. Not part of the public API, so this file\n * isn't re-exported from `index.ts`.\n *\n * Each control generates its own ids with `useId` and hands them to these\n * parts, so every link (`aria-labelledby`, `aria-describedby`) is already in\n * the markup the server sends.\n */\n\n/** The ` *` after the label of a required control. Decorative: `required` is on the control. */\nexport const RequiredAsterisk = ({ className }: { className?: string }) => (\n <span aria-hidden className={className}>\n {\"\\u2009*\"}\n </span>\n);\n\ntype FieldHelperTextProps = {\n /** Id the control points at with `aria-describedby`. */\n id: string;\n error: boolean;\n /** The text is a warning: amber, and announced politely. */\n warning?: boolean;\n children: ReactNode;\n};\n\n/**\n * Helper text below a control. Announced as an alert while in error, and as a\n * status while it is a warning.\n */\nexport function FieldHelperText({\n id,\n error,\n warning = false,\n children,\n}: FieldHelperTextProps) {\n return (\n <p\n id={id}\n className={styles.HelperText}\n data-invalid={error ? \"\" : undefined}\n data-warning={warning && !error ? \"\" : undefined}\n role={error ? \"alert\" : warning ? \"status\" : undefined}\n >\n {children}\n </p>\n );\n}\n\n/**\n * What the line below a control shows: the warning while there is one,\n * otherwise the helper text. An error wins over a warning, so with `error`\n * the line is the helper text (where a form puts its error message).\n */\nexport function fieldMessage(\n helperText: ReactNode,\n warning: ReactNode,\n error: boolean,\n): { content: ReactNode; warning: boolean } {\n const showWarning =\n !error && warning != null && warning !== false && warning !== \"\";\n return { content: showWarning ? warning : helperText, warning: showWarning };\n}\n\ntype FieldGroupLabelProps = {\n /** Id the group points at with `aria-labelledby`. It sits on the text alone. */\n id: string;\n /**\n * Id of a single control this labels. The text is then a real `<label>`, so\n * a click on it moves focus to the control.\n */\n htmlFor?: string;\n /** Help text behind an info button next to the label. */\n tooltip?: ReactNode;\n required?: boolean;\n error?: boolean;\n disabled?: boolean;\n children: ReactNode;\n};\n\n/**\n * The label above a group of choices, or above a field whose label sits\n * outside it. The element is not a `<label>`: it names its control through\n * `aria-labelledby`, and a `<label>` around the info button would pass clicks\n * on the text to that button. With `htmlFor`, the text alone is a `<label>`.\n */\nexport function FieldGroupLabel({\n id,\n htmlFor,\n tooltip,\n required,\n error,\n disabled,\n children,\n}: FieldGroupLabelProps) {\n const labels = useTooltipTriggerLabels();\n const hasTooltip = tooltip != null && tooltip !== false;\n\n return (\n <div\n className={styles.GroupLabel}\n data-invalid={error ? \"\" : undefined}\n data-disabled={disabled ? \"\" : undefined}\n >\n {/* One part of the row, so the asterisk follows the text by its own\n thin space and stays on the last line when the text wraps. */}\n <span>\n {htmlFor != null ? (\n <label id={id} htmlFor={htmlFor}>\n {children}\n </label>\n ) : (\n <span id={id}>{children}</span>\n )}\n {required && <RequiredAsterisk />}\n </span>\n {hasTooltip && (\n <Tooltip\n title={tooltip}\n ariaLabel={labels.moreInformation}\n triggerIcon={<InfoIcon size={16} aria-hidden />}\n // The colour of the label, in every state of the button.\n style={{ color: \"inherit\" }}\n />\n )}\n </div>\n );\n}\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n *\n * The box, the floating label and the distances match the fields that are\n * still built on MUI (`Select`, `Autocomplete`, the date picker), so an\n * `Input` next to one of them lines up.\n */\n\n.Field {\n position: relative;\n display: flex;\n width: 100%;\n min-width: 0;\n flex-direction: column;\n}\n\n.Box {\n /* 54px, the height of every single-line field. */\n --input-height: calc(var(--lea-spacing) * 6.75);\n /* 14px, where the other fields start their text. */\n --input-padding-inline: calc(var(--lea-spacing) * 1.75);\n /* Room above the value for the floated label. */\n --input-padding-top: var(--lea-spacing-3);\n --input-border-color: var(--lea-color-neutral-300);\n --input-border-width: 1px;\n\n position: relative;\n display: flex;\n min-height: var(--input-height);\n box-sizing: border-box;\n border-radius: var(--lea-radius-xl);\n color: var(--lea-palette-text-base);\n cursor: text;\n font-size: var(--lea-text-body-md-font-size);\n font-weight: var(--lea-text-body-md-font-weight);\n letter-spacing: var(--lea-text-body-md-letter-spacing);\n line-height: var(--lea-text-body-md-line-height);\n}\n\n/*\n * The border is drawn over the box rather than on it, so going from 1px to\n * 2px in a state never moves what is inside.\n */\n.Box::after {\n position: absolute;\n border: var(--input-border-width) solid var(--input-border-color);\n border-radius: inherit;\n content: \"\";\n inset: 0;\n pointer-events: none;\n}\n\n@media (hover: hover) {\n .Box:hover:not(:focus-within, [data-disabled], [data-invalid]) {\n --input-border-color: var(--lea-palette-border-strong);\n --input-border-width: 2px;\n }\n}\n\n/* The border is the focus indicator: the input's own outline is taken off below. */\n.Box:focus-within {\n --input-border-color: var(--lea-palette-border-accent);\n --input-border-width: 2px;\n}\n\n.Box:active:not([data-disabled], [data-invalid]) {\n --input-border-color: var(--lea-palette-border-accent);\n --input-border-width: 2px;\n\n background-color: var(--lea-palette-bg-subtle);\n}\n\n/* After focus and active, so an invalid field stays red in every state. */\n.Box[data-invalid] {\n --input-border-color: var(--lea-palette-status-error-base);\n --input-border-width: 2px;\n}\n\n.Box[data-disabled] {\n color: var(--lea-palette-text-muted);\n cursor: not-allowed;\n}\n\n/* The label and the control share this cell, so the label follows an adornment of any width. */\n.Inner {\n --input-inset-start: var(--input-padding-inline);\n\n position: relative;\n display: flex;\n min-width: 0;\n flex: 1;\n}\n\n.Adornment[data-position=\"start\"] + .Inner {\n --input-inset-start: 0px;\n}\n\n.Control {\n width: 100%;\n min-width: 0;\n box-sizing: border-box;\n padding-block: var(--input-padding-top) calc(var(--lea-spacing) * 0.75);\n padding-inline: var(--input-inset-start) var(--input-padding-inline);\n border: 0;\n border-radius: inherit;\n margin: 0;\n background: none;\n color: inherit;\n font: inherit;\n letter-spacing: inherit;\n outline: none;\n}\n\n.Control:disabled {\n cursor: not-allowed;\n opacity: 1;\n /* Safari dims disabled text on top of the colour set here. */\n -webkit-text-fill-color: currentcolor;\n}\n\ntextarea.Control {\n display: block;\n resize: none;\n}\n\n/* With the label above the field, nothing floats, so the value is centred. */\n.Box[data-label-outside] .Control {\n padding-block: calc(\n (var(--input-height) - var(--lea-text-body-md-line-height) * 1em) / 2\n );\n}\n\n.Control::placeholder {\n color: var(--lea-palette-text-muted);\n opacity: 1;\n}\n\n/* While the label rests over the field the placeholder is hidden, so the two never overlap. */\n.Box[data-has-label]:not(:focus-within) .Control::placeholder {\n color: transparent;\n}\n\n/* Keeps the browser's autofill tint off the field. */\n.Control:-webkit-autofill {\n box-shadow: 0 0 0 100px var(--lea-palette-bg-base) inset;\n caret-color: var(--lea-palette-text-base);\n -webkit-text-fill-color: var(--lea-palette-text-base);\n}\n\n.Label {\n position: absolute;\n z-index: 1;\n overflow: hidden;\n max-width: calc(\n 100% - var(--input-inset-start) - var(--input-padding-inline)\n );\n /*\n * neutral-600 rather than text-subtle: a field has no background of its\n * own, and text-subtle falls below 4.5:1 on the tinted surfaces it can sit on.\n */\n color: var(--lea-color-neutral-600);\n inset-block-start: 0;\n inset-inline-start: var(--input-inset-start);\n pointer-events: none;\n scale: 1;\n text-overflow: ellipsis;\n transform-origin: top left;\n /* Resting, the label is centred in a single-line field, where the value will be. */\n translate: 0\n calc((var(--input-height) - var(--lea-text-body-md-line-height) * 1em) / 2);\n transition: color var(--lea-duration-normal) var(--lea-easing-standard),\n scale var(--lea-duration-normal) var(--lea-easing-standard),\n translate var(--lea-duration-normal) var(--lea-easing-standard);\n white-space: nowrap;\n}\n\n/*\n * The label floats to the top of the field once there is something to make\n * room for: focus, or a value. `:placeholder-shown` is how CSS sees an empty\n * control, which is why the control always has a placeholder, if only a space.\n */\n.Box:focus-within .Label,\n.Inner:has(.Control:not(:placeholder-shown)) .Label {\n max-width: calc(\n (100% - var(--input-inset-start) - var(--input-padding-inline)) / 0.75\n );\n scale: 0.75;\n translate: 0 calc(var(--lea-spacing) * 0.75);\n}\n\n/* Separate rules: a browser that does not know a selector drops the whole list. */\n.Inner:has(.Control:-webkit-autofill) .Label {\n max-width: calc(\n (100% - var(--input-inset-start) - var(--input-padding-inline)) / 0.75\n );\n scale: 0.75;\n translate: 0 calc(var(--lea-spacing) * 0.75);\n}\n\n.Inner:has(.Control:autofill) .Label {\n max-width: calc(\n (100% - var(--input-inset-start) - var(--input-padding-inline)) / 0.75\n );\n scale: 0.75;\n translate: 0 calc(var(--lea-spacing) * 0.75);\n}\n\n.Box:focus-within .Label {\n color: var(--lea-palette-text-accent);\n}\n\n.Box[data-invalid] .Label {\n color: var(--lea-palette-status-error-base);\n}\n\n.Box[data-disabled] .Label {\n color: var(--lea-palette-text-muted);\n}\n\n.Adornment {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n align-self: center;\n color: var(--lea-palette-text-subtle);\n white-space: nowrap;\n}\n\n.Adornment[data-position=\"start\"] {\n /*\n * The value sits low in the field to clear the floated label, so a centred\n * icon reads as too high next to it. The margin moves it down by half its\n * size, onto the text line.\n */\n margin-block-start: var(--lea-spacing-0-5);\n margin-inline: var(--lea-spacing-1-5) var(--lea-spacing-1);\n}\n\n/* With the label above the field the value is centred, so the icon is too. */\n.Box[data-label-outside] .Adornment[data-position=\"start\"] {\n margin-block-start: 0;\n}\n\n.Adornment[data-position=\"end\"] {\n margin-inline: var(--lea-spacing-1) var(--input-padding-inline);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Label {\n transition-duration: 0.01ms;\n }\n}\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n *\n * The list of options in a popup, shared by Select, MultiSelect, Autocomplete,\n * EmailInput and the country list of PhoneNumberInput.\n */\n\n/* Level with Dialog and Drawer (1300), and later in the page, so a list\n inside one of them opens above it. */\n.Positioner {\n z-index: 1300;\n outline: none;\n}\n\n/* As wide as the field: a long option wraps rather than widening the list. */\n.Popup {\n overflow: hidden auto;\n width: var(--anchor-width);\n max-width: var(--available-width);\n /*\n * 200px: five options, then it scrolls. Less when the window is short.\n * Base UI sets --available-height once it has placed the popup, and places\n * it by its size before that; the fallback keeps that first size at 200px,\n * so a long list is not sent above the field for want of room it has.\n */\n max-height: min(12.5rem, var(--available-height, 100vh));\n box-sizing: border-box;\n border: 1px solid var(--lea-palette-border-accent);\n border-radius: 0 0 var(--lea-radius-xl) var(--lea-radius-xl);\n background-color: var(--lea-palette-bg-base);\n box-shadow: var(--lea-shadow-lg);\n color: var(--lea-palette-text-base);\n outline: none;\n transition: opacity var(--lea-duration-fast) var(--lea-easing-standard);\n}\n\n.Popup[data-side=\"top\"] {\n border-radius: var(--lea-radius-xl) var(--lea-radius-xl) 0 0;\n}\n\n.Popup[data-starting-style],\n.Popup[data-ending-style] {\n opacity: 0;\n}\n\n.List {\n display: flex;\n flex-direction: column;\n padding: var(--lea-spacing-0-5);\n /* A hair between options, so two marked neighbours do not merge. */\n gap: calc(var(--lea-spacing) * 0.25);\n outline: none;\n}\n\n/* A list to search in can come up empty; `.Empty` then stands in for it. */\n.List[data-empty] {\n display: none;\n}\n\n.Item {\n --option-bg: transparent;\n\n display: flex;\n align-items: center;\n padding-block: var(--lea-spacing-1);\n padding-inline: var(--lea-spacing-2);\n border-radius: var(--lea-radius-md);\n background-color: var(--option-bg);\n cursor: pointer;\n font-size: var(--lea-text-body-md-font-size);\n font-weight: var(--lea-text-body-md-font-weight);\n gap: var(--lea-spacing-1);\n letter-spacing: var(--lea-text-body-md-letter-spacing);\n line-height: var(--lea-text-body-md-line-height);\n outline: none;\n user-select: none;\n}\n\n.ItemText {\n min-width: 0;\n flex: 1;\n /* A word longer than the list breaks instead of being cut off. */\n overflow-wrap: break-word;\n}\n\n@media (hover: hover) {\n .Item:hover:not([data-disabled]) {\n --option-bg: var(--lea-palette-bg-subtle);\n }\n}\n\n/* After hover, so the chosen option keeps its fill under the pointer. */\n.Item[data-selected] {\n --option-bg: var(--lea-palette-bg-accent);\n}\n\n/*\n * The option the arrow keys are on: a shade over whatever fill it has. In a\n * select Base UI moves focus to that option, so `:focus-visible` shows it for\n * the keyboard and leaves the mouse its hover. In a combobox focus stays in\n * the text field and Base UI marks the option `data-highlighted` instead; the\n * popup carries `data-keyboard` while the keyboard is what is in use.\n */\n.Item:focus-visible,\n[data-keyboard] .Item[data-highlighted] {\n background-color: color-mix(\n in srgb,\n var(--lea-palette-text-base) calc(var(--lea-opacity-pressed) * 100%),\n var(--option-bg)\n );\n}\n\n.Item[data-disabled] {\n cursor: not-allowed;\n opacity: 0.38;\n}\n\n/* \"No options\" and \"Loading…\", where the options would be. */\n.Empty:not(:empty) {\n padding-block: calc(var(--lea-spacing) * 1.75);\n padding-inline: var(--lea-spacing-2);\n color: var(--lea-palette-text-subtle);\n font-size: var(--lea-text-body-md-font-size);\n font-weight: var(--lea-text-body-md-font-weight);\n letter-spacing: var(--lea-text-body-md-letter-spacing);\n line-height: var(--lea-text-body-md-line-height);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Popup {\n transition-duration: 0.01ms;\n }\n}\n","import { createTranslation } from \"../../i18n\";\n\n/**\n * Translated texts for the controls with a list to search in (`Autocomplete`\n * and `EmailInput`): what the list says when it has nothing to show, and the\n * names of the open and clear buttons.\n */\nexport const useAutocompleteLabels = createTranslation({\n noOptions: {\n no: \"Ingen alternativer\",\n se: \"Inga alternativ\",\n fi: \"Ei vaihtoehtoja\",\n es: \"Sin opciones\",\n },\n loading: {\n no: \"Laster…\",\n se: \"Laddar…\",\n fi: \"Ladataan…\",\n es: \"Cargando…\",\n },\n open: { no: \"Åpne\", se: \"Öppna\", fi: \"Avaa\", es: \"Abrir\" },\n close: { no: \"Lukk\", se: \"Stäng\", fi: \"Sulje\", es: \"Cerrar\" },\n clear: { no: \"Tøm\", se: \"Rensa\", fi: \"Tyhjennä\", es: \"Borrar\" },\n});\n","import type { FocusEvent, RefObject } from \"react\";\n\n/**\n * `onBlur` for a field that is made of several elements, some of them in a\n * popup: it is called once focus has left them all, not when it moves from\n * one to another. Put the returned handler on each element in `refs`.\n */\nexport function useBlurOutside(\n onBlur: ((event: FocusEvent<HTMLElement>) => void) | undefined,\n refs: readonly RefObject<HTMLElement | null>[],\n) {\n return (event: FocusEvent<HTMLElement>) => {\n if (!onBlur) return;\n // Focus is on its way somewhere; decide when it has arrived.\n window.setTimeout(() => {\n const active = document.activeElement;\n if (refs.some((ref) => ref.current?.contains(active))) return;\n onBlur(event);\n });\n };\n}\n","\"use client\";\n\nimport { Combobox } from \"@base-ui/react/combobox\";\nimport { ChevronDown, X } from \"lucide-react\";\nimport {\n useId,\n useRef,\n useState,\n type CSSProperties,\n type FocusEvent,\n type InputHTMLAttributes,\n type ReactNode,\n type Ref,\n} from \"react\";\n\nimport styles from \"./combo-field.module.css\";\nimport { FieldHelperText, fieldMessage, RequiredAsterisk } from \"./field-parts\";\nimport inputStyles from \"./input.module.css\";\nimport listStyles from \"./option-list.module.css\";\nimport { useAutocompleteLabels } from \"./use-autocomplete-labels\";\nimport { useBlurOutside } from \"./use-blur-outside\";\n\n/*\n * Internal. The field that `Autocomplete` and `EmailInput` share: the box and\n * the floating label of `Input` around a text field with the `combobox` role,\n * the clear and open buttons, and the list in a popup. Not part of the public\n * API, so this file isn't re-exported from `index.ts`.\n *\n * The parts are Base UI's combobox parts. Which root they sit in is up to the\n * caller (`Combobox.Root` to choose among options, `Autocomplete.Root` for\n * free text with suggestions), so the caller renders it through `renderRoot`.\n */\n\n/** The props `Autocomplete` and `EmailInput` have in common. */\nexport type ComboFieldBaseProps = {\n /** Floating label, linked to the input for you. */\n label?: ReactNode;\n /** Helper / error text shown below the control and linked via `aria-describedby`. */\n helperText?: ReactNode;\n /** Puts the control, its label and its helper text into the error state. */\n error?: boolean;\n /**\n * A message for a value that is allowed but unusual, such as an unusually\n * high amount. Shown in amber in place of the helper text, without the\n * error state: the field is not marked invalid. `error` wins when both are set.\n */\n warning?: ReactNode;\n /** Marks the control as required, with an asterisk after the label. */\n required?: boolean;\n disabled?: boolean;\n /**\n * Text shown while the input is empty. With a `label` it appears once the\n * label has floated up, on focus.\n */\n placeholder?: string;\n /** Called when focus leaves the control, its list included. */\n onBlur?: (event: FocusEvent<HTMLElement>) => void;\n /** Submits the value under this name in a native form. */\n name?: string;\n /** Id of the text `<input>`. */\n id?: string;\n /** Names the control when there is no visible `label`. */\n \"aria-label\"?: string;\n \"aria-labelledby\"?: string;\n \"aria-describedby\"?: string;\n /** Class on the outermost element, for layout. */\n className?: string;\n /** Inline style on the outermost element, for layout. */\n style?: CSSProperties;\n /** The text `<input>`. */\n ref?: Ref<HTMLInputElement>;\n};\n\ntype ComboFieldProps = Omit<ComboFieldBaseProps, \"name\"> & {\n /** Wraps the field and its popup in the Base UI root that drives them. */\n renderRoot: (children: ReactNode) => ReactNode;\n /** More attributes for the `<input>`: `type`, `name`, `autoComplete`, … */\n inputProps?: InputHTMLAttributes<HTMLInputElement>;\n /**\n * The chosen values of a multiple control, as `Combobox.Chip`s. They share\n * the field with the input and wrap onto new lines.\n */\n chips?: ReactNode;\n /** Whether a multiple control has values, which is what floats its label. */\n filled?: boolean;\n /** Shows the button that empties the control, while there is something to empty. */\n clearable?: boolean;\n /** Shows the arrow button that opens and closes the list. */\n trigger?: boolean;\n /**\n * Selects the text on the first click into the input, so that typing\n * replaces it. For a control whose text is the label of the chosen option.\n */\n selectTextOnClick?: boolean;\n /** The content of the popup: the list, and what stands in for it when empty. */\n children: ReactNode;\n /** More below the helper text, inside the outermost element. */\n afterHelperText?: ReactNode;\n};\n\nconst join = (...parts: (string | false | null | undefined)[]) =>\n parts.filter(Boolean).join(\" \") || undefined;\n\nexport function ComboField({\n label,\n helperText,\n error = false,\n warning,\n required = false,\n disabled = false,\n placeholder,\n onBlur,\n id,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n \"aria-describedby\": ariaDescribedBy,\n className,\n style,\n ref,\n renderRoot,\n inputProps,\n chips,\n filled = false,\n clearable = false,\n trigger = false,\n selectTextOnClick = false,\n children,\n afterHelperText,\n}: ComboFieldProps) {\n const labels = useAutocompleteLabels();\n const generatedId = useId();\n const inputId = id ?? generatedId;\n const labelId = useId();\n const helperTextId = useId();\n const hasLabel = label != null;\n const message = fieldMessage(helperText, warning, error);\n const hasHelperText = message.content != null;\n const multiple = chips !== undefined;\n\n const boxRef = useRef<HTMLDivElement>(null);\n const popupRef = useRef<HTMLDivElement>(null);\n const handleBlur = useBlurOutside(onBlur, [boxRef, popupRef]);\n\n // Focus stays in the input while the arrow keys move through the list, so\n // `:focus-visible` cannot tell which of the two is in use. This does: the\n // option the keys are on is only marked once a key has been pressed.\n const [keyboard, setKeyboard] = useState(false);\n\n // True from the moment the input takes focus until the click that gave it\n // focus has been handled.\n const firstClick = useRef(false);\n\n const input = (\n <Combobox.Input\n {...inputProps}\n ref={ref}\n id={inputId}\n disabled={disabled}\n required={required}\n // Never empty: `:placeholder-shown` is how the stylesheet tells an\n // empty input from a filled one, and it needs a placeholder to work.\n placeholder={placeholder || \" \"}\n aria-label={ariaLabel}\n // Base UI hides everything but the input and the list from assistive\n // tech while the list is open, the label included. A label that is\n // referred to by id still names the input then.\n aria-labelledby={ariaLabelledBy ?? (hasLabel ? labelId : undefined)}\n aria-invalid={error || undefined}\n aria-describedby={join(hasHelperText && helperTextId, ariaDescribedBy)}\n className={\n multiple ? styles.ChipInput : join(inputStyles.Control, styles.Control)\n }\n onFocus={() => {\n firstClick.current = true;\n }}\n onClick={(event) => {\n const element = event.currentTarget;\n if (\n selectTextOnClick &&\n firstClick.current &&\n element.selectionStart === element.selectionEnd\n )\n element.select();\n firstClick.current = false;\n }}\n onKeyDown={(event) => {\n // With the list closed, Base UI empties the control on Escape. Left\n // alone, the key keeps its meaning for what is around the control,\n // such as closing a dialog.\n if (\n event.key === \"Escape\" &&\n event.currentTarget.getAttribute(\"aria-expanded\") !== \"true\"\n )\n event.preventBaseUIHandler();\n }}\n />\n );\n\n return (\n <div\n className={join(inputStyles.Field, styles.Field, className)}\n style={style}\n >\n {renderRoot(\n <>\n <Combobox.InputGroup\n ref={boxRef}\n className={join(inputStyles.Box, styles.Box)}\n data-invalid={error ? \"\" : undefined}\n data-has-label={hasLabel ? \"\" : undefined}\n data-filled={filled ? \"\" : undefined}\n onBlur={handleBlur}\n onKeyDownCapture={() => setKeyboard(true)}\n onPointerDownCapture={() => setKeyboard(false)}\n >\n <div className={join(inputStyles.Inner, styles.Inner)}>\n {hasLabel && (\n <label\n id={labelId}\n htmlFor={inputId}\n className={join(inputStyles.Label, styles.Label)}\n >\n {label}\n {required && <RequiredAsterisk />}\n </label>\n )}\n {multiple ? (\n <Combobox.Chips className={styles.Chips}>\n {chips}\n {input}\n </Combobox.Chips>\n ) : (\n input\n )}\n </div>\n {(clearable || trigger) && (\n <div className={styles.Buttons}>\n {clearable && (\n <Combobox.Clear\n className={join(styles.Button, styles.Clear)}\n aria-label={labels.clear}\n // Out of the tab order, as the arrow is: the keyboard\n // empties the input by deleting its text.\n tabIndex={-1}\n >\n <X aria-hidden />\n </Combobox.Clear>\n )}\n {trigger && (\n <Combobox.Trigger\n className={join(styles.Button, styles.Trigger)}\n tabIndex={-1}\n // Until the input has mounted, Base UI takes this button\n // for the combobox itself and says so in the markup the\n // server sends. It is the arrow next to one.\n render={(props, state) => (\n <button\n {...props}\n role={undefined}\n aria-haspopup=\"listbox\"\n aria-required={undefined}\n aria-label={state.open ? labels.close : labels.open}\n />\n )}\n >\n <ChevronDown aria-hidden />\n </Combobox.Trigger>\n )}\n </div>\n )}\n </Combobox.InputGroup>\n <Combobox.Portal>\n <Combobox.Positioner\n anchor={boxRef}\n side=\"bottom\"\n align=\"start\"\n sideOffset={0}\n className={listStyles.Positioner}\n >\n <Combobox.Popup\n ref={popupRef}\n className={join(listStyles.Popup, styles.Popup)}\n data-keyboard={keyboard ? \"\" : undefined}\n onBlur={handleBlur}\n onPointerDownCapture={() => setKeyboard(false)}\n >\n {children}\n </Combobox.Popup>\n </Combobox.Positioner>\n </Combobox.Portal>\n </>,\n )}\n {hasHelperText && (\n <FieldHelperText\n id={helperTextId}\n error={error}\n warning={message.warning}\n >\n {message.content}\n </FieldHelperText>\n )}\n {afterHelperText}\n </div>\n );\n}\n","\"use client\";\n\nimport { Combobox } from \"@base-ui/react/combobox\";\nimport { CircleX } from \"lucide-react\";\nimport { useState, type ReactElement, type ReactNode } from \"react\";\n\nimport chipStyles from \"../../data-display/chip/chip.module.css\";\nimport { useChipLabels } from \"../../data-display/chip/use-chip-labels\";\nimport { ComboField, type ComboFieldBaseProps } from \"../combo-field\";\nimport listStyles from \"../option-list.module.css\";\nimport type { SelectOption } from \"../select-field\";\nimport { useAutocompleteLabels } from \"../use-autocomplete-labels\";\n\ntype AutocompleteBaseProps<Value extends string> = ComboFieldBaseProps & {\n /** The choices. Typing filters them by their label. */\n options: readonly SelectOption<Value>[];\n /** Shown in the list when nothing matches. Translated by default. */\n noOptionsText?: ReactNode;\n /**\n * Whether the options are on their way. While there are none to show, the\n * list says so instead of \"no options\".\n */\n loading?: boolean;\n};\n\ntype SingleValueProps<Value extends string> = {\n /** Choose several values, shown as chips. */\n multiple?: false;\n /** The chosen value (controlled). `\"\"` means nothing is chosen. */\n value?: Value | \"\";\n /** The chosen value when first rendered, for uncontrolled use. */\n defaultValue?: Value | \"\";\n /** Called with the chosen value, or `\"\"` when the control is emptied. */\n onValueChange?: (value: Value | \"\") => void;\n};\n\ntype MultipleValueProps<Value extends string> = {\n /** Choose several values, shown as chips. */\n multiple: true;\n /** The chosen values (controlled). */\n value?: Value[];\n /** The chosen values when first rendered, for uncontrolled use. */\n defaultValue?: Value[];\n /** Called with the next array of chosen values. */\n onValueChange?: (value: Value[]) => void;\n /** Fewest values that must stay chosen. @default 1 */\n min?: number;\n /** Most values that can be chosen. Unlimited when omitted. */\n max?: number;\n};\n\ntype AutocompleteSingleProps<Value extends string = string> =\n AutocompleteBaseProps<Value> & SingleValueProps<Value>;\n\ntype AutocompleteMultipleProps<Value extends string = string> =\n AutocompleteBaseProps<Value> & MultipleValueProps<Value>;\n\nexport type AutocompleteProps<Value extends string = string> =\n | AutocompleteSingleProps<Value>\n | AutocompleteMultipleProps<Value>;\n\nconst sameOption = (a: SelectOption, b: SelectOption) => a.value === b.value;\n\ntype OptionListProps = {\n noOptionsText?: ReactNode;\n loading?: boolean;\n /** Options that cannot be picked right now, on top of their own `disabled`. */\n isDisabled?: (option: SelectOption) => boolean;\n};\n\n/** The content of the popup: the options that match, or a line of text when none do. */\nfunction OptionList({ noOptionsText, loading, isDisabled }: OptionListProps) {\n const labels = useAutocompleteLabels();\n\n return (\n <>\n <Combobox.Empty className={listStyles.Empty}>\n {loading ? labels.loading : (noOptionsText ?? labels.noOptions)}\n </Combobox.Empty>\n <Combobox.List className={listStyles.List}>\n {(option: SelectOption) => (\n <Combobox.Item\n key={option.value}\n value={option}\n disabled={option.disabled || isDisabled?.(option)}\n className={listStyles.Item}\n >\n <span className={listStyles.ItemText}>{option.label}</span>\n </Combobox.Item>\n )}\n </Combobox.List>\n </>\n );\n}\n\nfunction SingleAutocomplete<Value extends string>({\n options,\n value,\n defaultValue = \"\",\n onValueChange,\n noOptionsText,\n loading,\n name,\n ...field\n}: AutocompleteSingleProps<Value>) {\n const isControlled = value !== undefined;\n const [internal, setInternal] = useState<Value | \"\">(defaultValue);\n const current = isControlled ? value : internal;\n\n const chosen = options.find((option) => option.value === current) ?? null;\n\n return (\n <ComboField\n {...field}\n clearable\n trigger\n selectTextOnClick\n renderRoot={(children) => (\n <Combobox.Root<SelectOption<Value>>\n items={options}\n value={chosen}\n onValueChange={(next) => {\n const nextValue = next?.value ?? \"\";\n if (nextValue === current) return;\n if (!isControlled) setInternal(nextValue);\n onValueChange?.(nextValue);\n }}\n isItemEqualToValue={sameOption}\n name={name}\n disabled={field.disabled}\n required={field.required}\n // The keyboard and the mouse each mark their own option, as in\n // `Select`: hovering does not move the option the arrow keys are on.\n highlightItemOnHover={false}\n >\n {children}\n </Combobox.Root>\n )}\n >\n <OptionList noOptionsText={noOptionsText} loading={loading} />\n </ComboField>\n );\n}\n\nfunction MultipleAutocomplete<Value extends string>({\n options,\n value,\n defaultValue = [],\n onValueChange,\n min = 1,\n max,\n noOptionsText,\n loading,\n name,\n multiple: _multiple,\n ...field\n}: AutocompleteMultipleProps<Value>) {\n const chipLabels = useChipLabels();\n const isControlled = value !== undefined;\n const [internal, setInternal] = useState<Value[]>(defaultValue);\n const selected = isControlled ? value : internal;\n // What is typed to filter the options. Held here to be emptied after a choice.\n const [query, setQuery] = useState(\"\");\n\n // In the order chosen, which is the order of the chips.\n const selectedOptions = selected\n .map((item) => options.find((option) => option.value === item))\n .filter((option) => option !== undefined);\n\n const atMax = max !== undefined && selected.length >= max;\n const atMin = selected.length <= min;\n const canRemove = !field.disabled && !atMin;\n\n return (\n <ComboField\n {...field}\n // Emptying the control would go below `min`, so the button is only\n // there when nothing has to stay chosen.\n clearable={min <= 0}\n trigger\n filled={selectedOptions.length > 0}\n chips={selectedOptions.map((option) => (\n <Combobox.Chip\n key={option.value}\n className={chipStyles.Chip}\n data-color=\"primary\"\n data-variant=\"filled\"\n data-size=\"sm\"\n >\n <span className={chipStyles.Label}>{option.label}</span>\n {canRemove && (\n <Combobox.ChipRemove\n className={chipStyles.Delete}\n aria-label={`${chipLabels.remove} ${option.label}`}\n >\n <CircleX aria-hidden />\n </Combobox.ChipRemove>\n )}\n </Combobox.Chip>\n ))}\n renderRoot={(children) => (\n <Combobox.Root<SelectOption<Value>, true>\n multiple\n items={options}\n value={selectedOptions}\n inputValue={query}\n onInputValueChange={setQuery}\n // A choice leaves the list open, to pick several in a row, and\n // empties the text that was typed to find it. Base UI closes the\n // list instead when there was such text, and a key pressed while it\n // closes brings it back with the options of the old text.\n onOpenChange={(open, details) => {\n if (!open && details.reason === \"item-press\") details.cancel();\n }}\n onValueChange={(next, details) => {\n if (details.reason === \"item-press\") setQuery(\"\");\n const nextValues = next.map((option) => option.value);\n // The options that would break a bound are disabled; this also\n // covers Backspace in the input, which removes the last chip.\n if (nextValues.length < min && nextValues.length < selected.length)\n return;\n if (\n max !== undefined &&\n nextValues.length > max &&\n nextValues.length > selected.length\n )\n return;\n if (!isControlled) setInternal(nextValues);\n onValueChange?.(nextValues);\n }}\n isItemEqualToValue={sameOption}\n name={name}\n disabled={field.disabled}\n required={field.required}\n highlightItemOnHover={false}\n >\n {children}\n </Combobox.Root>\n )}\n >\n <OptionList\n noOptionsText={noOptionsText}\n loading={loading}\n isDisabled={(option) => {\n const isSelected = selected.includes(option.value as Value);\n return (atMax && !isSelected) || (atMin && isSelected);\n }}\n />\n </ComboField>\n );\n}\n\n/**\n * A select to search in, on Base UI's `Combobox`, in the same box as `Input`.\n * Typing filters the options. The label, the helper text and the ids that\n * link them to the input are all rendered and wired for you.\n *\n * ```tsx\n * <Autocomplete\n * label=\"Land\"\n * options={[\n * { value: \"no\", label: \"Norge\" },\n * { value: \"se\", label: \"Sverige\" },\n * ]}\n * value={country}\n * onValueChange={setCountry}\n * />\n * ```\n *\n * Values are strings, typed from the options as in `Select`, and `\"\"` means\n * nothing is chosen. Pass `multiple` to choose several: they show as chips\n * inside the field, bounded by `min` (default 1) and `max`.\n *\n * For an email address with suggested domains, use `EmailInput`.\n */\n// A signature for each mode ahead of the one that takes either: TypeScript\n// then reads the type of the values from `options`, `value` and\n// `onValueChange` together, as it does for `Select` and `MultiSelect`. The\n// last one is for a wrapper that passes on props typed `AutocompleteProps`.\nexport function Autocomplete<Value extends string = string>(\n props: AutocompleteMultipleProps<Value>,\n): ReactElement;\nexport function Autocomplete<Value extends string = string>(\n props: AutocompleteSingleProps<Value>,\n): ReactElement;\nexport function Autocomplete<Value extends string = string>(\n props: AutocompleteProps<Value>,\n): ReactElement;\nexport function Autocomplete<Value extends string = string>(\n props: AutocompleteProps<Value>,\n) {\n return props.multiple ? (\n <MultipleAutocomplete {...props} />\n ) : (\n <SingleAutocomplete {...props} />\n );\n}\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n */\n\n.Checkbox {\n display: flex;\n width: 100%;\n flex-direction: column;\n}\n\n.Box {\n display: inline-flex;\n width: 1.3rem;\n height: 1.3rem;\n box-sizing: border-box;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n padding: 0;\n border: 2px solid var(--lea-palette-border-base);\n margin: 0;\n border-radius: var(--lea-radius-sm);\n background-color: var(--lea-palette-bg-base);\n /* The colour of the tick. */\n color: var(--lea-palette-primary-contrast);\n cursor: pointer;\n transition: background-color var(--lea-duration-normal)\n var(--lea-easing-standard),\n border-color var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n@media (hover: hover) {\n .Box:hover:not([data-checked], [data-disabled]) {\n border-color: var(--lea-palette-border-strong);\n }\n}\n\n.Box[data-checked] {\n border-color: var(--lea-palette-primary-base);\n background-color: var(--lea-palette-primary-base);\n}\n\n.Box:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n.Box[data-disabled] {\n cursor: not-allowed;\n opacity: 0.7;\n}\n\n.Tick {\n display: block;\n width: 100%;\n height: 100%;\n}\n\n/* The tick starts undrawn: the gap in the dash covers the whole path. */\n.Tick path {\n stroke-dasharray: 1;\n stroke-dashoffset: 1;\n transition: stroke-dashoffset var(--lea-duration-emphasis)\n var(--lea-easing-spring);\n}\n\n.Box[data-checked] .Tick path {\n stroke-dashoffset: 0;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Box,\n .Tick path {\n transition-duration: 0.01ms;\n }\n}\n","import styles from \"./checkbox.module.css\";\n\n/** The tick. It is drawn in by a transition when its box becomes checked. */\nexport const checkboxTick = (\n <svg className={styles.Tick} viewBox=\"0 0 24 24\" aria-hidden>\n <path\n d=\"M5 12.5l4.5 4.5L19 7\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={3}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n pathLength={1}\n />\n </svg>\n);\n\n/**\n * Internal. The checkbox box as a picture only, for places where something\n * else is the control: `MultiSelect` shows it inside each option, and the\n * option is what gets picked. It looks exactly like `Checkbox`, which uses\n * the same class on the real control.\n */\nexport const CheckboxBox = ({ checked }: { checked: boolean }) => (\n <span\n className={styles.Box}\n data-checked={checked ? \"\" : undefined}\n aria-hidden\n >\n {checkboxTick}\n </span>\n);\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so these parts render correctly without a provider.\n *\n * The row a `Checkbox` and a `RadioGroup.Item` share: the control, then its\n * label text.\n */\n\n.Row {\n display: flex;\n align-items: center;\n padding-block-end: var(--lea-spacing-1);\n margin: 0;\n color: var(--lea-palette-text-base);\n cursor: pointer;\n font-size: var(--lea-text-body-md-font-size);\n font-weight: var(--lea-text-body-md-font-weight);\n gap: var(--lea-spacing-1-5);\n letter-spacing: var(--lea-text-body-md-letter-spacing);\n line-height: var(--lea-text-body-md-line-height);\n}\n\n/* Base UI marks the control, whether it or its whole group is disabled. */\n.Row:has([data-disabled]) {\n color: var(--lea-palette-text-muted);\n cursor: not-allowed;\n}\n\n/* The asterisk keeps its error colour while the label text stays readable. */\n.Row[data-invalid] .Asterisk {\n color: var(--lea-palette-status-error-base);\n}\n\n.Text {\n min-width: 0;\n}\n","\"use client\";\n\nimport { Checkbox as BaseCheckbox } from \"@base-ui/react/checkbox\";\nimport {\n useId,\n type CSSProperties,\n type FocusEventHandler,\n type ReactNode,\n type Ref,\n} from \"react\";\n\nimport { checkboxTick } from \"../checkbox-box\";\nimport styles from \"../checkbox.module.css\";\nimport rowStyles from \"../choice-row.module.css\";\nimport { FieldHelperText, RequiredAsterisk } from \"../field-parts\";\n\n/**\n * Requires an accessible name so the checkbox is never nameless. Provide a\n * visible `label`, or an `aria-label` / `aria-labelledby` when the design has\n * none (a row in a table, say).\n */\ntype CheckboxAccessibleName =\n | { label: ReactNode; \"aria-label\"?: never; \"aria-labelledby\"?: never }\n | { label?: never; \"aria-label\": string; \"aria-labelledby\"?: never }\n | { label?: never; \"aria-label\"?: never; \"aria-labelledby\": string };\n\nexport type CheckboxProps = CheckboxAccessibleName & {\n /** Whether the box is ticked (controlled). Pair with `onCheckedChange`. */\n checked?: boolean;\n /** Whether the box is ticked when first rendered, for uncontrolled use. */\n defaultChecked?: boolean;\n /** Called with the new state when the box is ticked or cleared. */\n onCheckedChange?: (checked: boolean) => void;\n /** Helper / error text shown below the checkbox and linked via `aria-describedby`. */\n helperText?: ReactNode;\n /** Puts the checkbox and its helper text into the error state. */\n error?: boolean;\n /** Submits the checkbox under this name when inside a form. */\n name?: string;\n /** The value submitted when ticked. The browser default is `\"on\"`. */\n value?: string;\n /** Disables the checkbox. */\n disabled?: boolean;\n /** Marks the checkbox as required and adds the asterisk to the label. */\n required?: boolean;\n /** Id of the checkbox itself, the element with the `checkbox` role. */\n id?: string;\n onBlur?: FocusEventHandler<HTMLElement>;\n onFocus?: FocusEventHandler<HTMLElement>;\n /** Extra ids describing the checkbox, alongside the helper text. */\n \"aria-describedby\"?: string;\n /** Class on the outermost element, for layout. */\n className?: string;\n /** Inline style on the outermost element, for layout. */\n style?: CSSProperties;\n /**\n * The `<input type=\"checkbox\">` that holds the value. It is hidden, but\n * `ref.current.focus()` moves focus to the visible box.\n */\n ref?: Ref<HTMLInputElement>;\n};\n\nconst join = (...parts: (string | false | null | undefined)[]) =>\n parts.filter(Boolean).join(\" \") || undefined;\n\n/**\n * Checkbox with its label, helper text and error state built in, on Base UI's\n * accessible `Checkbox` primitive.\n *\n * Pass `label` for the text next to the box; it is a real `<label>`, so\n * clicking it ticks the box. Without a visible label, pass `aria-label`.\n * `className` and `style` go on the outermost element.\n *\n * ```tsx\n * <Checkbox\n * label=\"Jeg godtar vilkårene\"\n * checked={accepted}\n * onCheckedChange={setAccepted}\n * />\n * ```\n */\nexport function Checkbox({\n label,\n helperText,\n error = false,\n checked,\n defaultChecked,\n onCheckedChange,\n name,\n value,\n disabled = false,\n required = false,\n id,\n onBlur,\n onFocus,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n \"aria-describedby\": ariaDescribedBy,\n className,\n style,\n ref,\n}: CheckboxProps) {\n const helperTextId = useId();\n const hasLabel = label != null;\n const hasHelperText = helperText != null;\n const bare = !hasLabel && !hasHelperText;\n\n const box = (\n <BaseCheckbox.Root\n // A real `<button>`, so the `<label>` around it names it natively: in\n // the server markup, and as one element for `getByLabelText`.\n nativeButton\n render={<button type=\"button\" />}\n id={id}\n inputRef={ref}\n checked={checked}\n defaultChecked={defaultChecked}\n onCheckedChange={(next) => onCheckedChange?.(next)}\n name={name}\n value={value}\n disabled={disabled}\n required={required}\n onBlur={onBlur}\n onFocus={onFocus}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={join(hasHelperText && helperTextId, ariaDescribedBy)}\n aria-invalid={error ? true : undefined}\n // Nothing to lay out around the box, so `className` and `style` land on it.\n className={bare ? join(styles.Box, className) : styles.Box}\n style={bare ? style : undefined}\n >\n {checkboxTick}\n </BaseCheckbox.Root>\n );\n\n if (bare) return box;\n\n return (\n <div className={join(styles.Checkbox, className)} style={style}>\n {hasLabel ? (\n <label className={rowStyles.Row} data-invalid={error ? \"\" : undefined}>\n {box}\n <span className={rowStyles.Text}>\n {label}\n {required && <RequiredAsterisk className={rowStyles.Asterisk} />}\n </span>\n </label>\n ) : (\n box\n )}\n {hasHelperText && (\n <FieldHelperText id={helperTextId} error={error}>\n {helperText}\n </FieldHelperText>\n )}\n </div>\n );\n}\n","// `Country` is owned by the hooks/country module and re-exported from the\n// package root via the hooks barrel. Validators import it from there directly so\n// the validation barrel does not produce an ambiguous duplicate `Country`\n// export at the package root.\n\n/**\n * Result of a validation check. `normalized` holds the canonical form of the\n * input (e.g. digits only, or E.164 phone) when the value is valid.\n */\nexport interface ValidationResult {\n valid: boolean;\n normalized?: string;\n}\n\nexport const invalid = (): ValidationResult => ({ valid: false });\n\nexport const valid = (normalized: string): ValidationResult => ({\n valid: true,\n normalized,\n});\n","import type { Country } from \"../hooks/country/countries\";\nimport { invalid, valid, type ValidationResult } from \"./types\";\n\n/**\n * Luhn (mod-10) checksum over a string of digits.\n */\nfunction passesLuhn(digits: string): boolean {\n let sum = 0;\n let double = digits.length % 2 === 0;\n\n for (const char of digits) {\n let value = Number(char);\n if (double) {\n value *= 2;\n if (value > 9) value -= 9;\n }\n sum += value;\n double = !double;\n }\n\n return sum % 10 === 0;\n}\n\n/**\n * Swedish personnummer. Accepts 10 or 12 digit forms (with or without\n * separators) and validates the trailing Luhn check digit.\n */\nexport function isValidSwedishNationalId(value: string): boolean {\n let digits = value.replace(/\\D/g, \"\");\n\n if (digits.length === 12) {\n digits = digits.slice(2);\n }\n\n if (digits.length !== 10) {\n return false;\n }\n\n const month = Number(digits.slice(2, 4));\n const day = Number(digits.slice(4, 6));\n\n if (month < 1 || month > 12 || day < 1 || day > 31) {\n return false;\n }\n\n return passesLuhn(digits);\n}\n\nconst FINNISH_CHECK_CHARACTERS = \"0123456789ABCDEFHJKLMNPRSTUVWXY\";\n\n/**\n * Finnish henkilötunnus (HETU). Validates structure, date, century marker and\n * the mod-31 check character.\n */\nexport function isValidFinnishNationalId(value: string): boolean {\n const normalized = value.replace(/\\s/g, \"\").toUpperCase();\n\n if (!/^\\d{6}[+\\-A]\\d{3}[0-9A-Y]$/.test(normalized)) {\n return false;\n }\n\n const day = Number(normalized.slice(0, 2));\n const month = Number(normalized.slice(2, 4));\n const yearShort = Number(normalized.slice(4, 6));\n const centurySign = normalized[6];\n\n const centuryBase =\n centurySign === \"+\"\n ? 1800\n : centurySign === \"-\"\n ? 1900\n : centurySign === \"A\"\n ? 2000\n : null;\n\n if (centuryBase === null) {\n return false;\n }\n\n const year = centuryBase + yearShort;\n const date = new Date(year, month - 1, day);\n\n if (\n date.getFullYear() !== year ||\n date.getMonth() !== month - 1 ||\n date.getDate() !== day\n ) {\n return false;\n }\n\n const checksumNumber = Number(\n `${normalized.slice(0, 6)}${normalized.slice(7, 10)}`,\n );\n\n return normalized[10] === FINNISH_CHECK_CHARACTERS[checksumNumber % 31];\n}\n\nconst NO_WEIGHTS_1 = [3, 7, 6, 1, 8, 9, 4, 5, 2];\nconst NO_WEIGHTS_2 = [5, 4, 3, 2, 7, 6, 5, 4, 3, 2];\n\nfunction norwegianControlDigit(digits: number[], weights: number[]): number {\n const sum = weights.reduce((acc, weight, i) => acc + weight * digits[i], 0);\n const remainder = sum % 11;\n return remainder === 0 ? 0 : 11 - remainder;\n}\n\n/**\n * Norwegian fødselsnummer. 11 digits with two mod-11 control digits.\n */\nexport function isValidNorwegianNationalId(value: string): boolean {\n const digits = value.replace(/\\D/g, \"\");\n\n if (digits.length !== 11) {\n return false;\n }\n\n const numbers = digits.split(\"\").map(Number);\n const day = Number(digits.slice(0, 2));\n const month = Number(digits.slice(2, 4));\n\n // Accounts for D-numbers (day + 40) and H-numbers (month + 40).\n const realDay = day > 40 ? day - 40 : day;\n const realMonth = month > 40 ? month - 40 : month;\n\n if (realMonth < 1 || realMonth > 12 || realDay < 1 || realDay > 31) {\n return false;\n }\n\n const k1 = norwegianControlDigit(numbers.slice(0, 9), NO_WEIGHTS_1);\n if (k1 === 11 || k1 !== numbers[9]) {\n return false;\n }\n\n const k2 = norwegianControlDigit(numbers.slice(0, 10), NO_WEIGHTS_2);\n if (k2 === 11 || k2 !== numbers[10]) {\n return false;\n }\n\n return true;\n}\n\nconst SPANISH_DNI_LETTERS = \"TRWAGMYFPDXBNJZSQVHLCKE\";\nconst SPANISH_NIE_PREFIX: Record<string, string> = { X: \"0\", Y: \"1\", Z: \"2\" };\n\n/**\n * Spanish DNI / NIE. Validates the control letter (mod-23). NIE prefixes\n * (X/Y/Z) are mapped to their numeric equivalents before the check.\n */\nexport function isValidSpanishNationalId(value: string): boolean {\n const normalized = value.replace(/[\\s-]/g, \"\").toUpperCase();\n\n if (!/^[XYZ]?\\d{7,8}[A-Z]$/.test(normalized)) {\n return false;\n }\n\n const letter = normalized.slice(-1);\n let numberPart = normalized.slice(0, -1);\n\n const prefix = numberPart[0];\n if (prefix in SPANISH_NIE_PREFIX) {\n numberPart = SPANISH_NIE_PREFIX[prefix] + numberPart.slice(1);\n }\n\n if (numberPart.length !== 8) {\n return false;\n }\n\n return SPANISH_DNI_LETTERS[Number(numberPart) % 23] === letter;\n}\n\nconst NATIONAL_ID_VALIDATORS: Record<Country, (value: string) => boolean> = {\n SE: isValidSwedishNationalId,\n FI: isValidFinnishNationalId,\n NO: isValidNorwegianNationalId,\n ES: isValidSpanishNationalId,\n};\n\n/**\n * Validates a national identity number for the given country.\n */\nexport function isValidNationalId(country: Country, value: string): boolean {\n return NATIONAL_ID_VALIDATORS[country](value);\n}\n\n/**\n * Validates a national identity number and returns the normalized (uppercased,\n * separator-free) value when valid.\n */\nexport function validateNationalId(\n country: Country,\n value: string,\n): ValidationResult {\n if (!isValidNationalId(country, value)) {\n return invalid();\n }\n\n return valid(value.replace(/[\\s-]/g, \"\").toUpperCase());\n}\n\n/**\n * The length of a national identity number once {@link normalizeNationalId}\n * has removed its formatting. Swedish numbers are 12 digits: the form with the\n * full year of birth (`YYYYMMDDNNNN`). The 10-digit form passes\n * {@link isValidNationalId} but is 10 long here, so a form that needs the\n * full year can ask for it.\n */\nexport const NATIONAL_ID_LENGTH: Readonly<Record<Country, number>> = {\n SE: 12,\n FI: 11,\n NO: 11,\n ES: 9,\n};\n\n// Hyphen, non-breaking hyphen, figure dash, en dash, em dash, horizontal bar\n// and minus sign. Phones and pasted text turn a typed \"-\" into one of these.\nconst DASHES = /[‐-―−]/g;\n\nconst NATIONAL_ID_NORMALIZERS: Record<Country, (value: string) => string> = {\n // Digits only: separators, the \"+\" of the 10-digit form and stray dots go.\n SE: (value) => value.replace(/\\D/g, \"\"),\n // The century sign (`+`, `-`, `A`…) and the check character are part of\n // the number, so only what cannot be part of it goes.\n FI: (value) =>\n value\n .replace(DASHES, \"-\")\n .toUpperCase()\n .replace(/[^0-9A-Z+-]/g, \"\"),\n NO: (value) => value.replace(/\\D/g, \"\"),\n ES: (value) => value.toUpperCase().replace(/[^0-9A-Z]/g, \"\"),\n};\n\n/**\n * Removes the formatting people type or paste into a national identity\n * number (spaces, separators, dashes of every kind) and keeps what the\n * checksum needs. It does not validate: pass the result to\n * {@link isValidNationalId}, and compare its length with\n * {@link NATIONAL_ID_LENGTH}.\n *\n * @example\n * normalizeNationalId(\"SE\", \"19811218–9876\") // → \"198112189876\"\n * normalizeNationalId(\"FI\", \"131052-308t\") // → \"131052-308T\"\n */\nexport function normalizeNationalId(country: Country, value: string): string {\n return NATIONAL_ID_NORMALIZERS[country](value);\n}\n\n/**\n * Whether two national identity numbers are the same person's, as for \"the\n * co-applicant must not be the main applicant\". Both are normalised first,\n * and both must be valid: an empty or invalid number never matches.\n */\nexport function isSameNationalId(\n country: Country,\n a: string | null | undefined,\n b: string | null | undefined,\n): boolean {\n const first = normalizeNationalId(country, a ?? \"\");\n const second = normalizeNationalId(country, b ?? \"\");\n\n return first !== \"\" && first === second && isValidNationalId(country, first);\n}\n","// Generated by scripts/generate-mobile-patterns.mjs from libphonenumber-js\n// 1.13.10 (\"mobile\" metadata). Do not edit by hand.\n\n/**\n * The national number of a mobile phone, per country. A number is a mobile\n * when the whole national number matches.\n */\nexport const MOBILE_PATTERNS = {\n SE: \"7[023689]\\\\d{7}\",\n FI: \"4946\\\\d{2,6}|(?:4[0-8]|50)\\\\d{4,8}\",\n NO: \"(?:4[015-9]|9\\\\d)\\\\d{6}\",\n ES: \"96906(?:09|10)\\\\d\\\\d|(?:590(?:10[0-2]|600)|97390\\\\d)\\\\d{3}|(?:6\\\\d|7[1-48])\\\\d{7}\",\n} as const;\n","import {\n parsePhoneNumberFromString,\n type CountryCode,\n} from \"libphonenumber-js\";\nimport { MOBILE_PATTERNS } from \"./mobile-patterns\";\nimport { invalid, valid, type ValidationResult } from \"./types\";\n\nexport type { CountryCode };\n\n/**\n * Validates a phone number for the given country, delegating to\n * `libphonenumber-js` so every country's rules stay accurate and maintained.\n *\n * Accepts national format (with or without a leading trunk `0`) and\n * international format (`+CC` / `00CC`). On success, `normalized` holds the\n * canonical E.164 form (e.g. `+358401234567`).\n */\nexport function validatePhoneNumber(\n country: CountryCode,\n value: string,\n): ValidationResult {\n // Strip all whitespace so the display grouping (e.g. \"400 12 345\") never\n // reaches the parser; validation runs on the bare number.\n const cleaned = value.replace(/\\s/g, \"\");\n if (cleaned === \"\") {\n return invalid();\n }\n\n const parsed = parsePhoneNumberFromString(cleaned, country);\n if (!parsed || !parsed.isValid()) {\n return invalid();\n }\n\n return valid(parsed.number);\n}\n\n/**\n * Boolean convenience wrapper around {@link validatePhoneNumber}.\n */\nexport function isValidPhoneNumber(\n country: CountryCode,\n value: string,\n): boolean {\n return validatePhoneNumber(country, value).valid;\n}\n\n/**\n * The countries {@link validateMobileNumber} knows mobile numbers for: Lea\n * Bank's markets.\n */\nexport type MobileCountry = keyof typeof MOBILE_PATTERNS;\n\nconst MOBILE_REGEXPS = Object.fromEntries(\n Object.entries(MOBILE_PATTERNS).map(([country, pattern]) => [\n country,\n new RegExp(`^(?:${pattern})$`),\n ]),\n) as Record<MobileCountry, RegExp>;\n\n/**\n * Whether {@link validateMobileNumber} can tell a mobile number from a\n * landline in this country.\n */\nexport function isMobileCountry(country: string): country is MobileCountry {\n return Object.prototype.hasOwnProperty.call(MOBILE_PATTERNS, country);\n}\n\n/**\n * Validates a mobile phone number of the given country, as\n * {@link validatePhoneNumber} does, and also rejects:\n *\n * - landlines and other numbers that are not a mobile (`08-123 456 78`), and\n * - a valid number of another country (`+47 412 34 567` for `\"SE\"`).\n *\n * Mobile numbers come from libphonenumber-js's own metadata\n * (`scripts/generate-mobile-patterns.mjs`). On success, `normalized` holds\n * the E.164 form (`+46701234567`).\n */\nexport function validateMobileNumber(\n country: MobileCountry,\n value: string,\n): ValidationResult {\n const cleaned = value.replace(/\\s/g, \"\");\n if (cleaned === \"\") {\n return invalid();\n }\n\n const parsed = parsePhoneNumberFromString(cleaned, country);\n if (\n !parsed ||\n !parsed.isValid() ||\n parsed.country !== country ||\n !MOBILE_REGEXPS[country].test(parsed.nationalNumber)\n ) {\n return invalid();\n }\n\n return valid(parsed.number);\n}\n\n/**\n * Whether two phone numbers are the same number, as for \"the co-applicant\n * must not be the main applicant\". Both are read as numbers of `country`\n * unless they carry a country code (`+46…`, `0046…`), and both must be\n * valid: an empty or invalid number never matches.\n */\nexport function isSamePhone(\n country: CountryCode,\n a: string | null | undefined,\n b: string | null | undefined,\n): boolean {\n const first = validatePhoneNumber(country, a ?? \"\");\n const second = validatePhoneNumber(country, b ?? \"\");\n\n return first.valid && second.valid && first.normalized === second.normalized;\n}\n","export const modulo97 = (digits: string): number => {\n let remainder = 0;\n\n for (const digit of digits) {\n remainder = (remainder * 10 + Number(digit)) % 97;\n }\n\n return remainder;\n};\n\nexport const replaceLatinLettersWithNumbers = (value: string): string =>\n value.replace(/[A-Z]/g, (char) => String(char.charCodeAt(0) - 55));\n","import type { Country } from \"../hooks/country/countries\";\nimport { modulo97, replaceLatinLettersWithNumbers } from \"./modulo-97\";\nimport { invalid, valid, type ValidationResult } from \"./types\";\n\nconst IBAN_LENGTHS: Record<Country, number> = {\n SE: 24,\n FI: 18,\n NO: 15,\n ES: 24,\n};\n\nfunction normalize(value: string): string {\n return value.replace(/[\\s-]/g, \"\").toUpperCase();\n}\n\n/**\n * Generic IBAN check: structure, country length and the ISO 7064 mod-97\n * checksum.\n */\nexport function isValidIban(value: string, country?: Country): boolean {\n const iban = normalize(value);\n\n if (!/^[A-Z]{2}\\d{2}[A-Z0-9]+$/.test(iban)) {\n return false;\n }\n\n const ibanCountry = iban.slice(0, 2) as Country;\n\n if (country && ibanCountry !== country) {\n return false;\n }\n\n const expectedLength = IBAN_LENGTHS[ibanCountry];\n if (expectedLength && iban.length !== expectedLength) {\n return false;\n }\n\n const rearranged = iban.slice(4) + iban.slice(0, 4);\n return modulo97(replaceLatinLettersWithNumbers(rearranged)) === 1;\n}\n\nconst NO_ACCOUNT_WEIGHTS = [5, 4, 3, 2, 7, 6, 5, 4, 3, 2];\n\n/**\n * Norwegian kontonummer: 11 digits with a mod-11 control digit.\n */\nexport function isValidNorwegianAccount(value: string): boolean {\n const digits = value.replace(/\\D/g, \"\");\n\n if (digits.length !== 11) {\n return false;\n }\n\n const numbers = digits.split(\"\").map(Number);\n const sum = NO_ACCOUNT_WEIGHTS.reduce(\n (acc, weight, i) => acc + weight * numbers[i],\n 0,\n );\n const remainder = sum % 11;\n const control = remainder === 0 ? 0 : 11 - remainder;\n\n return control < 10 && control === numbers[10];\n}\n\n/**\n * Swedish clearing + account number. Bank-specific checksums vary, so this is a\n * structural check: a 4-5 digit clearing number followed by a 6-10 digit\n * account number.\n */\nexport function isValidSwedishAccount(value: string): boolean {\n const digits = value.replace(/\\D/g, \"\");\n return digits.length >= 10 && digits.length <= 15;\n}\n\n/**\n * Validates a bank account number for the given country.\n *\n * - **FI / ES** require a valid IBAN.\n * - **NO** accepts a domestic kontonummer or a Norwegian IBAN.\n * - **SE** accepts a domestic clearing + account number or a Swedish IBAN.\n */\nexport function validateBankAccount(\n country: Country,\n value: string,\n): ValidationResult {\n const normalized = normalize(value);\n const looksLikeIban = /^[A-Z]{2}\\d{2}/.test(normalized);\n\n switch (country) {\n case \"FI\":\n case \"ES\":\n return isValidIban(normalized, country) ? valid(normalized) : invalid();\n case \"NO\":\n if (looksLikeIban) {\n return isValidIban(normalized, \"NO\") ? valid(normalized) : invalid();\n }\n return isValidNorwegianAccount(normalized)\n ? valid(normalized.replace(/\\D/g, \"\"))\n : invalid();\n case \"SE\":\n if (looksLikeIban) {\n return isValidIban(normalized, \"SE\") ? valid(normalized) : invalid();\n }\n return isValidSwedishAccount(normalized)\n ? valid(normalized.replace(/\\D/g, \"\"))\n : invalid();\n }\n}\n\n/**\n * Boolean convenience wrapper around {@link validateBankAccount}.\n */\nexport function isValidBankAccount(country: Country, value: string): boolean {\n return validateBankAccount(country, value).valid;\n}\n","import { modulo97 } from \"./modulo-97\";\nimport { invalid, valid, type ValidationResult } from \"./types\";\n\nconst normalize = (value: string): string =>\n value.replace(/\\s/g, \"\").toUpperCase();\n\n/** Finnish domestic reference: 3–19 base digits and a 7-3-1 check digit. */\nexport function isValidFinnishDomesticReference(value: string): boolean {\n const reference = normalize(value);\n if (!/^[1-9]\\d{3,19}$/.test(reference)) {\n return false;\n }\n\n const base = reference.slice(0, -1);\n const checkDigit = Number(reference.at(-1));\n const weights = [7, 3, 1];\n let sum = 0;\n\n for (let index = base.length - 1, weightIndex = 0; index >= 0; index--) {\n sum += Number(base[index]) * weights[weightIndex % weights.length];\n weightIndex++;\n }\n\n return (10 - (sum % 10)) % 10 === checkDigit;\n}\n\n/** ISO 11649 RF reference using Finland's 4–20 digit reference length. */\nexport function isValidFinnishRfReference(value: string): boolean {\n const reference = normalize(value);\n if (!/^RF\\d{2}[1-9]\\d{3,19}$/.test(reference)) {\n return false;\n }\n\n const rearranged = `${reference.slice(4)}2715${reference.slice(2, 4)}`;\n return modulo97(rearranged) === 1;\n}\n\n/** Validates either a Finnish domestic or ISO 11649 RF reference. */\nexport function validateFinnishReference(value: string): ValidationResult {\n const normalized = normalize(value);\n\n return isValidFinnishDomesticReference(normalized) ||\n isValidFinnishRfReference(normalized)\n ? valid(normalized)\n : invalid();\n}\n\n/** Boolean convenience wrapper around {@link validateFinnishReference}. */\nexport function isValidFinnishReference(value: string): boolean {\n return validateFinnishReference(value).valid;\n}\n","import { invalid, valid, type ValidationResult } from \"./types\";\n\n// Pragmatic RFC-5322-ish pattern: good enough for UI validation without the\n// false negatives of an overly strict expression.\nconst EMAIL_PATTERN =\n /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+$/;\n\n/**\n * Common consumer email domains across the Nordics and Spain, used to power the\n * `@`-triggered autocomplete dropdown.\n */\nexport const COMMON_EMAIL_DOMAINS: readonly string[] = [\n \"gmail.com\",\n \"outlook.com\",\n \"hotmail.com\",\n \"icloud.com\",\n \"yahoo.com\",\n \"live.com\",\n \"live.se\",\n \"live.no\",\n \"hotmail.se\",\n \"hotmail.no\",\n \"telia.com\",\n \"online.no\",\n \"telenor.no\",\n \"elisanet.fi\",\n \"suomi24.fi\",\n \"telia.se\",\n \"comhem.se\",\n \"kolumbus.fi\",\n \"pp.inet.fi\",\n \"luukku.com\",\n \"me.com\",\n \"msn.com\",\n \"gmx.com\",\n \"yahoo.se\",\n \"yahoo.no\",\n];\n\n/**\n * Validates an email address and returns the lowercased value when valid.\n */\nexport function validateEmail(value: string): ValidationResult {\n const trimmed = value.trim();\n\n if (!EMAIL_PATTERN.test(trimmed)) {\n return invalid();\n }\n\n const atIndex = trimmed.lastIndexOf(\"@\");\n if (atIndex >= 0) {\n const domain = trimmed.slice(atIndex + 1).toLowerCase();\n if (domain.endsWith(\".con\")) {\n return invalid();\n }\n }\n\n return valid(trimmed.toLowerCase());\n}\n\n/**\n * Boolean convenience wrapper around {@link validateEmail}.\n */\nexport function isValidEmail(value: string): boolean {\n return validateEmail(value).valid;\n}\n\n/**\n * Given the current email input, returns a list of completed email suggestions\n * based on common domains. Suggestions only appear once an `@` has been typed,\n * and are filtered by whatever partial domain follows it.\n *\n * @example\n * getEmailDomainSuggestions(\"alice@\") // → [\"alice@gmail.com\", ...]\n * getEmailDomainSuggestions(\"alice@gm\") // → [\"alice@gmail.com\"]\n */\nexport function getEmailDomainSuggestions(\n value: string,\n domains: readonly string[] = COMMON_EMAIL_DOMAINS,\n): string[] {\n const atIndex = value.indexOf(\"@\");\n\n if (atIndex < 1) {\n return [];\n }\n\n const localPart = value.slice(0, atIndex);\n const domainPart = value.slice(atIndex + 1).toLowerCase();\n\n // Don't suggest once a complete domain has been typed.\n if (domainPart.includes(\".\") && domains.includes(domainPart)) {\n return [];\n }\n\n return domains\n .filter((domain) => domain.startsWith(domainPart))\n .map((domain) => `${localPart}@${domain}`);\n}\n\n/**\n * Whether two email addresses are the same, ignoring case and surrounding\n * spaces, as for \"the co-applicant must not be the main applicant\". An empty\n * address never matches.\n */\nexport function isSameEmail(\n a: string | null | undefined,\n b: string | null | undefined,\n): boolean {\n const first = (a ?? \"\").trim().toLowerCase();\n const second = (b ?? \"\").trim().toLowerCase();\n\n return first !== \"\" && first === second;\n}\n\n/*\n * Typo suggestions, the approach of Mailcheck (github.com/mailcheck/mailcheck),\n * written here with our own lists: compare the domain with known domains and,\n * failing that, the top-level domain with known ones.\n */\n\n// Real domains a typo or two away from a common one. They are never\n// \"corrected\": anna@mail.com is not a typo of gmail.com.\nconst REAL_LOOKALIKES = [\n \"mail.com\",\n \"email.com\",\n \"ymail.com\",\n \"googlemail.com\",\n \"gmx.net\",\n \"gmx.de\",\n \"gmx.se\",\n \"aol.com\",\n \"mac.com\",\n \"live.dk\",\n \"live.fi\",\n \"hotmail.dk\",\n \"hotmail.fi\",\n \"hotmail.es\",\n \"outlook.es\",\n \"yahoo.es\",\n \"yahoo.dk\",\n \"tele2.se\",\n \"telenor.se\",\n \"telenor.com\",\n \"spray.se\",\n \"bredband.net\",\n];\n\n// Top-level domains people use for email. One that is not here and is one\n// typo from exactly one that is gets corrected (\"con\" → \"com\"), so the list\n// errs on the long side: a real one missing from it could be \"corrected\".\nconst TOP_LEVEL_DOMAINS = [\n \"com\",\n \"net\",\n \"org\",\n \"se\",\n \"no\",\n \"fi\",\n \"es\",\n \"nu\",\n \"dk\",\n \"is\",\n \"ax\",\n \"fo\",\n \"gl\",\n \"de\",\n \"eu\",\n \"uk\",\n \"co.uk\",\n \"fr\",\n \"it\",\n \"nl\",\n \"be\",\n \"at\",\n \"ch\",\n \"pl\",\n \"ee\",\n \"lv\",\n \"lt\",\n \"pt\",\n \"ie\",\n \"us\",\n \"ca\",\n \"info\",\n \"biz\",\n \"edu\",\n \"gov\",\n \"io\",\n \"me\",\n \"app\",\n \"dev\",\n];\n\n/** Edits (insert, delete, replace, swap two neighbours) from `a` to `b`. */\nfunction editDistance(a: string, b: string): number {\n const d = Array.from({ length: a.length + 1 }, (_, i) =>\n Array.from({ length: b.length + 1 }, (_, j) =>\n i === 0 ? j : j === 0 ? i : 0,\n ),\n );\n for (let i = 1; i <= a.length; i++) {\n for (let j = 1; j <= b.length; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n d[i][j] = Math.min(\n d[i - 1][j] + 1,\n d[i][j - 1] + 1,\n d[i - 1][j - 1] + cost,\n );\n if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {\n d[i][j] = Math.min(d[i][j], d[i - 2][j - 2] + 1);\n }\n }\n }\n return d[a.length][b.length];\n}\n\n/** The one candidate within `limit` edits, nearest first. A tie is no answer. */\nfunction closest(\n value: string,\n candidates: readonly string[],\n limit: number,\n): string | undefined {\n let best: string | undefined;\n let bestDistance = limit + 1;\n let tie = false;\n for (const candidate of candidates) {\n const distance = editDistance(value, candidate);\n if (distance < bestDistance) {\n [best, bestDistance, tie] = [candidate, distance, false];\n } else if (distance === bestDistance && candidate !== best) {\n tie = true;\n }\n }\n return tie ? undefined : best;\n}\n\n/**\n * Suggests a correction for a mistyped email domain, or `undefined` when there\n * is nothing to suggest. It never changes a domain it knows, nor a domain that\n * is merely uncommon (`anna@firma.se` stays).\n *\n * @example\n * suggestEmailCorrection(\"anna@gnail.con\") // → \"anna@gmail.com\"\n * suggestEmailCorrection(\"anna@hotmial.com\") // → \"anna@hotmail.com\"\n * suggestEmailCorrection(\"anna@firma.con\") // → \"anna@firma.com\"\n * suggestEmailCorrection(\"anna@firma.se\") // → undefined\n */\nexport function suggestEmailCorrection(\n value: string,\n domains: readonly string[] = COMMON_EMAIL_DOMAINS,\n): string | undefined {\n const trimmed = value.trim();\n const at = trimmed.lastIndexOf(\"@\");\n if (at < 1) return undefined;\n\n const local = trimmed.slice(0, at);\n const domain = trimmed\n .slice(at + 1)\n .toLowerCase()\n .replace(/\\.+$/, \"\");\n if (!/^[a-z0-9-]+(\\.[a-z0-9-]+)*$/.test(domain)) return undefined;\n\n const known = [...domains, ...REAL_LOOKALIKES];\n if (known.includes(domain)) return undefined;\n\n const suggest = (fixed: string | undefined) =>\n fixed && fixed !== domain && !REAL_LOOKALIKES.includes(fixed)\n ? `${local}@${fixed}`\n : undefined;\n const nameOf = (entry: string) => entry.slice(0, entry.indexOf(\".\"));\n const tldOf = (entry: string) => entry.slice(entry.indexOf(\".\") + 1);\n\n const dot = domain.indexOf(\".\");\n\n // No top-level domain at all: \"anna@gmail\", \"anna@hotmial\".\n if (dot < 0) {\n const names = [...new Set(domains.map(nameOf))];\n const name = closest(domain, names, domain.length >= 6 ? 2 : 1);\n return suggest(domains.find((entry) => nameOf(entry) === name));\n }\n\n const name = domain.slice(0, dot);\n const tld = domain.slice(dot + 1);\n\n // A real top-level domain: only a typo in the name, of one edit, against\n // the known domains with that top-level domain: \"hotmial.com\". A name with\n // a different real top-level domain (online.de) is somebody else's.\n if (TOP_LEVEL_DOMAINS.includes(tld)) {\n const sameTld = known.filter((entry) => tldOf(entry) === tld);\n return suggest(closest(domain, sameTld, 1));\n }\n\n // A top-level domain that does not exist: a known domain one typo away, or\n // two in a longer one (\"gnail.con\"), or else the nearest real top-level\n // domain (\"firma.con\").\n const near = closest(domain, known, domain.length >= 9 ? 2 : 1);\n if (near) return suggest(near);\n\n const fixedTld = closest(tld, TOP_LEVEL_DOMAINS, 1);\n return fixedTld ? suggest(`${name}.${fixedTld}`) : undefined;\n}\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n */\n\n.Link {\n --link-color: var(--lea-palette-secondary-base);\n\n color: var(--link-color);\n /* Size, line height and family come from the surrounding text. */\n font-weight: var(--lea-font-weight-regular);\n /*\n * Underlined at rest: in a sentence the colour alone does not set a link\n * apart from the words around it, least of all `secondary`, which is the\n * colour of body text.\n */\n text-decoration: underline;\n text-decoration-thickness: 0.0625rem;\n /* Clears the descenders of the font; there is no token for it. */\n text-underline-offset: 0.3rem;\n cursor: pointer;\n}\n\n/*\n * The brand colours as text: a step of the same scale dark enough to read at\n * body size. `tertiary.base` is too light for text on white.\n */\n.Link[data-color=\"primary\"] {\n --link-color: var(--lea-color-purple-700);\n}\n\n.Link[data-color=\"tertiary\"] {\n --link-color: var(--lea-color-teal-700);\n}\n\n.Link[data-color=\"contrast\"] {\n --link-color: var(--lea-palette-text-contrast);\n}\n\n/* `render={<button type=\"button\" />}`: an action that looks like a link. */\nbutton.Link {\n padding: 0;\n border: 0;\n appearance: none;\n background: none;\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n letter-spacing: inherit;\n text-align: inherit;\n}\n\n.Link:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n/*\n * `arrow`: a call to action of its own, as the website's CTA links. The\n * chevron is 24px whatever the text size, 4px after the text.\n */\n.Link[data-arrow] {\n --link-hover-color: var(--lea-palette-primary-base);\n\n display: inline-flex;\n align-items: center;\n gap: var(--lea-spacing-0-5);\n transition: color var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n/*\n * On a dark surface the hover is light purple. Plain links keep their colour\n * on hover: on a status or brand fill (a filled `Alert`, say) a purple hover\n * would drop below 4.5:1, so it is only given to links with the arrow, which\n * sit on the website's white, grey, beige, black and deep purple surfaces.\n */\n.Link[data-arrow][data-color=\"contrast\"] {\n --link-hover-color: var(--lea-palette-text-contrast-hover);\n}\n\n.Link[data-arrow][data-color=\"primary\"],\n.Link[data-arrow][data-color=\"tertiary\"] {\n --link-hover-color: var(--link-color);\n}\n\n.Text {\n min-width: 0;\n}\n\n.Arrow {\n width: var(--lea-spacing-3);\n height: var(--lea-spacing-3);\n flex-shrink: 0;\n transition: translate var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n/* Hover thickens the line, from 1px to 2px, without moving the text. */\n@media (hover: hover) {\n .Link:hover {\n text-decoration-thickness: 0.125rem;\n }\n\n .Link[data-arrow]:hover {\n color: var(--link-hover-color);\n }\n\n .Link[data-arrow]:hover .Arrow {\n translate: var(--lea-spacing-1) 0;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Link[data-arrow],\n .Arrow {\n transition-duration: 0.01ms;\n }\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { ChevronRight } from \"lucide-react\";\nimport type { Ref } from \"react\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport type { ColorRoles } from \"../../color-roles\";\nimport styles from \"./link.module.css\";\n\nexport type LinkColor = ColorRoles<\n \"secondary\" | \"primary\" | \"tertiary\" | \"contrast\"\n>;\n\nexport type LinkProps = Omit<useRender.ComponentProps<\"a\">, \"ref\" | \"color\"> & {\n /**\n * The colour of the link: `secondary` is the near-black of body text,\n * `primary` the brand purple, `tertiary` the brand teal. Use `contrast`\n * (white) on a dark surface.\n * @default \"secondary\"\n */\n color?: LinkColor;\n /**\n * Adds a chevron after the text, for a link that stands on its own as a call\n * to action, as on the website. On hover the chevron slides to the right and\n * the text turns purple (`secondary`) or light purple (`contrast`). Checked\n * for contrast on white, `bg.subtle`, `bg.muted` and the status tints\n * (`secondary`), and on `bg.contrast` and `bg.contrast-accent` (`contrast`).\n * @default false\n */\n arrow?: boolean;\n /** The rendered element: the `<a>`, or the element passed to `render`. */\n ref?: Ref<HTMLElement>;\n};\n\n/**\n * A link inside running text: underlined, so it is told apart from the words\n * around it by more than its colour, and in the size and line height of the\n * text around it. Renders an `a`; pass a router link\n * through `render`:\n *\n * ```tsx\n * <Link href=\"/om-oss\">Om oss</Link>\n * <Link render={<NextLink href=\"/om-oss\" />}>Om oss</Link>\n * <Link render={<RouterLink to=\"/om-oss\" />}>Om oss</Link>\n * ```\n *\n * For an action that only looks like a link, render a button:\n * `<Link render={<button type=\"button\" />} onClick={…}>`. For a link that\n * looks like a button, use `Button` with `render`.\n *\n * With `arrow`, the link is a call to action of its own, sized by the text\n * around it: `<Text textStyle=\"body-lg\"><Link arrow href=\"/lan\">Se alle\n * lån</Link></Text>`.\n */\nexport function Link({\n color = \"secondary\",\n arrow = false,\n render,\n ref,\n className,\n ...rest\n}: LinkProps) {\n return useRender({\n render,\n ref,\n defaultTagName: \"a\",\n props: {\n ...rest,\n \"data-color\": color,\n \"data-arrow\": arrow || undefined,\n className: mergeClassName(styles.Link, className)({}),\n ...(arrow && {\n children: (\n <>\n <span className={styles.Text}>{rest.children}</span>\n <ChevronRight aria-hidden className={styles.Arrow} />\n </>\n ),\n }),\n },\n });\n}\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n *\n * `--_gap` arrives inline from the `gap` prop of the root and of each item.\n */\n\n.Root {\n display: flex;\n flex-direction: column;\n gap: calc(var(--lea-spacing) * var(--_gap));\n padding: 0;\n margin: 0;\n list-style: none;\n counter-reset: list-counter;\n}\n\n.Item {\n display: flex;\n align-items: flex-start;\n gap: calc(var(--lea-spacing) * var(--_gap));\n counter-increment: list-counter;\n}\n\n/* As tall as one line of body text, so the marker centres on the first line\n of the item instead of on the whole block. */\n.Marker {\n display: flex;\n min-width: var(--lea-spacing-3);\n height: calc(\n var(--lea-text-body-md-font-size) * var(--lea-text-body-md-line-height)\n );\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n}\n\n.Marker[data-marker=\"dot\"]::before {\n width: 0.375rem;\n height: 0.375rem;\n border-radius: var(--lea-radius-full);\n background-color: var(--lea-palette-text-base);\n content: \"\";\n}\n\n/* heading-xs's size and weight on a line as tall as the digit, so the number\n centres in the marker like the dot does. */\n.Marker[data-marker=\"number\"] {\n color: var(--lea-palette-text-base);\n font-size: var(--lea-font-step-0);\n font-weight: var(--lea-font-weight-semibold);\n line-height: var(--lea-line-height-none);\n}\n\n.Marker[data-marker=\"number\"]::before {\n content: counter(list-counter) \".\";\n}\n\n.Content {\n min-width: 0;\n flex: 1;\n}\n","\"use client\";\n\nimport {\n type ComponentProps,\n type CSSProperties,\n type ReactNode,\n createContext,\n useContext,\n} from \"react\";\n\nimport styles from \"./lists.module.css\";\nimport { Text } from \"../text/text\";\n\n/**\n * List is a composition component for rendering ordered or unordered lists.\n *\n * `List.Root` renders the wrapping `<ul>` (default) or `<ol>` element via the\n * `variant` prop. Each `List.Item` shows a marker: a dot for `ul`, an\n * auto-incrementing number for `ol`, or a custom icon when one is passed.\n *\n * @example Unordered list with default dots\n * <List.Root>\n * <List.Item>Fast response</List.Item>\n * <List.Item>Account data report</List.Item>\n * </List.Root>\n *\n * @example Ordered list with auto-numbering\n * <List.Root variant=\"ol\">\n * <List.Item>First step</List.Item>\n * <List.Item>Second step</List.Item>\n * </List.Root>\n *\n * @example Per-item icon marker\n * <List.Root>\n * <List.Item icon={<CircleCheck size={20} />}>Verified</List.Item>\n * </List.Root>\n */\n\ntype ListVariant = \"ul\" | \"ol\";\n\ninterface ListContextValue {\n variant: ListVariant;\n}\n\nconst ListContext = createContext<ListContextValue | null>(null);\n\nconst useListContext = () => {\n const context = useContext(ListContext);\n if (!context) {\n throw new Error(\"List parts must be used within <List.Root>\");\n }\n return context;\n};\n\nconst joinClassNames = (...names: (string | undefined)[]) =>\n names.filter(Boolean).join(\" \");\n\ntype RootProps = Omit<ComponentProps<\"ul\">, \"ref\"> & {\n /**\n * The type of list to render.\n * @default \"ul\"\n */\n variant?: ListVariant;\n /**\n * The gap between the list items, as a multiple of the spacing step.\n * @default 1.5\n */\n gap?: number;\n};\n\nexport const Root = ({\n variant = \"ul\",\n gap = 1.5,\n className,\n style,\n children,\n ...rest\n}: RootProps) => {\n const Element = variant;\n\n return (\n <ListContext.Provider value={{ variant }}>\n <Element\n {...rest}\n className={joinClassNames(styles.Root, className)}\n style={{ \"--_gap\": gap, ...style } as CSSProperties}\n >\n {children}\n </Element>\n </ListContext.Provider>\n );\n};\n\ntype ItemProps = Omit<ComponentProps<\"li\">, \"ref\"> & {\n /** Custom marker rendered in place of the default dot/number. */\n icon?: ReactNode;\n /**\n * The gap between a custom `icon` and the text, as a multiple of the spacing\n * step. The dot and the number keep their own spacing.\n * @default 1.5\n */\n gap?: number;\n};\n\nexport const Item = ({\n icon,\n gap = 1.5,\n className,\n style,\n children,\n ...rest\n}: ItemProps) => {\n const { variant } = useListContext();\n const marker = icon ? \"icon\" : variant === \"ol\" ? \"number\" : \"dot\";\n\n return (\n <li\n {...rest}\n className={joinClassNames(styles.Item, className)}\n style={{ \"--_gap\": icon ? gap : 0, ...style } as CSSProperties}\n >\n <span className={styles.Marker} data-marker={marker}>\n {icon}\n </span>\n <div className={styles.Content}>\n {typeof children === \"string\" ? <Text>{children}</Text> : children}\n </div>\n </li>\n );\n};\n","/*\n * The \"Did you mean …?\" line under the field. It sits where the helper text\n * does and is set like it (field-parts.module.css), so the two read as one\n * column. The address in it is a `Link` rendered as a button.\n */\n\n.Suggestion {\n /* Present even when empty: a live region only announces changes to an\n element that is already in the page. Empty, it takes no room. */\n margin: 0;\n}\n\n.Suggestion p {\n /* 14px, where the text inside a field starts. */\n margin-inline: calc(var(--lea-spacing) * 1.75);\n margin-block: var(--lea-spacing-0-5) 0;\n color: var(--lea-color-neutral-600);\n font-size: var(--lea-text-body-sm-font-size);\n font-weight: var(--lea-text-body-sm-font-weight);\n letter-spacing: var(--lea-text-body-sm-letter-spacing);\n line-height: var(--lea-text-body-sm-line-height);\n overflow-wrap: anywhere;\n}\n","\"use client\";\n\nimport { Autocomplete as BaseAutocomplete } from \"@base-ui/react/autocomplete\";\nimport { useMemo, useRef, useState } from \"react\";\n\nimport { createTranslation } from \"../../../i18n\";\nimport {\n COMMON_EMAIL_DOMAINS,\n getEmailDomainSuggestions,\n suggestEmailCorrection,\n} from \"../../../validation\";\nimport { Link } from \"../../typography\";\nimport { ComboField, type ComboFieldBaseProps } from \"../combo-field\";\nimport styles from \"./email-input.module.css\";\nimport listStyles from \"../option-list.module.css\";\n\nexport type EmailInputProps = ComboFieldBaseProps & {\n /** The address (controlled). Pair with `onValueChange`. */\n value?: string;\n /** The address when first rendered, for uncontrolled use. */\n defaultValue?: string;\n /** Called with the new text on every change, and with a suggestion when one is picked. */\n onValueChange?: (value: string) => void;\n /** Domains offered once the user types `@`. Defaults to common providers. */\n domains?: readonly string[];\n /** Most suggestions shown at once. @default 6 */\n maxSuggestions?: number;\n /**\n * When focus leaves the field, offers a fix for a mistyped domain below it:\n * \"Mente du anna@gmail.com?\" for `anna@gnail.con`, with the address as a\n * button that puts it in the field. The value never changes by itself, and\n * validation is not affected. See `suggestEmailCorrection`.\n * @default true\n */\n suggestCorrection?: boolean;\n /**\n * What the browser may fill in. The default lets it offer the user's own\n * address; pass `\"off\"` when the address is somebody else's.\n * @default \"email\"\n */\n autoComplete?: string;\n};\n\n/**\n * Email text field that suggests common domains in a list once the user\n * types `@`, on Base UI's `Autocomplete`, in the same box as `Input`. The\n * text is the value: a suggestion only saves typing.\n *\n * ```tsx\n * <EmailInput label=\"E-post\" value={email} onValueChange={setEmail} />\n * ```\n */\nexport function EmailInput({\n value,\n defaultValue = \"\",\n onValueChange,\n domains = COMMON_EMAIL_DOMAINS,\n maxSuggestions = 6,\n autoComplete = \"email\",\n suggestCorrection = true,\n name,\n onBlur,\n ref,\n ...field\n}: EmailInputProps) {\n const labels = useEmailInputLabels();\n const isControlled = value !== undefined;\n const [internal, setInternal] = useState(defaultValue);\n const current = isControlled ? value : internal;\n\n const inputRef = useRef<HTMLInputElement | null>(null);\n const setInput = (element: HTMLInputElement | null) => {\n inputRef.current = element;\n if (typeof ref === \"function\") return ref(element);\n if (ref) ref.current = element;\n };\n\n // Worked out when focus leaves the field, and gone as soon as the text\n // changes, so it never interrupts typing.\n const [correction, setCorrection] = useState<string>();\n const shownCorrection =\n suggestCorrection && !field.disabled ? correction : undefined;\n\n const change = (next: string) => {\n if (!isControlled) setInternal(next);\n setCorrection(undefined);\n onValueChange?.(next);\n };\n\n const acceptCorrection = () => {\n if (correction == null) return;\n change(correction);\n // The button goes away; focus goes back to the field, not to the page.\n inputRef.current?.focus();\n };\n\n // There is no \"no suggestions\" text, so with nothing to suggest the list is\n // closed, not open and empty.\n const [open, setOpen] = useState(false);\n\n const suggestions = useMemo(\n () => getEmailDomainSuggestions(current, domains).slice(0, maxSuggestions),\n [current, domains, maxSuggestions],\n );\n\n return (\n <ComboField\n {...field}\n ref={setInput}\n onBlur={(event) => {\n if (suggestCorrection)\n setCorrection(suggestEmailCorrection(current, domains));\n onBlur?.(event);\n }}\n afterHelperText={\n suggestCorrection && (\n <div role=\"status\" className={styles.Suggestion}>\n {shownCorrection != null && (\n <p>\n {labels.didYouMean}{\" \"}\n <Link\n render={<button type=\"button\" />}\n color=\"primary\"\n aria-label={`${labels.use} ${shownCorrection}`}\n onClick={acceptCorrection}\n >\n {shownCorrection}\n </Link>\n ?\n </p>\n )}\n </div>\n )\n }\n clearable\n inputProps={{\n type: \"email\",\n name,\n autoComplete,\n autoCapitalize: \"none\",\n spellCheck: false,\n }}\n renderRoot={(children) => (\n <BaseAutocomplete.Root\n items={suggestions}\n // The suggestions are already made from the text, so nothing is filtered out.\n filter={null}\n open={open && suggestions.length > 0}\n onOpenChange={setOpen}\n value={current}\n onValueChange={change}\n disabled={field.disabled}\n required={field.required}\n highlightItemOnHover={false}\n >\n {children}\n </BaseAutocomplete.Root>\n )}\n >\n <BaseAutocomplete.List className={listStyles.List}>\n {(suggestion: string) => (\n <BaseAutocomplete.Item\n key={suggestion}\n value={suggestion}\n className={listStyles.Item}\n >\n <span className={listStyles.ItemText}>{suggestion}</span>\n </BaseAutocomplete.Item>\n )}\n </BaseAutocomplete.List>\n </ComboField>\n );\n}\n\nconst useEmailInputLabels = createTranslation({\n didYouMean: {\n no: \"Mente du\",\n se: \"Menade du\",\n fi: \"Tarkoititko\",\n es: \"¿Quisiste decir\",\n },\n use: {\n no: \"Bruk\",\n se: \"Använd\",\n fi: \"Käytä\",\n es: \"Usar\",\n },\n});\n","\"use client\";\n\nimport { Input as BaseInput } from \"@base-ui/react/input\";\nimport {\n useEffect,\n useId,\n useLayoutEffect,\n useRef,\n type ComponentPropsWithoutRef,\n type CSSProperties,\n type MouseEvent,\n type ReactNode,\n type Ref,\n} from \"react\";\n\nimport {\n FieldGroupLabel,\n FieldHelperText,\n fieldMessage,\n RequiredAsterisk,\n} from \"../field-parts\";\nimport styles from \"../input.module.css\";\n\ntype NativeInputProps = Omit<\n ComponentPropsWithoutRef<\"input\">,\n | \"value\"\n | \"defaultValue\"\n | \"size\"\n | \"color\"\n | \"children\"\n | \"className\"\n | \"style\"\n>;\n\nexport type InputProps = NativeInputProps & {\n /** The label, linked to the input for you. */\n label?: ReactNode;\n /** Floating inside the field, or as a line above it. @default \"inside\" */\n labelPlacement?: \"inside\" | \"outside\";\n /**\n * Help text behind an info button next to the label. Needs\n * `labelPlacement=\"outside\"`: a floating label has no room for it.\n */\n tooltip?: ReactNode;\n /** Helper / error text shown below the input and linked via `aria-describedby`. */\n helperText?: ReactNode;\n /** Puts the input, its label and its helper text into the error state. */\n error?: boolean;\n /**\n * A message for a value that is allowed but unusual, such as an unusually\n * high amount. Shown in amber in place of the helper text, without the\n * error state: the field is not marked invalid. `error` wins when both are set.\n */\n warning?: ReactNode;\n /** The text (controlled). Pair with `onValueChange`. */\n value?: string;\n /** The text when first rendered, for uncontrolled use. */\n defaultValue?: string;\n /** Called with the new text on every change. `onChange` still gets the event. */\n onValueChange?: (value: string) => void;\n /** Content before the text, inside the field, e.g. an icon. */\n startAdornment?: ReactNode;\n /** Content after the text, inside the field, e.g. a show / hide button. */\n endAdornment?: ReactNode;\n /**\n * Renders a `<textarea>` that grows with its content. The native attributes\n * and event handlers then apply to the textarea.\n */\n multiline?: boolean;\n /** Fixed number of rows when `multiline`. */\n rows?: number;\n /** Fewest rows shown when `multiline`. */\n minRows?: number;\n /** Most rows shown when `multiline`, before it scrolls. */\n maxRows?: number;\n /** Class on the outermost element, for layout. */\n className?: string;\n /** Inline style on the outermost element, for layout. */\n style?: CSSProperties;\n /** The `<input>`, or the `<textarea>` when `multiline`. */\n ref?: Ref<HTMLInputElement | HTMLTextAreaElement>;\n};\n\nconst join = (...parts: (string | false | null | undefined)[]) =>\n parts.filter(Boolean).join(\" \") || undefined;\n\n// `useLayoutEffect` warns when a component is rendered on the server.\nconst useIsoLayoutEffect =\n typeof document === \"undefined\" ? useEffect : useLayoutEffect;\n\n/**\n * Sizes a textarea to its content, between `rows` (its `minRows`) and\n * `maxRows`. It listens to the element itself, so it also follows typing in\n * an uncontrolled textarea, which causes no render.\n */\nfunction useAutosize(enabled: boolean, maxRows?: number) {\n const elementRef = useRef<HTMLElement | null>(null);\n\n const resize = () => {\n const element = elementRef.current;\n if (!element) return;\n\n // Back to the height the `rows` attribute gives, to measure from there.\n element.style.height = \"auto\";\n const computed = getComputedStyle(element);\n const max =\n maxRows != null\n ? parseFloat(computed.lineHeight) * maxRows +\n parseFloat(computed.paddingTop) +\n parseFloat(computed.paddingBottom)\n : Infinity;\n const content = element.scrollHeight;\n element.style.height = `${Math.min(content, max)}px`;\n element.style.overflowY = content > max ? \"auto\" : \"hidden\";\n };\n\n useIsoLayoutEffect(() => {\n if (enabled) resize();\n });\n\n useEffect(() => {\n const element = elementRef.current;\n if (!enabled || !element) return;\n\n element.addEventListener(\"input\", resize);\n // The lines rewrap when the width changes.\n const observer =\n typeof ResizeObserver === \"undefined\" ? null : new ResizeObserver(resize);\n observer?.observe(element);\n return () => {\n element.removeEventListener(\"input\", resize);\n observer?.disconnect();\n };\n // `resize` reads nothing but the element and `maxRows`.\n }, [enabled, maxRows]);\n\n return elementRef;\n}\n\n/**\n * Text input, on Base UI's `Input` primitive. The label, the helper text and\n * the ids that link them to the input are all rendered and wired for you.\n *\n * Takes the native `<input>` attributes (`type`, `name`, `placeholder`,\n * `autoComplete`, `inputMode`, `maxLength`, `onKeyDown`, …), which land on the\n * input itself. `className` and `style` go on the outermost element. Pass\n * `multiline` (and `minRows`) for a textarea. Set `labelPlacement=\"outside\"`\n * for a label above the field, which can also carry a `tooltip`.\n *\n * ```tsx\n * <Input\n * label=\"E-post\"\n * helperText=\"Vi deler den aldri.\"\n * value={email}\n * onValueChange={setEmail}\n * />\n * ```\n */\nexport function Input({\n label,\n labelPlacement = \"inside\",\n tooltip,\n helperText,\n error = false,\n warning,\n value,\n defaultValue,\n onValueChange,\n startAdornment,\n endAdornment,\n multiline = false,\n rows,\n minRows,\n maxRows,\n className,\n style,\n ref,\n id,\n disabled = false,\n required = false,\n placeholder,\n \"aria-describedby\": ariaDescribedBy,\n ...inputAttributes\n}: InputProps) {\n const generatedId = useId();\n const inputId = id ?? generatedId;\n const labelId = useId();\n const helperTextId = useId();\n const hasLabel = label != null;\n const labelOutside = hasLabel && labelPlacement === \"outside\";\n const message = fieldMessage(helperText, warning, error);\n const hasHelperText = message.content != null;\n\n // A fixed `rows` is left alone; otherwise the textarea follows its content.\n const autosize = multiline && rows == null;\n const autosizeRef = useAutosize(autosize, maxRows);\n\n const setControl = (element: HTMLInputElement | null) => {\n autosizeRef.current = element;\n if (typeof ref === \"function\") return ref(element);\n if (ref) ref.current = element;\n };\n\n // The label, the padding and the adornments are part of the field, so a\n // click on any of them goes to the control, unless it hit something that\n // takes clicks itself (a show / hide button in an adornment).\n const focusControl = (event: MouseEvent<HTMLDivElement>) => {\n const target = event.target as Element;\n if (target.closest(\"button, a, input, textarea, select, [tabindex]\"))\n return;\n event.currentTarget.querySelector<HTMLElement>(\"input, textarea\")?.focus();\n };\n\n return (\n <div className={join(styles.Field, className)} style={style}>\n {labelOutside && (\n <FieldGroupLabel\n id={labelId}\n htmlFor={inputId}\n tooltip={tooltip}\n required={required}\n error={error}\n disabled={disabled}\n >\n {label}\n </FieldGroupLabel>\n )}\n <div\n className={styles.Box}\n data-invalid={error ? \"\" : undefined}\n data-disabled={disabled ? \"\" : undefined}\n data-has-label={hasLabel && !labelOutside ? \"\" : undefined}\n data-label-outside={labelOutside ? \"\" : undefined}\n onClick={focusControl}\n >\n {startAdornment != null && (\n <span className={styles.Adornment} data-position=\"start\">\n {startAdornment}\n </span>\n )}\n <div className={styles.Inner}>\n {hasLabel && !labelOutside && (\n <label htmlFor={inputId} className={styles.Label}>\n {label}\n {required && <RequiredAsterisk />}\n </label>\n )}\n <BaseInput\n {...inputAttributes}\n ref={setControl}\n id={inputId}\n value={value}\n defaultValue={defaultValue}\n onValueChange={(next) => onValueChange?.(next)}\n disabled={disabled}\n required={required}\n // Never empty: `:placeholder-shown` is how the stylesheet tells an\n // empty control from a filled one, and it needs a placeholder to work.\n placeholder={placeholder || \" \"}\n aria-invalid={error}\n aria-describedby={join(\n hasHelperText && helperTextId,\n ariaDescribedBy,\n )}\n className={styles.Control}\n render={\n multiline ? <textarea rows={rows ?? minRows ?? 1} /> : undefined\n }\n />\n </div>\n {endAdornment != null && (\n <span className={styles.Adornment} data-position=\"end\">\n {endAdornment}\n </span>\n )}\n </div>\n {hasHelperText && (\n <FieldHelperText\n id={helperTextId}\n error={error}\n warning={message.warning}\n >\n {message.content}\n </FieldHelperText>\n )}\n </div>\n );\n}\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n *\n * Shared by Select and MultiSelect. The box, its border states and the\n * adornments come from input.module.css, and the list from\n * option-list.module.css; this file adds the trigger, the label (which floats\n * on a value, not on focus) and the arrow.\n */\n\n/*\n * `.Field` in front of a selector lifts it over the rules of input.module.css\n * for the same element, whichever of the two stylesheets loads last.\n */\n.Field .Box {\n /* White, where Input shows the surface it sits on. As it always was. */\n background-color: var(--lea-palette-bg-base);\n cursor: pointer;\n}\n\n.Field .Box[data-disabled] {\n cursor: not-allowed;\n}\n\n/* The list is in a portal, so focus is not inside the box while it is open. */\n.Field .Box[data-open]:not([data-invalid]) {\n --input-border-color: var(--lea-palette-border-accent);\n --input-border-width: 2px;\n}\n\n/* The field and its open list read as one shape: square where they meet. */\n.Box[data-popup-side=\"bottom\"] {\n border-end-end-radius: 0;\n border-end-start-radius: 0;\n}\n\n.Box[data-popup-side=\"top\"] {\n border-start-end-radius: 0;\n border-start-start-radius: 0;\n}\n\n/* An empty layer over the whole box: a click anywhere in the field opens the list. */\n.Trigger {\n position: absolute;\n border-radius: inherit;\n cursor: inherit;\n inset: 0;\n /* The border of the box is the focus indicator, as in Input. */\n outline: none;\n}\n\n/* What the field shows lies over the trigger and lets clicks through to it. */\n.Box > :not(.Trigger) {\n position: relative;\n pointer-events: none;\n}\n\n.Box :is(button, a, input) {\n pointer-events: auto;\n}\n\n.Inner {\n flex-direction: column;\n}\n\n/* The value, or the chips. One line: a long value is cut off with an ellipsis. */\n.Display {\n overflow: hidden;\n min-height: calc(var(--lea-text-body-md-line-height) * 1em);\n box-sizing: content-box;\n padding-block: var(--input-padding-top) calc(var(--lea-spacing) * 0.75);\n padding-inline: var(--input-inset-start) var(--lea-spacing-0-5);\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.Placeholder {\n color: var(--lea-palette-text-muted);\n}\n\n/* While the label rests over the field the placeholder is hidden, so the two never overlap. */\n.Box[data-has-label]:not(:focus-within, [data-open]) .Placeholder {\n visibility: hidden;\n}\n\n/*\n * The label of Input, with one difference: it floats when there is a value or\n * a placeholder to make room for, not on focus alone. An empty select has\n * nothing to show under a floated label.\n */\n.Label {\n position: absolute;\n z-index: 1;\n overflow: hidden;\n max-width: calc(100% - var(--input-inset-start));\n /*\n * neutral-600 rather than text-subtle: a field has no background of its\n * own, and text-subtle falls below 4.5:1 on the tinted surfaces it can sit on.\n */\n color: var(--lea-color-neutral-600);\n inset-block-start: 0;\n inset-inline-start: var(--input-inset-start);\n scale: 1;\n text-overflow: ellipsis;\n transform-origin: top left;\n /* Resting, the label is centred in the field, where the value will be. */\n translate: 0\n calc((var(--input-height) - var(--lea-text-body-md-line-height) * 1em) / 2);\n transition: color var(--lea-duration-normal) var(--lea-easing-standard),\n scale var(--lea-duration-normal) var(--lea-easing-standard),\n translate var(--lea-duration-normal) var(--lea-easing-standard);\n white-space: nowrap;\n}\n\n.Box[data-filled] .Label,\n.Box[data-has-placeholder]:focus-within .Label,\n.Box[data-has-placeholder][data-open] .Label {\n max-width: calc((100% - var(--input-inset-start)) / 0.75);\n scale: 0.75;\n translate: 0 calc(var(--lea-spacing) * 0.75);\n}\n\n.Box:focus-within .Label,\n.Box[data-open] .Label {\n color: var(--lea-palette-text-accent);\n}\n\n.Box[data-invalid] .Label {\n color: var(--lea-palette-status-error-base);\n}\n\n.Box[data-disabled] .Label {\n color: var(--lea-palette-text-muted);\n}\n\n.Icon {\n width: 1.25rem;\n height: 1.25rem;\n flex-shrink: 0;\n align-self: center;\n margin-inline: var(--lea-spacing-0-5) var(--input-padding-inline);\n color: var(--lea-palette-text-base);\n transition: rotate var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n.Box[data-open] .Icon {\n rotate: 180deg;\n}\n\n.Box[data-disabled] .Icon {\n color: var(--lea-palette-text-muted);\n}\n\n.EndAdornment {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n align-self: center;\n color: var(--lea-palette-text-subtle);\n margin-inline-start: var(--lea-spacing-1);\n white-space: nowrap;\n}\n\n.SrOnly {\n position: absolute;\n overflow: hidden;\n width: 1px;\n height: 1px;\n padding: 0;\n border: 0;\n margin: -1px;\n clip-path: inset(50%);\n white-space: nowrap;\n}\n\n/*\n * `size=\"sm\"`: a compact field, 32px tall and 8px in at the sides, the\n * website's country select. The label is a line above it (`FieldGroupLabel`),\n * so the value is centred and nothing floats; an icon at the start is centred\n * with it.\n */\n.Box[data-size=\"sm\"] {\n --input-height: var(--lea-spacing-4);\n --input-padding-inline: var(--lea-spacing-1);\n}\n\n.Box[data-size=\"sm\"] .Display {\n padding-block: calc(\n (var(--input-height) - var(--lea-text-body-md-line-height) * 1em) / 2\n );\n}\n\n/* Before `.Adornment` in input.module.css: `.Field` lifts it over that rule. */\n.Field .Box[data-size=\"sm\"] > [data-position=\"start\"] {\n margin-block-start: 0;\n margin-inline: var(--lea-spacing-1);\n}\n\n/* The icon of an option (a flag), in the list and in the field. */\n.OptionIcon {\n display: flex;\n width: 1.25rem;\n height: 1.25rem;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n}\n\n.OptionIcon > :is(svg, img) {\n width: 100%;\n height: 100%;\n}\n\n/* MultiSelect: the chips, on one line. */\n.Chips {\n position: relative;\n display: flex;\n min-width: 0;\n}\n\n.ChipRow {\n display: flex;\n overflow: hidden;\n min-width: 0;\n flex: 1;\n flex-wrap: nowrap;\n align-items: center;\n gap: var(--lea-spacing-0-5);\n}\n\n/* Every chip and a badge, laid out unseen to be measured. */\n.ChipMeasure {\n position: absolute;\n display: flex;\n overflow: hidden;\n height: 0;\n gap: var(--lea-spacing-0-5);\n inset-block-start: 0;\n inset-inline-start: 0;\n visibility: hidden;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Label,\n .Icon {\n transition-duration: 0.01ms;\n }\n}\n","\"use client\";\n\nimport { Select as BaseSelect } from \"@base-ui/react/select\";\nimport { ChevronDown } from \"lucide-react\";\nimport {\n useEffect,\n useId,\n useRef,\n useState,\n type ComponentProps,\n type CSSProperties,\n type FocusEvent,\n type ReactNode,\n type Ref,\n} from \"react\";\n\nimport {\n FieldGroupLabel,\n FieldHelperText,\n RequiredAsterisk,\n} from \"./field-parts\";\nimport inputStyles from \"./input.module.css\";\nimport listStyles from \"./option-list.module.css\";\nimport styles from \"./select.module.css\";\nimport { useBlurOutside } from \"./use-blur-outside\";\n\n/*\n * Internal. The field that `Select` and `MultiSelect` share: the box of\n * `Input`, a trigger with the `combobox` role, and the list in a popup, on\n * Base UI's `Select`. Not part of the public API, so this file isn't\n * re-exported from `index.ts`.\n *\n * The trigger is an empty layer over the whole box, so a click anywhere in\n * the field opens the list. What the field shows (the label, the value or the\n * chips, the adornments, the arrow) lies on top of it and lets clicks through.\n * That keeps the remove buttons of `MultiSelect`'s chips out of the trigger:\n * a control inside a control is invalid, and its keys would open the list.\n */\n\n/** One choice in a `Select` or `MultiSelect`. */\nexport type SelectOption<Value extends string = string> = {\n value: Value;\n label: string;\n /** Shown, but not selectable. */\n disabled?: boolean;\n /**\n * A picture before the label, 20px: a flag, a logo. Decoration, hidden\n * from assistive tech, so the label has to say it all. In a `Select` the\n * chosen option's icon also shows in the field, unless `startAdornment`\n * takes its place.\n */\n icon?: ReactNode;\n};\n\n/**\n * `\"md\"`: the 54px field with a floating label. `\"sm\"`: a compact 32px field\n * for a narrow place (the country list in the website's footer), with its\n * label as a line above it.\n */\nexport type SelectSize = \"sm\" | \"md\";\n\n/** The props `Select` and `MultiSelect` have in common. */\nexport type SelectFieldBaseProps = {\n /** Floating label, linked to the select for you. */\n label?: ReactNode;\n /** Helper / error text shown below the control and linked via `aria-describedby`. */\n helperText?: ReactNode;\n /** Puts the select, its label and its helper text into the error state. */\n error?: boolean;\n /** Marks the select as required, with an asterisk after the label. */\n required?: boolean;\n disabled?: boolean;\n /**\n * Text shown while nothing is chosen. With a `label` it appears once the\n * label has floated up: on focus and while the list is open.\n */\n placeholder?: string;\n /** Content before the value, inside the field, e.g. an icon. */\n startAdornment?: ReactNode;\n /** Content after the value, inside the field, left of the dropdown arrow. */\n endAdornment?: ReactNode;\n /** Whether the list is open (controlled). Pair with `onOpenChange`. */\n open?: boolean;\n /** Called when the list opens or closes. */\n onOpenChange?: (open: boolean) => void;\n /** Called when focus leaves the select, the open list included. */\n onBlur?: (event: FocusEvent<HTMLElement>) => void;\n /** Submits the value under this name in a native form. */\n name?: string;\n /** Id of the element with the `combobox` role. */\n id?: string;\n /** Names the select when there is no visible `label`. */\n \"aria-label\"?: string;\n \"aria-labelledby\"?: string;\n \"aria-describedby\"?: string;\n /** Class on the outermost element, for layout. */\n className?: string;\n /** Inline style on the outermost element, for layout. */\n style?: CSSProperties;\n /** The element that takes focus, the one with the `combobox` role. */\n ref?: Ref<HTMLElement>;\n};\n\ntype SelectFieldProps = SelectFieldBaseProps & {\n /** `Select` only; `MultiSelect` is always `md`. */\n size?: SelectSize;\n multiple: boolean;\n /** The value as Base UI holds it: `null` or an array when empty. */\n value: string | string[] | null;\n onValueChange: (value: string | string[] | null) => void;\n /** Whether something is chosen, which is what floats the label. */\n filled: boolean;\n /** What the field shows for the value: text, or the chips. */\n display: ReactNode;\n /**\n * Hides `display` from assistive tech, when it only repeats `valueText`.\n * Not for chips: their remove buttons are controls.\n */\n displayHidden?: boolean;\n /** The value as plain text, for assistive tech, inside the trigger. */\n valueText: string;\n /** The `SelectFieldItem`s. */\n children: ReactNode;\n};\n\nconst join = (...parts: (string | false | null | undefined)[]) =>\n parts.filter(Boolean).join(\" \") || undefined;\n\ntype Side = \"top\" | \"bottom\";\n\ntype PositionerElementProps = ComponentProps<\"div\"> & {\n side: string;\n onSideChange: (side: Side) => void;\n};\n\n/**\n * The popup's positioner, which also reports the side it ended up on. The\n * field squares the corners on that side so the two read as one shape, and\n * the popup is in a portal where the field's CSS cannot see it.\n */\nfunction PositionerElement({\n side,\n onSideChange,\n ...props\n}: PositionerElementProps) {\n useEffect(() => {\n onSideChange(side === \"top\" ? \"top\" : \"bottom\");\n }, [side, onSideChange]);\n\n return <div {...props} />;\n}\n\nexport function SelectField({\n label,\n helperText,\n error = false,\n required = false,\n disabled = false,\n placeholder,\n startAdornment,\n endAdornment,\n open: openProp,\n onOpenChange,\n onBlur,\n name,\n id,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n \"aria-describedby\": ariaDescribedBy,\n className,\n style,\n ref,\n size = \"md\",\n multiple,\n value,\n onValueChange,\n filled,\n display,\n displayHidden = false,\n valueText,\n children,\n}: SelectFieldProps) {\n const generatedId = useId();\n const triggerId = id ?? generatedId;\n const labelId = useId();\n const helperTextId = useId();\n const hasLabel = label != null;\n // A 32px field has no room for a label to float in: it goes above.\n const labelOutside = hasLabel && size === \"sm\";\n const hasHelperText = helperText != null;\n\n const [internalOpen, setInternalOpen] = useState(false);\n const open = openProp ?? internalOpen;\n const [side, setSide] = useState<Side>(\"bottom\");\n\n const boxRef = useRef<HTMLDivElement>(null);\n const popupRef = useRef<HTMLDivElement>(null);\n\n // Opening the list moves focus from the trigger into the popup, and\n // choosing moves it back. Neither is leaving the select.\n const handleBlur = useBlurOutside(onBlur, [boxRef, popupRef]);\n\n return (\n <div\n className={join(inputStyles.Field, styles.Field, className)}\n style={style}\n >\n {labelOutside && (\n <FieldGroupLabel\n id={labelId}\n required={required}\n error={error}\n disabled={disabled}\n >\n {label}\n </FieldGroupLabel>\n )}\n <BaseSelect.Root\n id={triggerId}\n name={name}\n multiple={multiple}\n value={value}\n onValueChange={onValueChange}\n open={open}\n onOpenChange={(next) => {\n if (openProp === undefined) setInternalOpen(next);\n onOpenChange?.(next);\n }}\n disabled={disabled}\n required={required}\n // The keyboard and the mouse each mark their own option, as in a\n // native list: hovering does not move the option the arrow keys are on.\n highlightItemOnHover={false}\n >\n <div\n ref={boxRef}\n className={join(inputStyles.Box, styles.Box)}\n data-invalid={error ? \"\" : undefined}\n data-disabled={disabled ? \"\" : undefined}\n data-size={size === \"md\" ? undefined : size}\n data-has-label={hasLabel && !labelOutside ? \"\" : undefined}\n data-has-placeholder={placeholder ? \"\" : undefined}\n data-filled={filled ? \"\" : undefined}\n data-open={open ? \"\" : undefined}\n data-popup-side={open ? side : undefined}\n onBlur={handleBlur}\n >\n <BaseSelect.Trigger\n // Rendered as a `div`, see above; Base UI types the ref for a button.\n ref={ref as Ref<HTMLButtonElement>}\n nativeButton={false}\n render={<div />}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy ?? (hasLabel ? labelId : undefined)}\n aria-describedby={join(\n hasHelperText && helperTextId,\n ariaDescribedBy,\n )}\n aria-invalid={error || undefined}\n aria-required={required || undefined}\n className={styles.Trigger}\n >\n <span className={styles.SrOnly}>{valueText}</span>\n </BaseSelect.Trigger>\n {startAdornment != null && (\n <span className={inputStyles.Adornment} data-position=\"start\">\n {startAdornment}\n </span>\n )}\n <div className={join(inputStyles.Inner, styles.Inner)}>\n {hasLabel && !labelOutside && (\n <span id={labelId} className={styles.Label}>\n {label}\n {required && <RequiredAsterisk />}\n </span>\n )}\n <div\n className={styles.Display}\n aria-hidden={displayHidden || undefined}\n >\n {filled\n ? display\n : placeholder && (\n <span className={styles.Placeholder} aria-hidden>\n {placeholder}\n </span>\n )}\n </div>\n </div>\n {endAdornment != null && (\n <span className={styles.EndAdornment}>{endAdornment}</span>\n )}\n <ChevronDown className={styles.Icon} aria-hidden />\n </div>\n <BaseSelect.Portal>\n <BaseSelect.Positioner\n side=\"bottom\"\n align=\"start\"\n sideOffset={0}\n alignItemWithTrigger={false}\n className={listStyles.Positioner}\n render={(props, state) => (\n <PositionerElement\n {...props}\n side={state.side}\n onSideChange={setSide}\n />\n )}\n >\n <BaseSelect.Popup\n ref={popupRef}\n className={listStyles.Popup}\n onBlur={handleBlur}\n >\n <BaseSelect.List className={listStyles.List}>\n {children}\n </BaseSelect.List>\n </BaseSelect.Popup>\n </BaseSelect.Positioner>\n </BaseSelect.Portal>\n </BaseSelect.Root>\n {hasHelperText && (\n <FieldHelperText id={helperTextId} error={error}>\n {helperText}\n </FieldHelperText>\n )}\n </div>\n );\n}\n\ntype SelectFieldItemProps = {\n option: SelectOption;\n disabled?: boolean;\n /** Content before the label, e.g. the checkbox of `MultiSelect`. */\n children?: ReactNode;\n};\n\n/** One option in the list. */\nexport function SelectFieldItem({\n option,\n disabled,\n children,\n}: SelectFieldItemProps) {\n return (\n <BaseSelect.Item\n value={option.value}\n label={option.label}\n disabled={disabled ?? option.disabled}\n className={listStyles.Item}\n >\n {children}\n {option.icon != null && <OptionIcon>{option.icon}</OptionIcon>}\n <BaseSelect.ItemText className={listStyles.ItemText}>\n {option.label}\n </BaseSelect.ItemText>\n </BaseSelect.Item>\n );\n}\n\n/** The icon of an option, in the list or in the field: 20px, decoration. */\nexport function OptionIcon({ children }: { children: ReactNode }) {\n return (\n <span aria-hidden className={styles.OptionIcon}>\n {children}\n </span>\n );\n}\n","\"use client\";\n\nimport {\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n type ReactNode,\n type Ref,\n} from \"react\";\n\nimport { Chip } from \"../../data-display/chip/chip\";\nimport { CheckboxBox } from \"../checkbox-box\";\nimport {\n SelectField,\n SelectFieldItem,\n type SelectFieldBaseProps,\n type SelectOption,\n} from \"../select-field\";\nimport styles from \"../select.module.css\";\n\ntype SelectedChipsProps = {\n items: SelectOption[];\n canDelete: boolean;\n onDelete: (value: string) => void;\n};\n\n// Gap between chips, in px (`--lea-spacing-0-5` in the stylesheet).\nconst CHIP_GAP = 4;\n\n// `useLayoutEffect` warns when a component is rendered on the server.\nconst useIsoLayoutEffect =\n typeof document === \"undefined\" ? useEffect : useLayoutEffect;\n\n/**\n * Renders the selected values as chips on a single line. When they don't all\n * fit, the overflow is collapsed into a trailing `+N` badge rather than\n * wrapping onto a second line. Widths are measured off-screen so the visible\n * row can reserve room for the badge before deciding how many chips to show.\n */\nfunction SelectedChips({ items, canDelete, onDelete }: SelectedChipsProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n const measureRef = useRef<HTMLDivElement>(null);\n const [visibleCount, setVisibleCount] = useState(items.length);\n\n const recompute = useCallback(() => {\n const container = containerRef.current;\n const measure = measureRef.current;\n if (!container || !measure) return;\n\n const available = container.clientWidth;\n // The measurement row renders every chip followed by a single badge.\n const children = Array.from(measure.children) as HTMLElement[];\n const badgeEl = children[children.length - 1];\n const chipEls = children.slice(0, -1);\n const badgeWidth = badgeEl ? badgeEl.offsetWidth + CHIP_GAP : 0;\n\n const total = chipEls.reduce(\n (sum, el, index) => sum + el.offsetWidth + (index > 0 ? CHIP_GAP : 0),\n 0,\n );\n if (total <= available) {\n setVisibleCount(items.length);\n return;\n }\n\n let used = 0;\n let count = 0;\n for (let i = 0; i < chipEls.length; i++) {\n const next = chipEls[i].offsetWidth + (i > 0 ? CHIP_GAP : 0);\n if (used + next + badgeWidth <= available) {\n used += next;\n count += 1;\n } else {\n break;\n }\n }\n setVisibleCount(count);\n }, [items.length]);\n\n const itemsKey = items.map((item) => item.value).join(\"|\");\n\n useIsoLayoutEffect(() => {\n recompute();\n // Re-measure when the set of chips or the delete affordance changes.\n }, [recompute, itemsKey, canDelete]);\n\n useEffect(() => {\n const container = containerRef.current;\n if (!container || typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(() => recompute());\n observer.observe(container);\n return () => observer.disconnect();\n }, [recompute]);\n\n const hiddenCount = Math.max(items.length - visibleCount, 0);\n\n const renderChip = (item: SelectOption) => (\n <Chip\n key={item.value}\n size=\"sm\"\n label={item.label}\n style={{ flexShrink: 0, maxWidth: \"100%\" }}\n onDelete={canDelete ? () => onDelete(item.value) : undefined}\n />\n );\n\n const badge = (count: number) => (\n <Chip\n color=\"neutral\"\n size=\"sm\"\n label={`+${count}`}\n style={{ flexShrink: 0 }}\n />\n );\n\n return (\n <div className={styles.Chips}>\n {/* Off-screen measurement row: always renders every chip + a badge. */}\n <div ref={measureRef} aria-hidden className={styles.ChipMeasure}>\n {items.map((item) => renderChip(item))}\n {badge(items.length)}\n </div>\n\n <div ref={containerRef} className={styles.ChipRow}>\n {items.slice(0, visibleCount).map((item) => renderChip(item))}\n {hiddenCount > 0 && badge(hiddenCount)}\n </div>\n </div>\n );\n}\n\nexport type MultiSelectProps<Value extends string = string> =\n SelectFieldBaseProps & {\n /** The choices, shown with a checkbox each. */\n options: readonly SelectOption<Value>[];\n /** Currently selected values (controlled). */\n value?: Value[];\n /** Initially selected values (uncontrolled). */\n defaultValue?: Value[];\n /** Called with the next array of selected values. */\n onValueChange?: (value: Value[]) => void;\n /** Minimum number of values that must stay selected. @default 1 */\n min?: number;\n /** Maximum number of values that can be selected. Unlimited when omitted. */\n max?: number;\n /** Override how the selected values are summarised in the closed control. */\n renderValue?: (selected: Value[]) => ReactNode;\n };\n\n/**\n * Multi-select on Base UI's `Select`, in the same box as `Select`, with the\n * label, the helper text and their ids wired. Each option has a checkbox, and\n * the closed control shows the selection as removable chips by default.\n *\n * The selection is bounded by `min` (default 1, so the last value can't be\n * removed) and `max` (unlimited by default): options that would breach either\n * bound are disabled, and the constraints are also enforced on change.\n *\n * Works controlled (`value`) or uncontrolled (`defaultValue`).\n */\nexport function MultiSelect<Value extends string = string>({\n options,\n value,\n defaultValue = [],\n onValueChange,\n renderValue,\n min = 1,\n max,\n disabled = false,\n ref,\n ...rest\n}: MultiSelectProps<Value>) {\n const isControlled = value !== undefined;\n const [internal, setInternal] = useState<Value[]>(defaultValue);\n const selected = isControlled ? value : internal;\n\n const triggerRef = useRef<HTMLElement | null>(null);\n const setTrigger: Ref<HTMLElement> = (element) => {\n triggerRef.current = element;\n if (typeof ref === \"function\") return ref(element);\n if (ref) ref.current = element;\n };\n\n const atMax = max !== undefined && selected.length >= max;\n const atMin = selected.length <= min;\n\n const commit = (next: Value[]) => {\n if (!isControlled) setInternal(next);\n onValueChange?.(next);\n };\n\n const handleChange = (next: Value[]) => {\n // Enforce the bounds even if a disabled option is somehow toggled.\n const isAdding = next.length > selected.length;\n if (isAdding && max !== undefined && next.length > max) return;\n const isRemoving = next.length < selected.length;\n if (isRemoving && next.length < min) return;\n\n commit(next);\n };\n\n const handleDelete = (valueToRemove: string) => {\n if (selected.length <= min) return;\n commit(selected.filter((v) => v !== valueToRemove));\n // The button that was pressed is gone; keep focus in the field.\n triggerRef.current?.focus();\n };\n\n const canDelete = !disabled && selected.length > min;\n\n // In the order they were chosen, like the chips of a tag field.\n const selectedOptions = selected.map(\n (v): SelectOption =>\n options.find((option) => option.value === v) ?? { value: v, label: v },\n );\n\n return (\n <SelectField\n {...rest}\n ref={setTrigger}\n disabled={disabled}\n multiple\n value={selected}\n onValueChange={(next) => handleChange((next ?? []) as Value[])}\n filled={selected.length > 0}\n display={\n renderValue ? (\n renderValue(selected)\n ) : (\n <SelectedChips\n items={selectedOptions}\n canDelete={canDelete}\n onDelete={handleDelete}\n />\n )\n }\n valueText={selectedOptions.map((option) => option.label).join(\", \")}\n >\n {options.map((option) => {\n const isSelected = selected.includes(option.value);\n const atBound = (atMax && !isSelected) || (atMin && isSelected);\n return (\n <SelectFieldItem\n key={option.value}\n option={option}\n disabled={atBound || option.disabled}\n >\n {/* A picture of the state. The option itself is what gets picked. */}\n <CheckboxBox checked={isSelected} />\n </SelectFieldItem>\n );\n })}\n </SelectField>\n );\n}\n","export type Country = \"NO\" | \"SE\" | \"FI\" | \"ES\";\n\nexport type Currency = \"NOK\" | \"SEK\" | \"EUR\";\n\nexport interface CountryConfig {\n country: Country;\n locale: string;\n currency: Currency;\n}\n\nexport const COUNTRIES: Record<Country, CountryConfig> = {\n NO: { country: \"NO\", locale: \"nb-NO\", currency: \"NOK\" },\n SE: { country: \"SE\", locale: \"sv-SE\", currency: \"SEK\" },\n FI: { country: \"FI\", locale: \"fi-FI\", currency: \"EUR\" },\n ES: { country: \"ES\", locale: \"es-ES\", currency: \"EUR\" },\n};\n","import { isValidElement, type ReactNode } from \"react\";\n\nimport {\n COUNTRIES,\n type Country,\n type Currency,\n} from \"../../../hooks/country/countries\";\nimport type { Language } from \"../../../i18n\";\n\n/**\n * A unit the library writes for you, in the language of `LeaProvider`:\n *\n * - `{ type: \"years\" }`: \"år\", \"vuotta\", \"años\". The word follows the number\n * in the field, so Finnish and Spanish read \"1 vuosi\" and \"2 vuotta\",\n * \"1 año\" and \"2 años\".\n * - `{ type: \"currency\" }`: the symbol of the currency, \"kr\" or \"€\". The\n * currency is the one of the language (`no` NOK, `se` SEK, `fi` and `es`\n * EUR); pass `currency` for another.\n */\nexport type NumberInputUnitSpec =\n { type: \"years\" } | { type: \"currency\"; currency?: Currency };\n\n/** What `NumberInput` takes as `unit`: your own content, or a unit spec. */\nexport type NumberInputUnit = ReactNode | NumberInputUnitSpec;\n\nconst COUNTRY_OF_LANGUAGE: Record<Language, Country> = {\n no: \"NO\",\n se: \"SE\",\n fi: \"FI\",\n es: \"ES\",\n};\n\n/** A spec, as opposed to a string, a number or an element. */\nexport function isUnitSpec(unit: NumberInputUnit): unit is NumberInputUnitSpec {\n return (\n typeof unit === \"object\" &&\n unit !== null &&\n !isValidElement(unit) &&\n \"type\" in unit &&\n (unit.type === \"years\" || unit.type === \"currency\")\n );\n}\n\n/**\n * The text of a unit spec for a language. `value` picks the plural of \"years\"\n * and is ignored for a currency; an empty field counts as zero.\n */\nexport function formatUnit(\n unit: NumberInputUnitSpec,\n language: Language,\n value: number | null | undefined,\n): string {\n const { locale, currency: languageCurrency } =\n COUNTRIES[COUNTRY_OF_LANGUAGE[language]];\n\n const options: Intl.NumberFormatOptions =\n unit.type === \"years\"\n ? { style: \"unit\", unit: \"year\", unitDisplay: \"long\" }\n : {\n style: \"currency\",\n currency: unit.currency ?? languageCurrency,\n currencyDisplay: \"narrowSymbol\",\n };\n\n // Only the unit is wanted. `formatToParts` hands it over on its own, in the\n // right plural, without the number next to it.\n const part = new Intl.NumberFormat(locale, options)\n .formatToParts(unit.type === \"years\" ? (value ?? 0) : 0)\n .find((p) => p.type === (unit.type === \"years\" ? \"unit\" : \"currency\"));\n\n return part?.value ?? \"\";\n}\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n *\n * The field itself is the box of `Input`, from input.module.css. This file\n * adds what only a number field has: the unit and the stepper buttons.\n */\n\n/* The unit, set off from the value by a short line. */\n.Unit {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n align-self: center;\n padding-inline-start: var(--lea-spacing-1-5);\n border-inline-start: 1px solid var(--lea-palette-border-subtle);\n margin-inline-end: var(--lea-spacing-1);\n color: var(--lea-palette-text-subtle);\n /* Tighter than body text, which sets how tall the line beside it is. */\n line-height: var(--lea-line-height-tight);\n white-space: nowrap;\n}\n\n.Box[data-disabled] .Unit {\n color: var(--lea-palette-text-muted);\n}\n\n.Steppers {\n display: flex;\n flex-shrink: 0;\n align-self: center;\n gap: var(--lea-spacing-0-5);\n margin-inline: var(--lea-spacing-1);\n}\n\n/* Next to a unit, the unit's own margin is the gap. */\n.Unit + .Steppers {\n margin-inline-start: 0;\n}\n\n.Stepper {\n --stepper-tint: color-mix(\n in srgb,\n var(--lea-palette-text-base) calc(var(--lea-opacity-hover) * 100%),\n transparent\n );\n\n display: inline-flex;\n width: 2.25rem;\n height: 2.25rem;\n box-sizing: border-box;\n align-items: center;\n justify-content: center;\n padding: 0;\n border: 2px solid var(--lea-palette-border-subtle);\n border-radius: var(--lea-radius-full);\n background-color: transparent;\n color: var(--lea-palette-text-subtle);\n cursor: pointer;\n transition: background-color var(--lea-duration-normal)\n var(--lea-easing-standard);\n}\n\n.Stepper svg {\n display: block;\n flex-shrink: 0;\n}\n\n.Stepper:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n.Stepper[data-disabled] {\n color: var(--lea-palette-text-muted);\n cursor: not-allowed;\n}\n\n@media (hover: hover) {\n .Stepper:hover:not([data-disabled]) {\n background-color: var(--stepper-tint);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Stepper {\n transition-duration: 0.01ms;\n }\n}\n","\"use client\";\n\nimport { NumberField as BaseNumberField } from \"@base-ui/react/number-field\";\nimport { Minus, Plus } from \"lucide-react\";\nimport {\n useId,\n useState,\n type CSSProperties,\n type MouseEvent,\n type ReactNode,\n type Ref,\n} from \"react\";\n\nimport { createTranslation, useLanguage } from \"../../../i18n\";\nimport {\n FieldGroupLabel,\n FieldHelperText,\n fieldMessage,\n RequiredAsterisk,\n} from \"../field-parts\";\nimport inputStyles from \"../input.module.css\";\nimport {\n formatUnit,\n isUnitSpec,\n type NumberInputUnit,\n} from \"./number-input-unit\";\nimport styles from \"./number-input.module.css\";\n\nexport type { NumberInputUnit, NumberInputUnitSpec } from \"./number-input-unit\";\n\nexport type NumberInputProps = Omit<\n BaseNumberField.Root.Props,\n \"ref\" | \"inputRef\" | \"render\" | \"className\" | \"style\"\n> & {\n /** The visible `<input>`. */\n ref?: Ref<HTMLInputElement>;\n /** The label, linked to the input for you. */\n label?: ReactNode;\n /** Floating inside the field, or as a line above it. @default \"inside\" */\n labelPlacement?: \"inside\" | \"outside\";\n /**\n * Help text behind an info button next to the label. Needs\n * `labelPlacement=\"outside\"`: a floating label has no room for it.\n */\n tooltip?: ReactNode;\n /** Helper / error text shown below the control and linked via `aria-describedby`. */\n helperText?: ReactNode;\n /** Renders the field in its error state (border, label and helper colour). */\n error?: boolean;\n /**\n * A message for a value that is allowed but unusual, such as an unusually\n * high amount. Shown in amber in place of the helper text, without the\n * error state: the field is not marked invalid. `error` wins when both are set.\n */\n warning?: ReactNode;\n /** Reflects `required` onto the control and the label asterisk. */\n required?: boolean;\n /** Disables the input and the stepper buttons. */\n disabled?: boolean;\n /** Placeholder shown while the field is empty. */\n placeholder?: string;\n /**\n * Decorative leading icon (e.g. a category glyph). Rendered `aria-hidden`, so\n * the visible label must carry the meaning.\n */\n startIcon?: ReactNode;\n /**\n * Trailing unit shown inside the field. Rendered as real text linked to the\n * input via `aria-describedby` — not a placeholder — so it is announced and\n * survives typing.\n *\n * - Your own content: `unit=\"kr/mnd\"`. It is not translated.\n * - `{ type: \"years\" }`: \"år\", \"vuotta\" or \"años\" in the language of\n * `LeaProvider`, in the plural that fits the number in the field.\n * - `{ type: \"currency\" }`: \"kr\" or \"€\" for the language (`{ type:\n * \"currency\", currency: \"EUR\" }` for another currency).\n */\n unit?: NumberInputUnit;\n /**\n * Whether to render the increment / decrement stepper buttons.\n * @default true\n */\n showSteppers?: boolean;\n /** Class on the outermost element, for layout. */\n className?: string;\n /** Inline style on the outermost element, for layout. */\n style?: CSSProperties;\n};\n\nconst join = (...parts: (string | false | null | undefined)[]) =>\n parts.filter(Boolean).join(\" \") || undefined;\n\n/**\n * Numeric input built on Base UI's `NumberField` (value / min / max / step /\n * formatting), in the same box as `Input`, with increment / decrement buttons.\n *\n * The label, the helper text and the ids that link them to the input are all\n * rendered and wired for you. Set `labelPlacement=\"outside\"` for a label above\n * the field, which can also carry a `tooltip`.\n *\n * For money and other open-ended amounts, set `showSteppers={false}` and use a\n * `unit` (e.g. `kr`) instead — a stepper is only helpful for small bounded\n * counts.\n *\n * `unit` takes your own text, or a unit the library writes in the language of\n * `LeaProvider`:\n *\n * ```tsx\n * <NumberInput label=\"Lånebeløp\" unit={{ type: \"currency\" }} showSteppers={false} />\n * <NumberInput label=\"Nedbetalingstid\" unit={{ type: \"years\" }} min={1} max={15} />\n * ```\n */\nexport function NumberInput({\n label,\n labelPlacement = \"inside\",\n tooltip,\n helperText,\n error = false,\n warning,\n required = false,\n disabled = false,\n id,\n placeholder,\n startIcon,\n unit,\n showSteppers = true,\n className,\n style,\n ref,\n value,\n defaultValue,\n onValueChange,\n ...rest\n}: NumberInputProps) {\n const language = useLanguage();\n // The plural of \"years\" follows the number in the field, controlled or not.\n const [typedValue, setTypedValue] = useState(defaultValue ?? null);\n const currentValue = value !== undefined ? value : typedValue;\n const unitContent = isUnitSpec(unit)\n ? formatUnit(unit, language, currentValue)\n : unit;\n const generatedId = useId();\n const inputId = id ?? generatedId;\n const labelId = useId();\n const helperTextId = useId();\n const unitId = useId();\n const stepperLabels = useStepperLabels();\n\n const hasLabel = label != null;\n const labelOutside = hasLabel && labelPlacement === \"outside\";\n const message = fieldMessage(helperText, warning, error);\n const hasHelperText = message.content != null;\n\n // The input is described by any combination of helper text + the trailing\n // unit, in reading order, and never points at an element that isn't rendered.\n const describedBy = join(\n hasHelperText && helperTextId,\n unitContent != null && unitContent !== \"\" && unitId,\n );\n\n // The label, the padding, the icon and the unit are part of the field, so a\n // click on any of them goes to the input. The stepper buttons keep theirs.\n const focusInput = (event: MouseEvent<HTMLDivElement>) => {\n const target = event.target as Element;\n if (target.closest(\"button, a, input, [tabindex]\")) return;\n event.currentTarget.querySelector(\"input\")?.focus();\n };\n\n return (\n <BaseNumberField.Root\n {...rest}\n value={value}\n defaultValue={defaultValue}\n onValueChange={(next, details) => {\n setTypedValue(next);\n onValueChange?.(next, details);\n }}\n id={inputId}\n disabled={disabled}\n required={required}\n className={join(inputStyles.Field, className)}\n style={style}\n >\n {labelOutside && (\n <FieldGroupLabel\n id={labelId}\n htmlFor={inputId}\n tooltip={tooltip}\n required={required}\n error={error}\n disabled={disabled}\n >\n {label}\n </FieldGroupLabel>\n )}\n <div\n className={join(inputStyles.Box, styles.Box)}\n data-invalid={error ? \"\" : undefined}\n data-disabled={disabled ? \"\" : undefined}\n data-has-label={hasLabel && !labelOutside ? \"\" : undefined}\n data-label-outside={labelOutside ? \"\" : undefined}\n onClick={focusInput}\n >\n {startIcon != null && (\n <span\n className={inputStyles.Adornment}\n data-position=\"start\"\n aria-hidden\n >\n {startIcon}\n </span>\n )}\n <div className={inputStyles.Inner}>\n {hasLabel && !labelOutside && (\n <label htmlFor={inputId} className={inputStyles.Label}>\n {label}\n {required && <RequiredAsterisk />}\n </label>\n )}\n <BaseNumberField.Input\n ref={ref}\n // Never empty: `:placeholder-shown` is how the stylesheet tells an\n // empty field from a filled one, and it needs a placeholder to work.\n placeholder={placeholder || \" \"}\n aria-invalid={error || undefined}\n aria-describedby={describedBy}\n className={inputStyles.Control}\n />\n </div>\n {unitContent != null && unitContent !== \"\" && (\n <span id={unitId} className={styles.Unit}>\n {unitContent}\n </span>\n )}\n {showSteppers && (\n <span className={styles.Steppers}>\n <BaseNumberField.Decrement\n aria-label={stepperLabels.decrease}\n className={styles.Stepper}\n >\n <Minus size={16} aria-hidden />\n </BaseNumberField.Decrement>\n <BaseNumberField.Increment\n aria-label={stepperLabels.increase}\n className={styles.Stepper}\n >\n <Plus size={16} aria-hidden />\n </BaseNumberField.Increment>\n </span>\n )}\n </div>\n {hasHelperText && (\n <FieldHelperText\n id={helperTextId}\n error={error}\n warning={message.warning}\n >\n {message.content}\n </FieldHelperText>\n )}\n </BaseNumberField.Root>\n );\n}\n\nconst useStepperLabels = createTranslation({\n increase: {\n no: \"Øk\",\n se: \"Öka\",\n fi: \"Lisää\",\n es: \"Aumentar\",\n },\n decrease: {\n no: \"Reduser\",\n se: \"Minska\",\n fi: \"Vähennä\",\n es: \"Disminuir\",\n },\n});\n","import {\n getCountries,\n getCountryCallingCode,\n type CountryCode,\n} from \"libphonenumber-js\";\n\nexport type { CountryCode };\n\n/**\n * Countries surfaced at the top of the country-code select. Lea Bank's core\n * markets, in priority order. The rest of the world follows, sorted by their\n * localised name.\n */\nexport const PINNED_COUNTRIES: readonly CountryCode[] = [\n \"FI\",\n \"SE\",\n \"NO\",\n \"ES\",\n];\n\nexport interface CountryOption {\n /** ISO 3166-1 alpha-2 code, e.g. `\"FI\"`. */\n code: CountryCode;\n /** International dialling code without the leading `+`, e.g. `\"358\"`. */\n dialCode: string;\n /** Localised country name, e.g. `\"Finland\"`. */\n name: string;\n /** Flag emoji derived from the ISO code. Purely decorative. */\n flag: string;\n /** Whether this is one of the pinned core-market countries. */\n pinned: boolean;\n}\n\n/**\n * Turns a two-letter ISO country code into its flag emoji by mapping each\n * letter to the matching Unicode regional-indicator symbol. No asset library\n * needed — the platform renders the pair as a flag.\n */\nexport function flagEmoji(code: string): string {\n return code\n .toUpperCase()\n .replace(/[A-Z]/g, (char) =>\n String.fromCodePoint(127397 + char.charCodeAt(0)),\n );\n}\n\n/**\n * Builds the ordered country list for the select: the pinned core markets\n * first, then every remaining country sorted by its name in the given locale.\n * Country names come from `Intl.DisplayNames`, so they follow the active locale\n * with no hand-maintained list.\n */\nexport function getCountryOptions(locale: string): CountryOption[] {\n const displayNames = new Intl.DisplayNames([locale], { type: \"region\" });\n const all = getCountries();\n\n const toOption = (code: CountryCode, pinned: boolean): CountryOption => ({\n code,\n dialCode: getCountryCallingCode(code),\n name: displayNames.of(code) ?? code,\n flag: flagEmoji(code),\n pinned,\n });\n\n const pinned = PINNED_COUNTRIES.filter((code) => all.includes(code)).map(\n (code) => toOption(code, true),\n );\n\n const rest = all\n .filter((code) => !PINNED_COUNTRIES.includes(code))\n .map((code) => toOption(code, false))\n .sort((a, b) => a.name.localeCompare(b.name, locale));\n\n return [...pinned, ...rest];\n}\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n *\n * The box, its border states, the label and the text field come from\n * input.module.css, and the options from option-list.module.css; this file\n * adds the country button at the start of the field and the popup it opens.\n */\n\n/*\n * `.Field` in front of a selector lifts it over the rules of input.module.css\n * for the same element, whichever of the two stylesheets loads last.\n */\n\n/* The country list is in a portal, so focus is not inside the box while it is open. */\n.Field .Box[data-country-open]:not([data-invalid]) {\n --input-border-color: var(--lea-palette-border-accent);\n --input-border-width: 2px;\n}\n\n/*\n * The label is always up: the country button takes the place where it would\n * rest, and with it up there is room for a placeholder that shows the format.\n */\n.Field .Label {\n max-width: calc(\n (100% - var(--input-inset-start) - var(--input-padding-inline)) / 0.75\n );\n scale: 0.75;\n translate: 0 calc(var(--lea-spacing) * 0.75);\n}\n\n.Field .Inner {\n /* 10px between the line after the country button and the number. */\n --input-inset-start: calc(var(--lea-spacing) * 1.25);\n}\n\n.Trigger {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n padding-block: 0;\n padding-inline: var(--lea-spacing-1) var(--lea-spacing-0-5);\n border: 0;\n /* The line between the country and the number. */\n border-inline-end: 1px solid var(--lea-palette-border-subtle);\n border-radius: var(--lea-radius-xl) 0 0 var(--lea-radius-xl);\n margin: 0;\n background: none;\n color: var(--lea-palette-text-base);\n cursor: pointer;\n font: inherit;\n gap: var(--lea-spacing-0-5);\n letter-spacing: inherit;\n white-space: nowrap;\n}\n\n@media (hover: hover) {\n .Trigger:hover:not(:disabled) {\n background-color: var(--lea-palette-bg-subtle);\n }\n}\n\n.Trigger[data-popup-open] {\n background-color: var(--lea-palette-bg-subtle);\n}\n\n/*\n * The dial code of a field with one country (`countries` of one): the\n * country button without the button. Same place, line and type, so a form\n * with both kinds lines up; no chevron and no hover, as there is nothing to\n * open.\n */\n.DialCode {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n padding-inline: var(--lea-spacing-1);\n border-inline-end: 1px solid var(--lea-palette-border-subtle);\n color: var(--lea-palette-text-base);\n gap: var(--lea-spacing-0-5);\n white-space: nowrap;\n}\n\n.Box[data-disabled] .DialCode {\n opacity: 0.6;\n}\n\n/* Inside the border of the field, which a ring around the button would cover. */\n.Trigger:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: -0.25rem;\n}\n\n.Trigger:disabled {\n cursor: not-allowed;\n opacity: 0.6;\n}\n\n/* One step up the type scale: a flag at the size of the text reads as smaller than it. */\n.Flag {\n font-size: var(--lea-font-step-1);\n line-height: var(--lea-line-height-none);\n}\n\n/* One width for every dial code, +1 or +358, so the number does not move with the country. */\n.Dial {\n min-width: 2.5rem;\n text-align: center;\n}\n\n.Chevron {\n width: 1rem;\n height: 1rem;\n flex-shrink: 0;\n color: var(--lea-palette-text-subtle);\n}\n\n/* As wide as the field, a little below it. */\n.Popup {\n overflow: hidden;\n width: var(--anchor-width);\n max-width: var(--available-width);\n box-sizing: border-box;\n border: 1px solid var(--lea-palette-border-accent);\n border-radius: var(--lea-radius-4xl);\n background-color: var(--lea-palette-bg-base);\n box-shadow: var(--lea-shadow-lg);\n color: var(--lea-palette-text-base);\n outline: none;\n transition: opacity var(--lea-duration-fast) var(--lea-easing-standard);\n}\n\n/*\n * The popup fades in, and goes at once: opened again while on its way out, it\n * came back with the search text it was closed with. See combo-field.module.css.\n */\n.Popup[data-starting-style] {\n opacity: 0;\n}\n\n.Popup[data-ending-style] {\n transition: none;\n}\n\n.Search {\n display: flex;\n align-items: center;\n padding-block: calc(var(--lea-spacing) * 1.25);\n padding-inline: calc(var(--lea-spacing) * 1.75);\n border-block-end: 1px solid var(--lea-palette-border-subtle);\n gap: var(--lea-spacing-1);\n}\n\n.Search svg {\n width: 1.125rem;\n height: 1.125rem;\n flex-shrink: 0;\n color: var(--lea-palette-text-subtle);\n}\n\n.SearchInput {\n min-width: 0;\n flex: 1;\n padding: 0;\n border: 0;\n margin: 0;\n background: none;\n color: inherit;\n font-family: inherit;\n font-size: var(--lea-text-body-md-font-size);\n font-weight: var(--lea-text-body-md-font-weight);\n letter-spacing: var(--lea-text-body-md-letter-spacing);\n line-height: var(--lea-text-body-md-line-height);\n /* The popup has the border that shows where focus is. */\n outline: none;\n}\n\n.SearchInput::placeholder {\n color: var(--lea-palette-text-muted);\n opacity: 1;\n}\n\n/*\n * 200px: five countries, then it scrolls. Less when the window is short, by\n * the height of the search box above it. The fallback is for the first\n * measurement, see option-list.module.css.\n */\n.List {\n overflow: hidden auto;\n max-height: min(12.5rem, var(--available-height, 100vh) - 4rem);\n}\n\n.Country {\n gap: calc(var(--lea-spacing) * 1.25);\n}\n\n.CountryName {\n overflow: hidden;\n min-width: 0;\n flex: 1;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/*\n * neutral-600 rather than text-subtle, which falls below 4.5:1 on the fill of\n * a row under the pointer. On the fill of the country in use it takes the\n * colour of the name.\n */\n.CountryDial {\n color: var(--lea-color-neutral-600);\n}\n\n.Country[data-selected] .CountryDial {\n color: inherit;\n}\n\n/* Between the pinned countries and the rest. */\n.Separator {\n flex-shrink: 0;\n height: 1px;\n background-color: var(--lea-palette-border-base);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Popup {\n transition-duration: 0.01ms;\n }\n}\n","\"use client\";\n\nimport { Combobox } from \"@base-ui/react/combobox\";\nimport {\n AsYouType,\n getCountryCallingCode,\n parsePhoneNumberFromString,\n} from \"libphonenumber-js\";\nimport { ChevronDown, Search } from \"lucide-react\";\nimport {\n Fragment,\n useId,\n useMemo,\n useRef,\n useState,\n type ChangeEvent,\n type ComponentPropsWithoutRef,\n type CSSProperties,\n type FocusEvent,\n type MouseEvent,\n type ReactNode,\n type Ref,\n type RefObject,\n} from \"react\";\n\nimport { createTranslation, useLanguage, type Language } from \"../../../i18n\";\nimport {\n isMobileCountry,\n validateMobileNumber,\n validatePhoneNumber,\n} from \"../../../validation/phone\";\nimport { invalid, valid } from \"../../../validation/types\";\nimport { FieldHelperText, RequiredAsterisk } from \"../field-parts\";\nimport inputStyles from \"../input.module.css\";\nimport listStyles from \"../option-list.module.css\";\nimport {\n getCountryOptions,\n type CountryCode,\n type CountryOption,\n} from \"./countries\";\nimport styles from \"./phone-number-input.module.css\";\n\n/** The default country and the locale used to name/sort the list, per language. */\nconst LANGUAGE_TO_COUNTRY: Record<Language, CountryCode> = {\n no: \"NO\",\n se: \"SE\",\n fi: \"FI\",\n es: \"ES\",\n};\n\nconst LANGUAGE_TO_LOCALE: Record<Language, string> = {\n no: \"nb-NO\",\n se: \"sv-SE\",\n fi: \"fi-FI\",\n es: \"es-ES\",\n};\n\n/** Details reported alongside every change so the consumer can react to validity. */\nexport interface PhoneNumberMeta {\n /** The selected country. */\n country: CountryCode;\n /** Canonical E.164 number when valid (e.g. `\"+358401234567\"`), otherwise `undefined`. */\n e164?: string;\n /** Whether the current number is a valid phone number for the country. */\n isValid: boolean;\n}\n\ntype NativeInputProps = Omit<\n ComponentPropsWithoutRef<\"input\">,\n | \"value\"\n | \"defaultValue\"\n | \"onChange\"\n | \"type\"\n | \"size\"\n | \"color\"\n | \"children\"\n | \"className\"\n | \"style\"\n>;\n\nexport type PhoneNumberInputProps = NativeInputProps & {\n /** Floating label, linked to the input for you. */\n label?: ReactNode;\n /** Helper / error text shown below the control and linked via `aria-describedby`. */\n helperText?: ReactNode;\n /**\n * Puts the input, its label and its helper text into the error state. When\n * set, true or false, the built-in check on blur stays out of it.\n */\n error?: boolean;\n /** Selected country (controlled). Pair with `onCountryChange`. */\n country?: CountryCode;\n /** Initial country when uncontrolled. Defaults to the country for the active locale. */\n defaultCountry?: CountryCode;\n /** Fired when the user picks a different country. */\n onCountryChange?: (country: CountryCode) => void;\n /** The national number as displayed (controlled). Pair with `onValueChange`. */\n value?: string;\n /** Initial national number when uncontrolled. */\n defaultValue?: string;\n /** Fired on every keystroke with the formatted national number and validity meta. */\n onValueChange?: (value: string, meta: PhoneNumberMeta) => void;\n /**\n * The countries the number may be from. With one, there is no country\n * list: its dial code stands fixed at the start of the field (`+46`). With\n * several, the list holds these alone.\n *\n * Either way the value is the national number without the trunk `0`\n * (`70 123 45 67`): a pasted `+46 70…`, `0046 70…` or `070…` is cleaned up to\n * it, and when focus leaves the field. A number of another country is\n * invalid. Left out, every country is in the list and the number is kept as\n * typed, as before.\n */\n countries?: readonly CountryCode[];\n /**\n * Accept mobile numbers only: a landline is invalid, with a message of its\n * own (\"Skriv inn et mobilnummer\"). Applies to Sweden, Finland, Norway and\n * Spain, as `validateMobileNumber`; a number of another country is checked\n * as any number. `meta.isValid` follows it.\n */\n mobileOnly?: boolean;\n /**\n * Show a built-in \"invalid number\" error once the field is blurred with a\n * non-empty, invalid value. Ignored when the consumer drives `error`.\n * @default true\n */\n validateOnBlur?: boolean;\n /** Class on the outermost element, for layout. */\n className?: string;\n /** Inline style on the outermost element, for layout. */\n style?: CSSProperties;\n /** The phone number `<input>`. */\n ref?: Ref<HTMLInputElement>;\n};\n\n/**\n * Formats the national number as the user types. Norwegian mobile numbers are\n * grouped 3-2-3 (e.g. `400 12 345`), which `libphonenumber-js` doesn't do on its\n * own (it pairs digits as `40 01 23 45`). Every other country keeps the library's\n * standard grouping via `AsYouType`.\n */\nfunction formatNationalNumber(country: CountryCode, input: string): string {\n if (country === \"NO\") {\n const digits = input.replace(/\\D/g, \"\").slice(0, 8);\n return [digits.slice(0, 3), digits.slice(3, 5), digits.slice(5, 8)]\n .filter(Boolean)\n .join(\" \");\n }\n return new AsYouType(country).input(input);\n}\n\n/**\n * The national number, grouped as in the international format but without\n * the dial code, for a field that shows the dial code in front of it:\n * `70 123 45 67` rather than `070-123 45 67`.\n */\nfunction formatWithoutDialCode(country: CountryCode, digits: string): string {\n if (country === \"NO\") return formatNationalNumber(country, digits);\n const dialCode = getCountryCallingCode(country);\n return new AsYouType(country)\n .input(`+${dialCode}${digits}`)\n .replace(`+${dialCode}`, \"\")\n .trim();\n}\n\n/** What typing gives, with the dial code in front of the field. */\nfunction formatTyped(country: CountryCode, text: string): string {\n // Someone typing \"070…\" or \"+46…\" by hand: left as typed until focus\n // leaves the field, rather than losing the first digit under their cursor.\n if (/^\\s*[+0]/.test(text)) return new AsYouType(country).input(text);\n return formatWithoutDialCode(country, text.replace(/\\D/g, \"\"));\n}\n\n/**\n * The text cleaned up to the national number of `country`, when it is a\n * number of that country (`+46 70…`, `0046 70…`, `070…`); otherwise as typed.\n */\nfunction cleanUp(country: CountryCode, text: string): string {\n const parsed = parsePhoneNumberFromString(\n text.replace(/[\\s-]/g, \"\"),\n country,\n );\n if (parsed && parsed.countryCallingCode === getCountryCallingCode(country)) {\n return formatWithoutDialCode(country, parsed.nationalNumber);\n }\n return formatTyped(country, text);\n}\n\ntype NumberCheck = { valid: boolean; normalized?: string; landline: boolean };\n\n/**\n * Validates the number for the field: of the selected country only, when the\n * countries are restricted, and a mobile only, with `mobileOnly`.\n */\nfunction checkNumber(\n country: CountryCode,\n value: string,\n { sameCountry, mobileOnly }: { sameCountry: boolean; mobileOnly: boolean },\n): NumberCheck {\n const ofCountry = () => {\n const parsed = parsePhoneNumberFromString(\n value.replace(/\\s/g, \"\"),\n country,\n );\n return parsed?.isValid() && parsed.country === country\n ? valid(parsed.number)\n : invalid();\n };\n\n if (mobileOnly && isMobileCountry(country)) {\n const mobile = validateMobileNumber(country, value);\n return mobile.valid\n ? { ...mobile, landline: false }\n : { valid: false, landline: ofCountry().valid };\n }\n\n return {\n ...(sameCountry ? ofCountry() : validatePhoneNumber(country, value)),\n landline: false,\n };\n}\n\nconst usePhoneLabels = createTranslation({\n countryLabel: {\n no: \"Landskode\",\n se: \"Landskod\",\n fi: \"Maakoodi\",\n es: \"Código de país\",\n },\n searchCountry: {\n no: \"Søk etter land\",\n se: \"Sök efter land\",\n fi: \"Hae maata\",\n es: \"Buscar país\",\n },\n noCountries: {\n no: \"Ingen land funnet\",\n se: \"Inga länder hittades\",\n fi: \"Maita ei löytynyt\",\n es: \"No se han encontrado países\",\n },\n invalid: {\n no: \"Ugyldig telefonnummer\",\n se: \"Ogiltigt telefonnummer\",\n fi: \"Virheellinen puhelinnumero\",\n es: \"Número de teléfono no válido\",\n },\n notMobile: {\n no: \"Skriv inn et mobilnummer\",\n se: \"Ange ett mobilnummer\",\n fi: \"Anna matkapuhelinnumero\",\n es: \"Introduce un número de móvil\",\n },\n});\n\nconst join = (...parts: (string | false | null | undefined)[]) =>\n parts.filter(Boolean).join(\" \") || undefined;\n\n/** What typing in the search box is matched against. */\nconst searchText = (option: CountryOption) =>\n `${option.name} ${option.code} ${option.dialCode} +${option.dialCode}`;\n\nconst sameCountry = (a: CountryOption, b: CountryOption) => a.code === b.code;\n\ntype CountrySelectProps = {\n options: CountryOption[];\n value: CountryCode;\n onChange: (country: CountryCode) => void;\n ariaLabel: string;\n searchLabel: string;\n noOptionsText: string;\n disabled: boolean;\n open: boolean;\n onOpenChange: (open: boolean) => void;\n /** The field, which the popup sits under and matches in width. */\n anchorRef: RefObject<HTMLDivElement | null>;\n};\n\n/**\n * The country button at the start of the field, and the popup it opens: a\n * search box over the list of countries, on Base UI's `Combobox`. The popup\n * is anchored to the whole field rather than to the button.\n */\nfunction CountrySelect({\n options,\n value,\n onChange,\n ariaLabel,\n searchLabel,\n noOptionsText,\n disabled,\n open,\n onOpenChange,\n anchorRef,\n}: CountrySelectProps) {\n const { contains } = Combobox.useFilter({ sensitivity: \"base\" });\n const [query, setQuery] = useState(\"\");\n // See `ComboField`: the option the arrow keys are on is marked once a key\n // has been pressed, not for a list opened with the mouse.\n const [keyboard, setKeyboard] = useState(false);\n\n const selected = options.find((option) => option.code === value) ?? null;\n const pinnedCount = options.filter((option) => option.pinned).length;\n\n return (\n <Combobox.Root<CountryOption>\n items={options}\n value={selected}\n onValueChange={(next) => {\n if (next) onChange(next.code);\n }}\n inputValue={query}\n onInputValueChange={setQuery}\n open={open}\n onOpenChange={onOpenChange}\n filter={(option, text) => contains(searchText(option), text)}\n itemToStringLabel={(option) => option.name}\n itemToStringValue={(option) => option.code}\n isItemEqualToValue={sameCountry}\n disabled={disabled}\n highlightItemOnHover={false}\n >\n <Combobox.Trigger\n className={styles.Trigger}\n aria-label={selected ? `${ariaLabel}, ${selected.name}` : ariaLabel}\n >\n {selected && (\n <>\n <span aria-hidden className={styles.Flag}>\n {selected.flag}\n </span>\n <span className={styles.Dial}>+{selected.dialCode}</span>\n </>\n )}\n <ChevronDown aria-hidden className={styles.Chevron} />\n </Combobox.Trigger>\n <Combobox.Portal>\n <Combobox.Positioner\n anchor={anchorRef}\n side=\"bottom\"\n align=\"start\"\n sideOffset={4}\n className={listStyles.Positioner}\n >\n <Combobox.Popup\n className={styles.Popup}\n // Base UI makes the popup a dialog, which needs a name.\n aria-label={ariaLabel}\n data-keyboard={keyboard ? \"\" : undefined}\n onKeyDownCapture={() => setKeyboard(true)}\n onPointerDownCapture={() => setKeyboard(false)}\n >\n <div className={styles.Search}>\n <Search aria-hidden />\n <Combobox.Input\n className={styles.SearchInput}\n placeholder={searchLabel}\n aria-label={searchLabel}\n />\n </div>\n <Combobox.Empty className={listStyles.Empty}>\n {noOptionsText}\n </Combobox.Empty>\n <Combobox.List className={join(listStyles.List, styles.List)}>\n {(option: CountryOption, index: number) => (\n <Fragment key={option.code}>\n {query === \"\" && pinnedCount > 0 && index === pinnedCount && (\n // A line to look at only: a listbox may hold nothing but\n // options, so it has no role of its own.\n <div role=\"presentation\" className={styles.Separator} />\n )}\n <Combobox.Item\n value={option}\n className={join(listStyles.Item, styles.Country)}\n >\n <span aria-hidden className={styles.Flag}>\n {option.flag}\n </span>\n <span className={styles.CountryName}>{option.name}</span>\n <span className={styles.CountryDial}>\n +{option.dialCode}\n </span>\n </Combobox.Item>\n </Fragment>\n )}\n </Combobox.List>\n </Combobox.Popup>\n </Combobox.Positioner>\n </Combobox.Portal>\n </Combobox.Root>\n );\n}\n\n/**\n * Phone number input with a searchable country-code picker at its start, in\n * the same box as `Input`. The number is formatted as you type and validated\n * with `libphonenumber-js`. Lea Bank's core markets (Finland, Sweden, Norway,\n * Spain) are pinned to the top of the country list; the default country follows\n * the active locale.\n *\n * The label, the helper text and the ids that link them to the input are all\n * rendered and wired for you. Takes the native `<input>` attributes (`name`,\n * `placeholder`, `onBlur`, `autoComplete`, …), which land on the number input.\n *\n * Both country and number can be **controlled** (`country` + `onCountryChange`,\n * `value` + `onValueChange`) or left **uncontrolled** (`defaultCountry` /\n * `defaultValue`).\n */\nexport function PhoneNumberInput({\n label,\n helperText,\n error,\n required = false,\n disabled = false,\n id,\n country,\n defaultCountry,\n onCountryChange,\n value,\n defaultValue,\n onValueChange,\n onBlur,\n countries,\n mobileOnly = false,\n validateOnBlur = true,\n className,\n style,\n ref,\n \"aria-describedby\": ariaDescribedBy,\n ...inputAttributes\n}: PhoneNumberInputProps) {\n const language = useLanguage();\n const t = usePhoneLabels();\n const generatedId = useId();\n const inputId = id ?? generatedId;\n const helperTextId = useId();\n const dialCodeId = useId();\n const boxRef = useRef<HTMLDivElement>(null);\n const [countryOpen, setCountryOpen] = useState(false);\n\n // Restricted to `countries`; with one, its dial code is fixed in front.\n const allowed = countries != null && countries.length > 0 ? countries : null;\n const fixed = allowed?.length === 1;\n const allowedKey = allowed?.join(\",\");\n\n // Keyed by the codes, as `countries` may be a new array on every render.\n const options = useMemo(() => {\n const all = getCountryOptions(LANGUAGE_TO_LOCALE[language]);\n const codes = allowedKey?.split(\",\");\n return codes ? all.filter((option) => codes.includes(option.code)) : all;\n }, [language, allowedKey]);\n\n const preferredCountry = defaultCountry ?? LANGUAGE_TO_COUNTRY[language];\n const [countryState, setCountryState] = useState<CountryCode>(\n allowed && !allowed.includes(preferredCountry)\n ? allowed[0]\n : preferredCountry,\n );\n const currentCountry = country ?? countryState;\n const checkOptions = { sameCountry: allowed != null, mobileOnly };\n\n const [valueState, setValueState] = useState<string>(defaultValue ?? \"\");\n const currentValue = value ?? valueState;\n\n const [touched, setTouched] = useState(false);\n\n const check = checkNumber(currentCountry, currentValue, checkOptions);\n const meta: PhoneNumberMeta = {\n country: currentCountry,\n e164: check.normalized,\n isValid: check.valid,\n };\n\n const showValidationError =\n validateOnBlur && touched && currentValue.trim() !== \"\" && !meta.isValid;\n const resolvedError = error ?? showValidationError;\n const resolvedHelper =\n helperText ??\n (showValidationError\n ? check.landline\n ? t.notMobile\n : t.invalid\n : undefined);\n const hasHelperText = resolvedHelper != null;\n\n const emit = (nextValue: string, nextCountry: CountryCode) => {\n const result = checkNumber(nextCountry, nextValue, checkOptions);\n onValueChange?.(nextValue, {\n country: nextCountry,\n e164: result.normalized,\n isValid: result.valid,\n });\n };\n\n const setNumber = (nextValue: string, nextCountry: CountryCode) => {\n if (value === undefined) setValueState(nextValue);\n if (nextCountry !== currentCountry) {\n if (country === undefined) setCountryState(nextCountry);\n onCountryChange?.(nextCountry);\n }\n emit(nextValue, nextCountry);\n };\n\n const handleCountryChange = (next: CountryCode) => {\n if (country === undefined) {\n setCountryState(next);\n }\n onCountryChange?.(next);\n // Re-emit so the consumer's meta (e.g. E.164) reflects the new dial code.\n emit(currentValue, next);\n };\n\n const handleInputChange = (event: ChangeEvent<HTMLInputElement>) => {\n const text = event.target.value;\n\n if (!allowed) {\n setNumber(formatNationalNumber(currentCountry, text), currentCountry);\n return;\n }\n\n // More than one character at once: pasted, or filled in by the browser.\n if (text.length - currentValue.length > 1) {\n // A number of another country in the list switches to that country.\n const parsed = parsePhoneNumberFromString(\n text.replace(/[\\s-]/g, \"\"),\n currentCountry,\n );\n const nextCountry =\n parsed?.country && allowed.includes(parsed.country)\n ? parsed.country\n : currentCountry;\n setNumber(cleanUp(nextCountry, text), nextCountry);\n return;\n }\n\n setNumber(formatTyped(currentCountry, text), currentCountry);\n };\n\n const handleBlur = (event: FocusEvent<HTMLInputElement>) => {\n setTouched(true);\n if (allowed) {\n const cleaned = cleanUp(currentCountry, currentValue);\n if (cleaned !== currentValue) setNumber(cleaned, currentCountry);\n }\n onBlur?.(event);\n };\n\n const fixedOption = fixed\n ? options.find((option) => option.code === currentCountry)\n : undefined;\n\n // The label and the padding are part of the field, so a click on them goes\n // to the number, unless it hit the country button. Clicks in the country\n // list arrive here as well, through the portal, and are not for the number.\n const focusInput = (event: MouseEvent<HTMLDivElement>) => {\n const target = event.target as Element;\n if (!event.currentTarget.contains(target)) return;\n if (target.closest(\"button, input\")) return;\n // By type: Base UI keeps a hidden input for the country in here too.\n event.currentTarget\n .querySelector<HTMLElement>('input[type=\"tel\"]')\n ?.focus();\n };\n\n return (\n <div\n className={join(inputStyles.Field, styles.Field, className)}\n style={style}\n >\n <div\n ref={boxRef}\n className={join(inputStyles.Box, styles.Box)}\n data-invalid={resolvedError ? \"\" : undefined}\n data-disabled={disabled ? \"\" : undefined}\n data-country-open={countryOpen ? \"\" : undefined}\n onClick={focusInput}\n >\n {fixed ? (\n fixedOption && (\n // The dial code is read with the field, as its description.\n <span className={styles.DialCode}>\n <span aria-hidden className={styles.Flag}>\n {fixedOption.flag}\n </span>\n <span id={dialCodeId} className={styles.Dial}>\n +{fixedOption.dialCode}\n </span>\n </span>\n )\n ) : (\n <CountrySelect\n options={options}\n value={currentCountry}\n onChange={handleCountryChange}\n ariaLabel={t.countryLabel}\n searchLabel={t.searchCountry}\n noOptionsText={t.noCountries}\n disabled={disabled}\n open={countryOpen}\n onOpenChange={setCountryOpen}\n anchorRef={boxRef}\n />\n )}\n <div className={join(inputStyles.Inner, styles.Inner)}>\n {label != null && (\n <label\n htmlFor={inputId}\n className={join(inputStyles.Label, styles.Label)}\n >\n {label}\n {required && <RequiredAsterisk />}\n </label>\n )}\n <input\n inputMode=\"tel\"\n autoComplete=\"tel-national\"\n {...inputAttributes}\n ref={ref}\n id={inputId}\n type=\"tel\"\n value={currentValue}\n onChange={handleInputChange}\n onBlur={handleBlur}\n disabled={disabled}\n required={required}\n aria-invalid={resolvedError}\n aria-describedby={join(\n fixedOption && dialCodeId,\n hasHelperText && helperTextId,\n ariaDescribedBy,\n )}\n className={inputStyles.Control}\n />\n </div>\n </div>\n {hasHelperText && (\n <FieldHelperText id={helperTextId} error={resolvedError}>\n {resolvedHelper}\n </FieldHelperText>\n )}\n </div>\n );\n}\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n */\n\n/* The options of a `RadioGroup.Root`, one under the other. */\n.Options {\n display: flex;\n flex-direction: column;\n gap: var(--lea-spacing-1);\n}\n\n/*\n * The radio itself: a bordered circle with a dot that springs in when it is\n * chosen. `RadioCard.Indicator` draws the same circle as a picture, from the\n * same classes, so the two can never drift apart.\n */\n.Circle {\n --radio-size: 1.3rem;\n --radio-dot-size: 0.6rem;\n\n display: inline-flex;\n width: var(--radio-size);\n height: var(--radio-size);\n box-sizing: border-box;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n padding: 0;\n border: 2px solid var(--lea-palette-border-base);\n border-radius: var(--lea-radius-full);\n margin: 0;\n background-color: var(--lea-palette-bg-base);\n cursor: pointer;\n transition: background-color var(--lea-duration-normal)\n var(--lea-easing-standard),\n border-color var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n.Circle[data-size=\"sm\"] {\n --radio-size: 1rem;\n --radio-dot-size: 0.45rem;\n}\n\n@media (hover: hover) {\n .Circle:hover:not([data-checked], [data-disabled]) {\n border-color: var(--lea-palette-border-strong);\n }\n}\n\n.Circle[data-checked] {\n border-color: var(--lea-palette-primary-base);\n background-color: var(--lea-palette-primary-base);\n}\n\n.Circle:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n.Circle[data-disabled] {\n cursor: not-allowed;\n opacity: 0.7;\n}\n\n.Dot {\n width: var(--radio-dot-size);\n height: var(--radio-dot-size);\n border-radius: var(--lea-radius-full);\n background-color: var(--lea-palette-primary-contrast);\n scale: 0;\n transition: scale var(--lea-duration-emphasis) var(--lea-easing-spring);\n}\n\n.Circle[data-checked] .Dot {\n scale: 1;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Circle,\n .Dot {\n transition-duration: 0.01ms;\n }\n}\n","import styles from \"./radio.module.css\";\n\nexport type RadioSize = \"sm\" | \"md\";\n\n/** The classes of the radio visual, for the real control to wear. */\nexport const radioCircleClassName = styles.Circle;\nexport const radioDotClassName = styles.Dot;\n\nexport type RadioCircleProps = {\n /**\n * Fills the circle and springs the dot in. Leave it out when a parent\n * selector decides instead, as `RadioCard.Root` does for its indicator.\n */\n checked?: boolean;\n size?: RadioSize;\n className?: string;\n};\n\n/**\n * Internal. The radio visual as a picture only, for places where something else\n * is the control: `RadioCard.Indicator` shows it inside a card, and the card's\n * own hidden radio is what gets picked. It looks exactly like\n * `RadioGroup.Item`, which puts the same classes on the real control.\n */\nexport const RadioCircle = ({\n checked = false,\n size = \"md\",\n className,\n}: RadioCircleProps) => (\n <span\n aria-hidden\n className={[styles.Circle, className].filter(Boolean).join(\" \")}\n data-size={size}\n data-checked={checked ? \"\" : undefined}\n >\n <span className={styles.Dot} />\n </span>\n);\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this part renders correctly without a provider.\n */\n\n/* The label, the options and the helper text of a radio group, stacked. */\n.Group {\n display: flex;\n width: 100%;\n min-width: 0;\n flex-direction: column;\n}\n","\"use client\";\n\nimport { RadioGroup as BaseRadioGroup } from \"@base-ui/react/radio-group\";\nimport {\n createContext,\n useContext,\n useId,\n useMemo,\n type CSSProperties,\n type FocusEvent,\n type ReactNode,\n type Ref,\n} from \"react\";\n\nimport { FieldGroupLabel, FieldHelperText } from \"./field-parts\";\nimport styles from \"./radio-group-root.module.css\";\nimport type { RadioSize } from \"./radio-circle\";\n\n/** What a radio can stand for. Compared with `===`, so `true` is not `\"true\"`. */\nexport type RadioValue = string | number | boolean;\n\nexport type RadioCardSize = \"sm\" | \"lg\";\n\ntype RadioGroupContextValue = {\n /** Size of the radio circle, set by `RadioGroup.Root`. */\n size?: RadioSize;\n /** Size of the cards, set by `RadioCard.Group`. */\n cardSize?: RadioCardSize;\n};\n\nconst RadioGroupContext = createContext<RadioGroupContextValue | null>(null);\n\n/** Reads the surrounding group. A radio cannot work without one. */\nexport const useRadioGroupContext = (component: string) => {\n const group = useContext(RadioGroupContext);\n if (!group) {\n throw new Error(\n `<${component}> must be rendered inside a <RadioGroup.Root> or <RadioCard.Group>.`,\n );\n }\n return group;\n};\n\n/**\n * Requires an accessible name so the group is never nameless. Provide a\n * visible `label`, or an `aria-label` / `aria-labelledby` when the question\n * is written somewhere else on the page.\n */\ntype RadioGroupAccessibleName =\n | { label: ReactNode; \"aria-label\"?: never; \"aria-labelledby\"?: never }\n | { label?: never; \"aria-label\": string; \"aria-labelledby\"?: never }\n | { label?: never; \"aria-label\"?: never; \"aria-labelledby\": string };\n\n/** The props `RadioGroup.Root` and `RadioCard.Group` have in common. */\nexport type RadioGroupBaseProps<T extends RadioValue> =\n RadioGroupAccessibleName & {\n /** Help text behind an info button next to the label. Needs a `label`. */\n tooltip?: ReactNode;\n /** Helper / error text shown below the options and linked via `aria-describedby`. */\n helperText?: ReactNode;\n /** Puts the group and its helper text into the error state. */\n error?: boolean;\n /** The chosen value (controlled). `null` means nothing is chosen. */\n value?: T | null;\n /** The chosen value when first rendered, for uncontrolled use. */\n defaultValue?: T;\n /** Called with the value of the option the user picks. */\n onValueChange?: (value: T) => void;\n /** Submits the choice under this name when inside a form. */\n name?: string;\n /** Disables every option. */\n disabled?: boolean;\n /** Marks the group as required and adds the asterisk to the label. */\n required?: boolean;\n /** Called when focus leaves the group, not when it moves between options. */\n onBlur?: (event: FocusEvent<HTMLDivElement>) => void;\n /** Id of the element with the `radiogroup` role. */\n id?: string;\n /** Extra ids describing the group, alongside the helper text. */\n \"aria-describedby\"?: string;\n /** Class on the outermost element, for layout. */\n className?: string;\n /** Inline style on the outermost element, for layout. */\n style?: CSSProperties;\n /**\n * The `<input>` of the radio that takes focus: the chosen one, or the\n * first that is not disabled. It is hidden, but `ref.current.focus()`\n * moves focus to the visible radio.\n */\n ref?: Ref<HTMLInputElement>;\n /**\n * `RadioGroup.Item` / `RadioCard.Root` elements, instead of or after `options`.\n */\n children?: ReactNode;\n };\n\ntype RadioGroupRootProps<T extends RadioValue> = RadioGroupBaseProps<T> & {\n size?: RadioSize;\n cardSize?: RadioCardSize;\n /** Layout of the options. */\n optionsClassName: string;\n optionsStyle?: CSSProperties;\n /** `\"many\"` when the options sit in more than one column. */\n optionsDataColumns?: string;\n};\n\nconst join = (...parts: (string | false | null | undefined)[]) =>\n parts.filter(Boolean).join(\" \") || undefined;\n\n/**\n * Internal. Everything `RadioGroup.Root` and `RadioCard.Group` share: Base UI's\n * `RadioGroup`, which holds the value and moves focus with the arrow keys,\n * and the label, tooltip, helper text and error state around the options.\n */\nexport function RadioGroupRoot<T extends RadioValue>({\n label,\n tooltip,\n helperText,\n error = false,\n value,\n defaultValue,\n onValueChange,\n name,\n disabled = false,\n required = false,\n onBlur,\n id,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n \"aria-describedby\": ariaDescribedBy,\n className,\n style,\n ref,\n size,\n cardSize,\n optionsClassName,\n optionsStyle,\n optionsDataColumns,\n children,\n}: RadioGroupRootProps<T>) {\n // Ids are wired by hand so the name and description of the group are\n // already in the markup the server sends.\n const labelId = useId();\n const helperTextId = useId();\n const hasLabel = label != null;\n const hasHelperText = helperText != null;\n\n const group = useMemo<RadioGroupContextValue>(\n () => ({ size, cardSize }),\n [size, cardSize],\n );\n\n return (\n <div className={join(styles.Group, className)} style={style}>\n {hasLabel && (\n <FieldGroupLabel\n id={labelId}\n tooltip={tooltip}\n required={required}\n error={error}\n disabled={disabled}\n >\n {label}\n </FieldGroupLabel>\n )}\n <RadioGroupContext.Provider value={group}>\n <BaseRadioGroup\n id={id}\n inputRef={ref}\n name={name}\n value={value}\n defaultValue={defaultValue}\n onValueChange={(next) => onValueChange?.(next as T)}\n disabled={disabled}\n required={required}\n aria-label={ariaLabel}\n aria-labelledby={hasLabel ? labelId : ariaLabelledBy}\n aria-describedby={join(\n hasHelperText && helperTextId,\n ariaDescribedBy,\n )}\n aria-invalid={error ? true : undefined}\n onBlur={(event) => {\n // Moving between two options is not leaving the group.\n if (event.currentTarget.contains(event.relatedTarget)) return;\n onBlur?.(event);\n }}\n className={optionsClassName}\n style={optionsStyle}\n data-columns={optionsDataColumns}\n >\n {children}\n </BaseRadioGroup>\n </RadioGroupContext.Provider>\n {hasHelperText && (\n <FieldHelperText id={helperTextId} error={error}>\n {helperText}\n </FieldHelperText>\n )}\n </div>\n );\n}\n","\"use client\";\n\nimport { Radio as BaseRadio } from \"@base-ui/react/radio\";\nimport type { CSSProperties, ReactNode, Ref } from \"react\";\n\nimport rowStyles from \"../choice-row.module.css\";\nimport {\n radioCircleClassName,\n radioDotClassName,\n type RadioSize,\n} from \"../radio-circle\";\nimport {\n RadioGroupRoot,\n useRadioGroupContext,\n type RadioGroupBaseProps,\n type RadioValue,\n} from \"../radio-group-root\";\nimport styles from \"../radio.module.css\";\n\nexport type { RadioSize as Size, RadioValue as Value };\n\n/**\n * One choice in the `options` list of a `RadioGroup.Root` or `RadioCard.Group`.\n */\nexport type Option<T extends RadioValue = string> = {\n value: T;\n label: ReactNode;\n disabled?: boolean;\n};\n\nexport type ItemProps<T extends RadioValue = RadioValue> = {\n /** What `onValueChange` on the group is called with when this radio is picked. */\n value: T;\n /** The text next to the radio. Without it, pass `aria-label`. */\n label?: ReactNode;\n /** Disables this radio only. */\n disabled?: boolean;\n /** Size of the circle. Defaults to the group's `size`. */\n size?: RadioSize;\n /** Id of the radio itself, the element with the `radio` role. */\n id?: string;\n /** Names the radio when it has no `label`. */\n \"aria-label\"?: string;\n /** Class on the outermost element. */\n className?: string;\n /** Inline style on the outermost element. */\n style?: CSSProperties;\n /**\n * The `<input type=\"radio\">` that holds the value. It is hidden, but\n * `ref.current.focus()` moves focus to the visible radio.\n */\n ref?: Ref<HTMLInputElement>;\n};\n\nconst join = (...parts: (string | false | null | undefined)[]) =>\n parts.filter(Boolean).join(\" \") || undefined;\n\n/**\n * One choice in a `RadioGroup.Root`, for when the `options` list is not enough:\n * a label with markup in it, or content between the choices. The group holds\n * the value, so a `RadioGroup.Item` only works inside one.\n *\n * ```tsx\n * <RadioGroup.Root label=\"Betaling\" value={value} onValueChange={setValue}>\n * <RadioGroup.Item value=\"monthly\" label=\"Månedlig\" />\n * <RadioGroup.Item value=\"yearly\" label={<>Årlig <Chip label=\"Spar 10 %\" /></>} />\n * </RadioGroup.Root>\n * ```\n */\nexport function Item<T extends RadioValue = RadioValue>({\n value,\n label,\n disabled = false,\n size,\n id,\n \"aria-label\": ariaLabel,\n className,\n style,\n ref,\n}: ItemProps<T>) {\n const group = useRadioGroupContext(\"RadioGroup.Item\");\n const bare = label == null;\n\n const circle = (\n <BaseRadio.Root\n // A real `<button>`, so the `<label>` around it names it natively: in\n // the server markup, and as one element for `getByLabelText`.\n nativeButton\n render={<button type=\"button\" />}\n id={id}\n inputRef={ref}\n value={value}\n disabled={disabled}\n aria-label={ariaLabel}\n data-size={size ?? group.size ?? \"md\"}\n className={\n bare ? join(radioCircleClassName, className) : radioCircleClassName\n }\n style={bare ? style : undefined}\n >\n <BaseRadio.Indicator keepMounted className={radioDotClassName} />\n </BaseRadio.Root>\n );\n\n if (bare) return circle;\n\n return (\n <label className={join(rowStyles.Row, className)} style={style}>\n {circle}\n <span className={rowStyles.Text}>{label}</span>\n </label>\n );\n}\n\nexport type RootProps<T extends RadioValue = string> =\n RadioGroupBaseProps<T> & {\n /** The choices, each rendered as a radio with its label. */\n options?: Option<T>[];\n /**\n * Size of the radio circles. A `size` on a `RadioGroup.Item` wins.\n * @default \"md\"\n */\n size?: RadioSize;\n };\n\n/**\n * A question with one answer, as a list of radios, on Base UI's accessible\n * `RadioGroup` primitive. The label, the helper text and the error state are\n * handled for you. Arrow keys move between the choices.\n *\n * Pass the choices as `options`. Values are compared with `===` and can be\n * strings, numbers or booleans, so a yes / no question can hold a real\n * `boolean`. For choices that need more than a label, pass `RadioGroup.Item`\n * children.\n *\n * ```tsx\n * <RadioGroup.Root\n * label=\"Hvor ofte vil du betale?\"\n * options={[\n * { value: \"monthly\", label: \"Månedlig\" },\n * { value: \"yearly\", label: \"Årlig\" },\n * ]}\n * value={value}\n * onValueChange={setValue}\n * />\n * ```\n */\nexport function Root<T extends RadioValue = string>({\n options,\n children,\n ...rest\n}: RootProps<T>) {\n return (\n <RadioGroupRoot {...rest} optionsClassName={styles.Options}>\n {options?.map((option) => (\n <Item\n key={String(option.value)}\n value={option.value}\n label={option.label}\n disabled={option.disabled}\n />\n ))}\n {children}\n </RadioGroupRoot>\n );\n}\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n */\n\n/* The cards of a `RadioCard.Group`, in as many columns as asked for. */\n.Options {\n display: grid;\n /* A list of cards sits tighter than a grid of them. */\n gap: var(--lea-spacing-1);\n grid-template-columns: repeat(var(--radio-card-columns, 1), 1fr);\n}\n\n.Options[data-columns=\"many\"] {\n gap: var(--lea-spacing-1-5);\n}\n\n.Card {\n --radio-card-padding-block: var(--lea-spacing-2);\n\n position: relative;\n display: flex;\n align-items: center;\n justify-content: flex-start;\n padding-block: var(--radio-card-padding-block);\n padding-inline: var(--lea-spacing-2);\n border: 1px solid var(--lea-palette-border-base);\n border-radius: var(--lea-radius-xl);\n background-color: var(--lea-palette-bg-base);\n color: var(--lea-palette-text-base);\n cursor: pointer;\n font-size: var(--lea-text-body-md-font-size);\n font-weight: var(--lea-text-body-md-font-weight);\n gap: var(--lea-spacing-1-5);\n letter-spacing: var(--lea-text-body-md-letter-spacing);\n line-height: var(--lea-text-body-md-line-height);\n text-align: start;\n transition: background-color var(--lea-duration-normal)\n var(--lea-easing-standard),\n border-color var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n.Card[data-size=\"sm\"] {\n --radio-card-padding-block: var(--lea-spacing-1-5);\n}\n\n.Card[data-orientation=\"vertical\"] {\n --radio-card-padding-block: var(--lea-spacing-4);\n\n flex-direction: column;\n justify-content: center;\n text-align: center;\n}\n\n.Card[data-orientation=\"vertical\"][data-size=\"sm\"] {\n --radio-card-padding-block: var(--lea-spacing-2);\n}\n\n@media (hover: hover) {\n .Card:hover:not(:has(.Radio[data-disabled])) {\n border-color: var(--lea-palette-border-strong);\n }\n}\n\n/*\n * The card reads its state off the radio inside it, which Base UI marks.\n * A chosen card changes border and fill, and its indicator fills in.\n */\n.Card:has(.Radio[data-checked]) {\n border-color: var(--lea-palette-primary-base);\n background-color: var(--lea-palette-bg-accent);\n}\n\n/* Keyboard focus is on the hidden radio, so the ring is drawn on the card. */\n.Card:has(.Radio:focus-visible) {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n.Card:has(.Radio[data-disabled]) {\n cursor: not-allowed;\n opacity: 0.6;\n}\n\n/*\n * The real control. It is there for the keyboard and for assistive\n * technology, and takes no room: the card around it is what you see and click.\n */\n.Radio {\n position: absolute;\n overflow: hidden;\n width: 1px;\n height: 1px;\n padding: 0;\n border: 0;\n margin: 0;\n clip-path: inset(50%);\n white-space: nowrap;\n}\n\n.Card:has(.Radio[data-checked]) .Indicator {\n border-color: var(--lea-palette-primary-base);\n background-color: var(--lea-palette-primary-base);\n}\n\n.Card:has(.Radio[data-checked]) .Indicator > * {\n scale: 1;\n}\n\n.Icon {\n flex-shrink: 0;\n color: var(--lea-palette-text-base);\n}\n\n.Content {\n display: flex;\n min-width: 0;\n flex: 1;\n flex-direction: column;\n align-items: flex-start;\n /* 2px between the two lines, tighter than any step: they read as one block. */\n gap: calc(var(--lea-spacing) * 0.25);\n text-align: start;\n}\n\n.Card[data-orientation=\"vertical\"] .Content {\n flex: none;\n align-items: center;\n text-align: center;\n}\n\n.Label {\n display: block;\n color: var(--lea-palette-text-base);\n font-size: var(--lea-text-label-lg-font-size);\n font-weight: var(--lea-text-label-lg-font-weight);\n letter-spacing: var(--lea-text-label-lg-letter-spacing);\n line-height: var(--lea-text-label-lg-line-height);\n}\n\n.Description {\n display: block;\n color: var(--lea-palette-text-subtle);\n font-size: var(--lea-text-body-sm-font-size);\n font-weight: var(--lea-text-body-sm-font-weight);\n letter-spacing: var(--lea-text-body-sm-letter-spacing);\n line-height: var(--lea-text-body-sm-line-height);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Card {\n transition-duration: 0.01ms;\n }\n}\n","\"use client\";\n\nimport { Radio as BaseRadio } from \"@base-ui/react/radio\";\nimport { LucideIcon } from \"lucide-react\";\nimport type { CSSProperties, ReactNode, Ref } from \"react\";\n\nimport styles from \"./radio-card.module.css\";\nimport type { Option } from \"../radio/radio\";\nimport { RadioCircle, type RadioSize } from \"../radio-circle\";\nimport {\n RadioGroupRoot,\n useRadioGroupContext,\n type RadioCardSize,\n type RadioGroupBaseProps,\n type RadioValue,\n} from \"../radio-group-root\";\n\nconst join = (...parts: (string | false | null | undefined)[]) =>\n parts.filter(Boolean).join(\" \") || undefined;\n\nexport type RootProps<T extends RadioValue = RadioValue> = {\n /** What `onValueChange` on the group is called with when this card is picked. */\n value: T;\n /** `\"horizontal\"` (indicator + text in a row) or `\"vertical\"` (icon stacked above text). */\n orientation?: \"horizontal\" | \"vertical\";\n /** Disables this card only. */\n disabled?: boolean;\n /** Padding of the card. Defaults to the group's `size`. */\n size?: RadioCardSize;\n /** Class on the card. */\n className?: string;\n /** Inline style on the card. */\n style?: CSSProperties;\n /** The card, which is a `<label>` around the radio. */\n ref?: Ref<HTMLLabelElement>;\n children?: ReactNode;\n};\n\n/**\n * One choice in a `RadioCard.Group`, for when the `options` list is not\n * enough. The whole card is clickable, and it is built from composable\n * pieces: `RadioCard.Content` (wraps the text), `RadioCard.Indicator`\n * (optional), `RadioCard.Icon`, `RadioCard.Label`, `RadioCard.Description`.\n *\n * Children are laid out as plain flex items, so the indicator is just another\n * child — place it before or after `RadioCard.Content` and it sits at that edge\n * (the content grows to fill the row, pushing the indicator to the side).\n *\n * The group holds the value, so a `RadioCard.Root` only works inside one.\n *\n * ```tsx\n * <RadioCard.Group label=\"Nedbetalingstid\" value={value} onValueChange={setValue}>\n * <RadioCard.Root value={10}>\n * <RadioCard.Content>\n * <RadioCard.Label>1316 kr/mnd</RadioCard.Label>\n * <RadioCard.Description>Over 10 år</RadioCard.Description>\n * </RadioCard.Content>\n * <RadioCard.Indicator />\n * </RadioCard.Root>\n * </RadioCard.Group>\n * ```\n */\nexport function Root<T extends RadioValue = RadioValue>({\n value,\n orientation = \"horizontal\",\n disabled = false,\n size,\n className,\n style,\n ref,\n children,\n}: RootProps<T>) {\n const group = useRadioGroupContext(\"RadioCard.Root\");\n\n return (\n <label\n ref={ref}\n className={join(styles.Card, className)}\n style={style}\n data-orientation={orientation}\n data-size={size ?? group.cardSize ?? \"lg\"}\n >\n <BaseRadio.Root\n // A real `<button>`, so the card, which is its `<label>`, names it\n // natively: in the server markup, and as one element for\n // `getByLabelText`.\n nativeButton\n render={<button type=\"button\" />}\n value={value}\n disabled={disabled}\n className={styles.Radio}\n />\n {children}\n </label>\n );\n}\n\nexport type IndicatorProps = {\n /** Size of the circle. @default \"md\" */\n size?: RadioSize;\n};\n\n/**\n * Optional radio dot for a `RadioCard.Root`. It is a picture of the card's\n * state, drawn like a `RadioGroup.Item`; the card's own radio is what gets\n * picked. Omit it for icon-only cards.\n */\nexport const Indicator = ({ size }: IndicatorProps) => (\n <RadioCircle size={size} className={styles.Indicator} />\n);\n\nexport type IconProps = {\n /** Icon box size (predefined, overridable). */\n size?: number | string;\n icon: LucideIcon;\n};\n\n/**\n * Fixed-size icon slot so every card's icon renders at the same size regardless\n * of the icon passed in.\n */\nexport const Icon = ({ size = 24, icon }: IconProps) => {\n const Glyph = icon;\n return <Glyph size={size} aria-hidden className={styles.Icon} />;\n};\n\n/**\n * Wraps the text lines (`RadioCard.Label` / `RadioCard.Description`) into a\n * column. Grows to fill the row in `horizontal` orientation — so a sibling\n * `RadioCard.Indicator` is pushed to the opposite edge — and centers in\n * `vertical`.\n */\nexport const Content = ({\n children,\n className,\n style,\n}: {\n children: ReactNode;\n className?: string;\n style?: CSSProperties;\n}) => (\n <span className={join(styles.Content, className)} style={style}>\n {children}\n </span>\n);\n\n/** Primary line of a `RadioCard.Root`. */\nexport const Label = ({ children }: { children: ReactNode }) => (\n <span className={styles.Label}>{children}</span>\n);\n\n/** Secondary, muted line of a `RadioCard.Root`. */\nexport const Description = ({ children }: { children: ReactNode }) => (\n <span className={styles.Description}>{children}</span>\n);\n\nexport type GroupProps<T extends RadioValue = string> =\n RadioGroupBaseProps<T> & {\n /**\n * The choices, each rendered as a card with a radio dot and its label.\n * For icons, descriptions or cards without the dot, pass `RadioCard.Root`\n * children instead.\n */\n options?: Option<T>[];\n /** How many cards sit side by side. @default 1 */\n columns?: number;\n /**\n * Padding of the cards. A `size` on a `RadioCard.Root` wins.\n * @default \"lg\"\n */\n size?: RadioCardSize;\n };\n\n/**\n * A question with one answer, as a set of selectable cards, on Base UI's\n * accessible `RadioGroup` primitive. The label, the tooltip, the helper text\n * and the error state are handled for you. Arrow keys move between the cards.\n *\n * Pass the choices as `options`. Values are compared with `===` and can be\n * strings, numbers or booleans, so a yes / no question can hold a real\n * `boolean`:\n *\n * ```tsx\n * <RadioCard.Group\n * label=\"Vil du legge til en medsøker?\"\n * columns={2}\n * size=\"sm\"\n * options={[\n * { value: true, label: \"Ja\" },\n * { value: false, label: \"Nei\" },\n * ]}\n * value={hasCoApplicant}\n * onValueChange={setHasCoApplicant}\n * />\n * ```\n *\n * For richer cards, pass `RadioCard.Root` children.\n */\nexport function Group<T extends RadioValue = string>({\n options,\n columns = 1,\n size,\n children,\n ...rest\n}: GroupProps<T>) {\n return (\n <RadioGroupRoot\n {...rest}\n cardSize={size}\n optionsClassName={styles.Options}\n optionsStyle={{ \"--radio-card-columns\": columns } as CSSProperties}\n optionsDataColumns={columns > 1 ? \"many\" : undefined}\n >\n {options?.map((option) => (\n <Root\n key={String(option.value)}\n value={option.value}\n disabled={option.disabled}\n >\n <Indicator />\n {option.label}\n </Root>\n ))}\n {children}\n </RadioGroupRoot>\n );\n}\n\nexport type { Option, RadioCardSize as Size, RadioValue as Value };\n","\"use client\";\n\nimport { useState } from \"react\";\n\nimport {\n OptionIcon,\n SelectField,\n SelectFieldItem,\n type SelectFieldBaseProps,\n type SelectOption,\n type SelectSize,\n} from \"../select-field\";\n\nexport type { SelectOption, SelectSize };\n\nexport type SelectProps<Value extends string = string> =\n SelectFieldBaseProps & {\n /** The choices. */\n options: readonly SelectOption<Value>[];\n /** The chosen value (controlled). `\"\"` means nothing is chosen. */\n value?: Value | \"\";\n /** The chosen value when first rendered, for uncontrolled use. */\n defaultValue?: Value | \"\";\n /** Called with the value of the option the user picks. */\n onValueChange?: (value: Value) => void;\n /**\n * `\"sm\"` is a compact 32px field for a narrow place, with the `label`\n * as a line above it instead of floating inside.\n * @default \"md\"\n */\n size?: SelectSize;\n };\n\n/**\n * Select on Base UI's `Select`, in the same box as `Input`. The label, the\n * helper text and the ids that link them to the control are all rendered and\n * wired for you.\n *\n * ```tsx\n * <Select\n * label=\"Land\"\n * options={[\n * { value: \"no\", label: \"Norge\" },\n * { value: \"se\", label: \"Sverige\" },\n * ]}\n * value={country}\n * onValueChange={setCountry}\n * />\n * ```\n *\n * Values are strings, and `\"\"` means nothing is chosen. Written with\n * `as const`, the options give `value` and `onValueChange` their type:\n * `\"no\" | \"se\"` here.\n *\n * An option can carry an `icon` (a flag, a logo), shown before its label in\n * the list and, once it is chosen, in the field. `size=\"sm\"` is a compact\n * 32px field with its label above it.\n */\nexport function Select<Value extends string = string>({\n options,\n value,\n defaultValue = \"\",\n onValueChange,\n startAdornment,\n ...rest\n}: SelectProps<Value>) {\n const isControlled = value !== undefined;\n const [internal, setInternal] = useState<Value | \"\">(defaultValue);\n const current = isControlled ? value : internal;\n\n const chosen = options.find((option) => option.value === current);\n\n return (\n <SelectField\n {...rest}\n startAdornment={\n startAdornment ??\n (chosen?.icon != null ? <OptionIcon>{chosen.icon}</OptionIcon> : null)\n }\n multiple={false}\n // Base UI's \"nothing chosen\" is `null`; an option may still have `\"\"`\n // as its value, e.g. \"None\", and is then a choice like any other.\n value={chosen ? chosen.value : null}\n onValueChange={(next) => {\n const nextValue = (next ?? \"\") as Value;\n if (!isControlled) setInternal(nextValue);\n onValueChange?.(nextValue);\n }}\n filled={chosen != null}\n display={chosen?.label}\n displayHidden\n valueText={chosen?.label ?? \"\"}\n >\n {options.map((option) => (\n <SelectFieldItem key={option.value} option={option} />\n ))}\n </SelectField>\n );\n}\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n *\n * The track, the min / max row and the helper text are styled in\n * slider-parts.module.css, which `AmountSlider` shares.\n */\n\n.Slider {\n display: flex;\n width: 100%;\n flex-direction: column;\n color: var(--lea-palette-text-base);\n font-size: var(--lea-text-body-md-font-size);\n font-weight: var(--lea-text-body-md-font-weight);\n line-height: var(--lea-text-body-md-line-height);\n letter-spacing: var(--lea-text-body-md-letter-spacing);\n}\n\n.Header {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: calc(var(--lea-spacing) * 2);\n}\n\n.Label {\n min-width: 0;\n font-weight: var(--lea-font-weight-medium);\n}\n\n.Value {\n margin-inline-start: auto;\n font-weight: var(--lea-font-weight-semibold);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n}\n\n/*\n * `color=\"contrast\"`, for a dark surface (the website's Calculator): white\n * text, fill and thumb, the thumb ringed in near-black as on the website.\n * The rail is the text colour at 40 %, so it shows on `bg.contrast` and on\n * `bg.contrast-accent` with the Calculator's 30 % black over it (3 : 1 or\n * more), where the website's black rail disappears on black. The parts read\n * these in slider-parts.module.css.\n */\n.Slider[data-color=\"contrast\"] {\n --slider-rail: color-mix(\n in srgb,\n var(--lea-palette-text-contrast) 40%,\n transparent\n );\n --slider-fill: var(--lea-palette-text-contrast);\n --slider-thumb-ring: var(--lea-palette-bg-contrast);\n --slider-focus: var(--lea-palette-text-contrast);\n --slider-secondary: color-mix(\n in srgb,\n var(--lea-palette-text-contrast) 75%,\n transparent\n );\n\n color: var(--lea-palette-text-contrast);\n}\n","\"use client\";\n\nimport { Field } from \"@base-ui/react/field\";\nimport { Slider as BaseSlider } from \"@base-ui/react/slider\";\nimport { useId, type ReactNode } from \"react\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport type { ColorRoles } from \"../../color-roles\";\nimport {\n renderWithoutElement,\n SliderHelperText,\n SliderRange,\n SliderTrack,\n} from \"../slider-parts\";\nimport styles from \"./slider.module.css\";\n\n/**\n * Requires an accessible name so the slider is never nameless. Provide a\n * visible `label`, or an `aria-label` when the design has none.\n */\n/**\n * `\"primary\"` for a light surface; `\"contrast\"` for a dark one\n * (`bg.contrast`, `bg.contrast-accent`, the website `Calculator`'s panel).\n */\nexport type SliderColor = ColorRoles<\"primary\" | \"contrast\">;\n\ntype SliderAccessibleName =\n | { label: ReactNode; \"aria-label\"?: never }\n | { label?: never; \"aria-label\": string };\n\nexport type SliderProps = Omit<\n BaseSlider.Root.Props<number>,\n | \"children\"\n | \"orientation\"\n | \"minStepsBetweenValues\"\n | \"thumbAlignment\"\n | \"thumbCollisionBehavior\"\n | \"aria-label\"\n | \"aria-labelledby\"\n> &\n SliderAccessibleName & {\n /** Helper / error text shown below the slider and linked via `aria-describedby`. */\n helperText?: ReactNode;\n /** Puts the slider and its helper text into the error state. */\n error?: boolean;\n /**\n * `\"primary\"`: purple on a light surface. `\"contrast\"`: white text, a\n * white fill and thumb, for a dark surface such as the website's\n * `Calculator`.\n * @default \"primary\"\n */\n color?: SliderColor;\n };\n\n/**\n * A single-value slider built on Base UI's accessible `Slider` primitive.\n *\n * The label, the current value, the `min` / `max` under each end of the track\n * and the helper text are all rendered and wired internally: the label names\n * the control, the helper text describes it, and `error` marks it invalid and\n * announces the helper text. Base UI renders a native range input, so arrow\n * keys, Page Up / Down, Home and End work, and the value is submitted when\n * `name` is set.\n *\n * Pass `format` and `locale` to control how the value, `min` and `max` are\n * written and announced. On a dark surface, pass `color=\"contrast\"`.\n *\n * ```tsx\n * <Slider\n * label=\"Lånebeløp\"\n * min={50000}\n * max={600000}\n * step={5000}\n * defaultValue={150000}\n * locale=\"nb-NO\"\n * format={{ style: \"currency\", currency: \"NOK\", maximumFractionDigits: 0 }}\n * onValueChange={(value) => setAmount(value)}\n * />\n * ```\n */\nexport function Slider({\n label,\n \"aria-label\": ariaLabel,\n helperText,\n error = false,\n color = \"primary\",\n min = 0,\n max = 100,\n format,\n locale,\n className,\n ...rest\n}: SliderProps) {\n // Base UI's `Slider.Label` only links itself after mount, which leaves the\n // server-rendered control nameless, so the label is wired by id here.\n const labelId = useId();\n const helperTextId = useId();\n\n return (\n // The field renders no element of its own, so `className`, `ref` and the\n // rest land on the slider.\n <Field.Root invalid={error} render={renderWithoutElement}>\n <BaseSlider.Root\n {...rest}\n min={min}\n max={max}\n format={format}\n locale={locale}\n aria-labelledby={label != null ? labelId : undefined}\n data-color={color}\n className={mergeClassName(styles.Slider, className)}\n >\n <div className={styles.Header}>\n {label != null && (\n <div id={labelId} className={styles.Label}>\n {label}\n </div>\n )}\n <BaseSlider.Value className={styles.Value} />\n </div>\n <SliderTrack\n aria-label={ariaLabel}\n aria-describedby={helperText != null ? helperTextId : undefined}\n />\n <SliderRange min={min} max={max} format={format} locale={locale} />\n {helperText != null && (\n <SliderHelperText id={helperTextId} error={error}>\n {helperText}\n </SliderHelperText>\n )}\n </BaseSlider.Root>\n </Field.Root>\n );\n}\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n */\n\n.Switch {\n display: inline-flex;\n width: 2.5rem;\n height: 1.5rem;\n flex-shrink: 0;\n align-items: center;\n padding: 0.125rem;\n border: none;\n border-radius: var(--lea-radius-full);\n background-color: var(--lea-color-neutral-200);\n cursor: pointer;\n transition: background-color var(--lea-duration-normal)\n var(--lea-easing-standard);\n}\n\n.Switch[data-checked] {\n background-color: var(--lea-palette-primary-base);\n}\n\n.Switch:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n.Switch[data-disabled] {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n.Thumb {\n display: block;\n width: 1.25rem;\n height: 1.25rem;\n border-radius: var(--lea-radius-full);\n background-color: var(--lea-palette-bg-base);\n transition: translate var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n.Thumb[data-checked] {\n translate: 1rem 0;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Switch,\n .Thumb {\n transition-duration: 0.01ms;\n }\n}\n","\"use client\";\n\nimport { Switch as BaseSwitch } from \"@base-ui/react/switch\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport styles from \"./switch.module.css\";\n\n/**\n * Requires an accessible name so the switch is never nameless. Provide exactly\n * one of `aria-label` or `aria-labelledby`.\n */\ntype SwitchAccessibleName =\n | { \"aria-label\": string; \"aria-labelledby\"?: never }\n | { \"aria-labelledby\": string; \"aria-label\"?: never };\n\nexport type SwitchProps = Omit<\n BaseSwitch.Root.Props,\n \"children\" | \"aria-label\" | \"aria-labelledby\"\n> &\n SwitchAccessibleName;\n\n/**\n * An on/off toggle built on Base UI's accessible `Switch` primitive.\n *\n * Base UI renders the `switch` role, keeps `aria-checked` in sync and submits\n * a hidden input when `name` is set. The thumb is rendered internally so it\n * can never be left out, and the slide respects `prefers-reduced-motion`.\n *\n * Unlike a checkbox, a switch takes effect immediately — use it for settings\n * that apply on toggle, not for values confirmed by a submit button.\n *\n * ```tsx\n * <Switch\n * aria-label=\"Analyse-informasjonskapsler\"\n * defaultChecked\n * onCheckedChange={(checked) => save(checked)}\n * />\n * ```\n */\nexport function Switch({ className, ...rest }: SwitchProps) {\n return (\n <BaseSwitch.Root\n {...rest}\n className={mergeClassName(styles.Switch, className)}\n >\n <BaseSwitch.Thumb className={styles.Thumb} />\n </BaseSwitch.Root>\n );\n}\n","/*\n * Responsive props arrive as inline `--_<prop>-<breakpoint>` properties, set\n * only for the breakpoints the consumer gave. Each media query falls back\n * through the smaller breakpoints, so `{ base: 1, md: 3 }` holds 1 until `md`.\n *\n * Custom properties inherit, so each rule resets every breakpoint first: a\n * nested `Grid` or `GridItem` would otherwise take its parent's columns, gap\n * or span. The inline properties of the props that are passed beat it.\n */\n.Grid {\n --_columns-base: initial;\n --_columns-sm: initial;\n --_columns-md: initial;\n --_columns-lg: initial;\n --_columns-xl: initial;\n --_gap-base: initial;\n --_gap-sm: initial;\n --_gap-md: initial;\n --_gap-lg: initial;\n --_gap-xl: initial;\n --_columns: var(--_columns-base, 12);\n --_gap: var(--_gap-base, 0);\n\n display: grid;\n min-width: 0;\n gap: calc(var(--lea-spacing) * var(--_gap));\n grid-template-columns: repeat(var(--_columns), minmax(0, 1fr));\n}\n\n/* A `GridItem` given no `span` takes the whole row; plain children take one column. */\n.GridItem {\n --_span-base: initial;\n --_span-sm: initial;\n --_span-md: initial;\n --_span-lg: initial;\n --_span-xl: initial;\n --_span: var(--_span-base, 1 / -1);\n\n min-width: 0;\n grid-column: var(--_span);\n}\n\n@media (min-width: 600px) {\n .Grid {\n --_columns: var(--_columns-sm, var(--_columns-base, 12));\n --_gap: var(--_gap-sm, var(--_gap-base, 0));\n }\n\n .GridItem {\n --_span: var(--_span-sm, var(--_span-base, 1 / -1));\n }\n}\n\n@media (min-width: 900px) {\n .Grid {\n --_columns: var(--_columns-md, var(--_columns-sm, var(--_columns-base, 12)));\n --_gap: var(--_gap-md, var(--_gap-sm, var(--_gap-base, 0)));\n }\n\n .GridItem {\n --_span: var(--_span-md, var(--_span-sm, var(--_span-base, 1 / -1)));\n }\n}\n\n@media (min-width: 1200px) {\n .Grid {\n --_columns: var(\n --_columns-lg,\n var(--_columns-md, var(--_columns-sm, var(--_columns-base, 12)))\n );\n --_gap: var(--_gap-lg, var(--_gap-md, var(--_gap-sm, var(--_gap-base, 0))));\n }\n\n .GridItem {\n --_span: var(\n --_span-lg,\n var(--_span-md, var(--_span-sm, var(--_span-base, 1 / -1)))\n );\n }\n}\n\n@media (min-width: 1536px) {\n .Grid {\n --_columns: var(\n --_columns-xl,\n var(\n --_columns-lg,\n var(--_columns-md, var(--_columns-sm, var(--_columns-base, 12)))\n )\n );\n --_gap: var(\n --_gap-xl,\n var(--_gap-lg, var(--_gap-md, var(--_gap-sm, var(--_gap-base, 0))))\n );\n }\n\n .GridItem {\n --_span: var(\n --_span-xl,\n var(\n --_span-lg,\n var(--_span-md, var(--_span-sm, var(--_span-base, 1 / -1)))\n )\n );\n }\n}\n","import type { CSSProperties } from \"react\";\n\nimport type { Breakpoint } from \"../../theme/tokens/breakpoints\";\n\n/** `base` applies from zero width; each other key from its breakpoint up. */\nexport type ResponsiveKey = \"base\" | Exclude<Breakpoint, \"xs\">;\n\n/**\n * A single value, or one per breakpoint, mobile-first. Unset breakpoints keep\n * the value of the nearest smaller one: `{ base: 1, md: 3 }` is 1 below `md`\n * and 3 from `md` up.\n */\nexport type Responsive<T> = T | Partial<Record<ResponsiveKey, T>>;\n\nconst isPerBreakpoint = <T>(\n value: Responsive<T>,\n): value is Partial<Record<ResponsiveKey, T>> =>\n typeof value === \"object\" && value !== null;\n\n/**\n * Writes a responsive prop as inline custom properties — `--_gap-base`,\n * `--_gap-md`, … — which the component's CSS Module picks up in its media\n * queries. Only the breakpoints that are set are written; the stylesheet\n * falls back through the smaller ones.\n *\n * Internal helper — not part of the public API.\n */\nexport function responsiveVars<T>(\n name: string,\n value: Responsive<T> | undefined,\n format: (value: T) => string | number = String,\n): CSSProperties {\n if (value === undefined) return {};\n\n const entries = isPerBreakpoint(value)\n ? Object.entries(value as Record<string, T | undefined>)\n : [[\"base\", value] as const];\n\n const vars: Record<string, string | number> = {};\n for (const [key, entry] of entries) {\n if (entry !== undefined) vars[`--_${name}-${key}`] = format(entry);\n }\n\n return vars as CSSProperties;\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\n\nimport type { SpacingFactor } from \"../../../theme/token-types\";\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport styles from \"./grid.module.css\";\nimport { type Responsive, responsiveVars } from \"../responsive\";\n\nexport type GridProps = Omit<useRender.ComponentProps<\"div\">, \"ref\"> & {\n /**\n * Number of equal-width columns.\n * @default 12\n */\n columns?: Responsive<number>;\n /**\n * Space between rows and columns, as a multiple of the spacing step\n * (`spacing(n)`).\n * @default 0\n */\n gap?: Responsive<SpacingFactor>;\n};\n\n/**\n * A grid of equal-width columns. Children fill one column each; wrap them in\n * `GridItem` to span several. Both props take a value per breakpoint,\n * mobile-first:\n *\n * ```tsx\n * <Grid columns={{ base: 1, sm: 2, lg: 3 }} gap={2}>\n * <Card />\n * <Card />\n * <Card />\n * </Grid>\n * ```\n */\nexport function Grid({\n columns,\n gap,\n render,\n className,\n style,\n ...rest\n}: GridProps) {\n return useRender({\n render,\n props: {\n ...rest,\n className: mergeClassName(styles.Grid, className)({}),\n style: {\n ...responsiveVars(\"columns\", columns),\n ...responsiveVars(\"gap\", gap),\n ...style,\n },\n },\n });\n}\n\nexport type GridItemSpan = number | \"full\";\n\nexport type GridItemProps = Omit<useRender.ComponentProps<\"div\">, \"ref\"> & {\n /**\n * How many of the grid's columns the item covers. `\"full\"` covers the whole\n * row whatever the column count.\n * @default \"full\"\n */\n span?: Responsive<GridItemSpan>;\n};\n\nconst spanValue = (span: GridItemSpan) =>\n span === \"full\" ? \"1 / -1\" : `span ${span} / span ${span}`;\n\n/**\n * An item in a `Grid`. It takes the whole row until `span` says otherwise, so\n * layouts stay single-column on small screens by default:\n *\n * ```tsx\n * <Grid gap={3}>\n * <GridItem span={{ md: 8 }}>Main content</GridItem>\n * <GridItem span={{ md: 4 }}>Sidebar</GridItem>\n * </Grid>\n * ```\n */\nexport function GridItem({\n span,\n render,\n className,\n style,\n ...rest\n}: GridItemProps) {\n return useRender({\n render,\n props: {\n ...rest,\n className: mergeClassName(styles.GridItem, className)({}),\n style: { ...responsiveVars(\"span\", span, spanValue), ...style },\n },\n });\n}\n","/*\n * Responsive props arrive as inline `--_<prop>-<breakpoint>` properties, set\n * only for the breakpoints the consumer gave. Each media query falls back\n * through the smaller breakpoints, so `{ base: 1, md: 3 }` holds 1 until `md`.\n *\n * Custom properties inherit, so the rule resets every breakpoint first: a\n * nested `Stack` would otherwise take its parent's direction and gap, or a\n * `Grid`'s gap. The inline properties of the props that are passed beat it.\n */\n.Stack {\n --_direction-base: initial;\n --_direction-sm: initial;\n --_direction-md: initial;\n --_direction-lg: initial;\n --_direction-xl: initial;\n --_gap-base: initial;\n --_gap-sm: initial;\n --_gap-md: initial;\n --_gap-lg: initial;\n --_gap-xl: initial;\n --_direction: var(--_direction-base, column);\n --_gap: var(--_gap-base, 0);\n\n display: flex;\n min-width: 0;\n flex-direction: var(--_direction);\n gap: calc(var(--lea-spacing) * var(--_gap));\n}\n\n@media (min-width: 600px) {\n .Stack {\n --_direction: var(--_direction-sm, var(--_direction-base, column));\n --_gap: var(--_gap-sm, var(--_gap-base, 0));\n }\n}\n\n@media (min-width: 900px) {\n .Stack {\n --_direction: var(\n --_direction-md,\n var(--_direction-sm, var(--_direction-base, column))\n );\n --_gap: var(--_gap-md, var(--_gap-sm, var(--_gap-base, 0)));\n }\n}\n\n@media (min-width: 1200px) {\n .Stack {\n --_direction: var(\n --_direction-lg,\n var(--_direction-md, var(--_direction-sm, var(--_direction-base, column)))\n );\n --_gap: var(--_gap-lg, var(--_gap-md, var(--_gap-sm, var(--_gap-base, 0))));\n }\n}\n\n@media (min-width: 1536px) {\n .Stack {\n --_direction: var(\n --_direction-xl,\n var(\n --_direction-lg,\n var(\n --_direction-md,\n var(--_direction-sm, var(--_direction-base, column))\n )\n )\n );\n --_gap: var(\n --_gap-xl,\n var(--_gap-lg, var(--_gap-md, var(--_gap-sm, var(--_gap-base, 0))))\n );\n }\n}\n\n.Stack[data-align=\"start\"] {\n align-items: flex-start;\n}\n\n.Stack[data-align=\"center\"] {\n align-items: center;\n}\n\n.Stack[data-align=\"end\"] {\n align-items: flex-end;\n}\n\n.Stack[data-align=\"stretch\"] {\n align-items: stretch;\n}\n\n.Stack[data-align=\"baseline\"] {\n align-items: baseline;\n}\n\n.Stack[data-justify=\"start\"] {\n justify-content: flex-start;\n}\n\n.Stack[data-justify=\"center\"] {\n justify-content: center;\n}\n\n.Stack[data-justify=\"end\"] {\n justify-content: flex-end;\n}\n\n.Stack[data-justify=\"between\"] {\n justify-content: space-between;\n}\n\n.Stack[data-justify=\"around\"] {\n justify-content: space-around;\n}\n\n.Stack[data-justify=\"evenly\"] {\n justify-content: space-evenly;\n}\n\n.Stack[data-wrap] {\n flex-wrap: wrap;\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\n\nimport type { SpacingFactor } from \"../../../theme/token-types\";\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport { type Responsive, responsiveVars } from \"../responsive\";\nimport styles from \"./stack.module.css\";\n\nexport type StackDirection = \"column\" | \"row\";\nexport type StackAlign = \"start\" | \"center\" | \"end\" | \"stretch\" | \"baseline\";\nexport type StackJustify =\n | \"start\"\n | \"center\"\n | \"end\"\n | \"between\"\n | \"around\"\n | \"evenly\";\n\nexport type StackProps = Omit<useRender.ComponentProps<\"div\">, \"ref\"> & {\n /**\n * Main axis. There is deliberately no reversed direction: it would make the\n * visual order differ from the reading and focus order.\n * @default \"column\"\n */\n direction?: Responsive<StackDirection>;\n /**\n * Space between children, as a multiple of the spacing step (`spacing(n)`).\n * @default 0\n */\n gap?: Responsive<SpacingFactor>;\n /** Cross-axis alignment (`align-items`). Stretches when unset. */\n align?: StackAlign;\n /** Main-axis distribution (`justify-content`). */\n justify?: StackJustify;\n /**\n * Lets children wrap onto new lines.\n * @default false\n */\n wrap?: boolean;\n};\n\n/**\n * Lays its children out in one direction with even, token-based spacing.\n * `direction` and `gap` take a value per breakpoint, mobile-first:\n *\n * ```tsx\n * <Stack direction={{ base: \"column\", md: \"row\" }} gap={{ base: 2, md: 3 }}>\n * <Card />\n * <Card />\n * </Stack>\n * ```\n *\n * Renders a `div`; pass `render` for another element, e.g.\n * `render={<ul />}` with `<li>` children.\n */\nexport function Stack({\n direction,\n gap,\n align,\n justify,\n wrap = false,\n render,\n className,\n style,\n ...rest\n}: StackProps) {\n return useRender({\n render,\n props: {\n ...rest,\n \"data-align\": align,\n \"data-justify\": justify,\n \"data-wrap\": wrap || undefined,\n className: mergeClassName(styles.Stack, className)({}),\n style: {\n ...responsiveVars(\"direction\", direction),\n ...responsiveVars(\"gap\", gap),\n ...style,\n },\n },\n });\n}\n\nexport type HStackProps = Omit<StackProps, \"direction\">;\n\n/**\n * A `Stack` fixed to `direction=\"row\"`. Children stay on one line unless\n * `wrap` is set.\n *\n * ```tsx\n * <HStack gap={1} justify=\"end\">\n * <Button variant=\"outline\">Avbryt</Button>\n * <Button>Lagre</Button>\n * </HStack>\n * ```\n */\nexport function HStack(props: HStackProps) {\n return <Stack {...props} direction=\"row\" />;\n}\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n */\n\n.Header {\n display: flex;\n width: 100%;\n align-items: center;\n justify-content: space-between;\n gap: var(--lea-spacing-2);\n}\n\n.Lead {\n display: flex;\n align-items: center;\n gap: var(--lea-spacing-2);\n}\n\n.Titles {\n display: flex;\n flex-direction: column;\n}\n\n.Title {\n margin: 0;\n color: var(--lea-palette-text-base);\n /* heading-sm's size at medium weight; the heading styles are semibold. */\n font-size: var(--lea-font-step-2);\n font-weight: var(--lea-font-weight-medium);\n line-height: var(--lea-line-height-normal);\n /* Focus is moved here from code when the step changes, never by Tab. */\n outline: none;\n}\n\n/* `titleAs=\"text\"`: body text at medium weight. */\n.Title[data-title-as=\"text\"] {\n font-size: var(--lea-font-step-0);\n}\n\n.Next {\n margin: 0;\n color: var(--lea-palette-text-subtle);\n font-size: var(--lea-text-body-sm-font-size);\n font-weight: var(--lea-text-body-sm-font-weight);\n line-height: var(--lea-text-body-sm-line-height);\n letter-spacing: var(--lea-text-body-sm-letter-spacing);\n}\n\n.Divider {\n border: 0;\n border-block-end: 1px solid var(--lea-palette-border-base);\n margin-block: var(--lea-spacing-1);\n margin-inline: 0;\n}\n\n.SrOnly {\n position: absolute;\n overflow: hidden;\n width: 1px;\n height: 1px;\n padding: 0;\n border: 0;\n margin: -1px;\n clip-path: inset(50%);\n white-space: nowrap;\n}\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n *\n * The diameter arrives inline from the `size` prop, and so does\n * `--_label-fit`: the largest font size at which the label stays inside the\n * ring.\n */\n\n.ProgressCircle {\n position: relative;\n flex-shrink: 0;\n /* The ring and the value are both drawn in the current colour. */\n color: var(--lea-palette-primary-base);\n}\n\n.ProgressCircle[data-color=\"tertiary\"] {\n color: var(--lea-palette-tertiary-base);\n}\n\n/* Turned so progress starts at 12 o'clock. */\n.Svg {\n display: block;\n transform: rotate(-90deg);\n transform-origin: center;\n}\n\n/* The unfilled part of the ring: the ring colour, faint. No opacity token is\n this strong without being the 60% highlight. */\n.Track {\n stroke-opacity: 0.2;\n}\n\n.Value {\n position: absolute;\n display: flex;\n align-items: center;\n justify-content: center;\n /* label-md's size and weight on a line as tall as the digits, so they\n centre on the ring. Smaller only where that size does not fit. */\n font-size: min(var(--lea-font-step-minus-1), var(--_label-fit));\n font-weight: var(--lea-font-weight-medium);\n line-height: var(--lea-line-height-none);\n inset: 0;\n}\n","\"use client\";\n\nimport {\n animate,\n motion,\n useMotionValue,\n useReducedMotion,\n} from \"motion/react\";\nimport { useEffect, type CSSProperties } from \"react\";\n\nimport type { ColorRoles } from \"../../color-roles\";\nimport styles from \"./progress-circle.module.css\";\n\nexport type ProgressCircleColor = ColorRoles<\"primary\" | \"tertiary\">;\n\n/*\n * How much room the `value/max` label takes, in em. The widths are the advance\n * of each digit, 0 to 9, and of the slash in Clash Grotesk at weight 500, and\n * the height is that of a digit; all measured in a browser. Measure again if\n * the font or the weight of the label changes.\n */\nconst DIGIT_WIDTHS = [\n 0.69, 0.33, 0.61, 0.6, 0.63, 0.64, 0.64, 0.54, 0.62, 0.64,\n];\nconst SLASH_WIDTH = 0.43;\nconst DIGIT_HEIGHT = 0.69;\n/** Air between the label and the ring, in px. */\nconst LABEL_GAP = 2;\n\n/**\n * The largest font size, in px, at which `text` stays inside a ring with a\n * hole of `diameter` px: the corners of the text are what reach the ring first.\n */\nconst fitFontSize = (text: string, diameter: number) => {\n const width = Array.from(text).reduce(\n (sum, char) =>\n // Anything but a digit or the slash counts as the widest digit.\n sum + (char === \"/\" ? SLASH_WIDTH : (DIGIT_WIDTHS[Number(char)] ?? 0.69)),\n 0,\n );\n const room = Math.max(diameter - 2 * LABEL_GAP, 0);\n\n return room / Math.hypot(width, DIGIT_HEIGHT);\n};\n\nexport type ProgressCircleProps = {\n /** Current value, e.g. the active step (1-based). Clamped to `0..max`. */\n value: number;\n /** Maximum value, e.g. the total number of steps. */\n max: number;\n /** Ring colour. Defaults to `primary`. */\n color?: ProgressCircleColor;\n /** Rendered diameter in px. Defaults to `48`. */\n size?: number;\n /** Ring thickness in px. Defaults to `4`. */\n strokeWidth?: number;\n /**\n * Show the `value/max` label in the centre. Defaults to `true`. The label\n * is set smaller when it would otherwise touch the ring, in a small circle\n * or with many digits. Below about 32px it is too small to read: leave it\n * out there.\n */\n showValue?: boolean;\n /**\n * Accessible name. When provided, the circle is exposed as a\n * `role=\"progressbar\"` (with `aria-valuetext={label}`) so it announces on its\n * own. When omitted, the circle is decorative (`aria-hidden`) — use this when\n * the surrounding component already conveys progress in text (as `FormStepper`\n * does).\n */\n label?: string;\n};\n\n/**\n * An animated circular progress indicator: the ring is an SVG whose stroke\n * animates as `value` changes, respecting `prefers-reduced-motion`. Colour is\n * limited to `primary` (purple) and `tertiary` (teal) so contrast stays owned\n * by the theme.\n *\n * ```tsx\n * <ProgressCircle value={2} max={5} color=\"tertiary\" label=\"Step 2 of 5\" />\n * ```\n */\nexport const ProgressCircle = ({\n value,\n max,\n color = \"primary\",\n size = 48,\n strokeWidth = 4,\n showValue = true,\n label,\n}: ProgressCircleProps) => {\n const prefersReducedMotion = useReducedMotion();\n\n const radius = (size - strokeWidth) / 2;\n const circumference = 2 * Math.PI * radius;\n const fraction = max > 0 ? Math.min(Math.max(value / max, 0), 1) : 0;\n const dashOffsetFor = (value: number) => circumference * (1 - value);\n\n const offset = useMotionValue(dashOffsetFor(0));\n\n useEffect(() => {\n const target = dashOffsetFor(fraction);\n if (prefersReducedMotion) {\n offset.set(target);\n return;\n }\n\n const controls = animate(offset, target, {\n duration: 0.6,\n ease: \"easeInOut\",\n });\n return () => controls.stop();\n // `circumference` is derived from size/strokeWidth, so it re-runs when those change.\n }, [fraction, circumference, prefersReducedMotion, offset]);\n\n const valueText = `${value}/${max}`;\n const labelFit = fitFontSize(valueText, size - 2 * strokeWidth);\n\n const accessibilityProps = label\n ? ({\n role: \"progressbar\",\n \"aria-label\": label,\n \"aria-valuemin\": 0,\n \"aria-valuemax\": max,\n \"aria-valuenow\": value,\n } as const)\n : ({ \"aria-hidden\": true } as const);\n\n return (\n <div\n {...accessibilityProps}\n className={styles.ProgressCircle}\n data-color={color}\n style={\n {\n width: size,\n height: size,\n \"--_label-fit\": `${labelFit.toFixed(2)}px`,\n } as CSSProperties\n }\n >\n <svg\n className={styles.Svg}\n width={size}\n height={size}\n viewBox={`0 0 ${size} ${size}`}\n >\n <circle\n className={styles.Track}\n cx={size / 2}\n cy={size / 2}\n r={radius}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={strokeWidth}\n />\n <motion.circle\n cx={size / 2}\n cy={size / 2}\n r={radius}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={strokeWidth}\n strokeLinecap=\"round\"\n strokeDasharray={circumference}\n style={{ strokeDashoffset: offset }}\n />\n </svg>\n {showValue && <span className={styles.Value}>{valueText}</span>}\n </div>\n );\n};\n","import { createTranslation } from \"../../i18n\";\n\n/**\n * Translated labels for `FormStepper`: the back button's accessible name, the\n * \"Next\" subtitle prefix, and the visually-hidden \"step X of Y\" context.\n * Consumers can override any of these per-instance via the `labels` prop.\n */\nexport const useStepperLabels = createTranslation({\n back: { no: \"Tilbake\", se: \"Tillbaka\", fi: \"Takaisin\", es: \"Atrás\" },\n next: { no: \"Neste\", se: \"Nästa\", fi: \"Seuraava\", es: \"Siguiente\" },\n step: { no: \"Trinn\", se: \"Steg\", fi: \"Vaihe\", es: \"Paso\" },\n of: { no: \"av\", se: \"av\", fi: \"/\", es: \"de\" },\n});\n\nexport type StepperLabels = Partial<ReturnType<typeof useStepperLabels>>;\n","\"use client\";\n\nimport { ArrowLeft } from \"lucide-react\";\nimport { useId, type ReactNode } from \"react\";\n\nimport { IconButton } from \"../../buttons/icon-button/icon-button\";\nimport styles from \"./form-stepper.module.css\";\nimport { ProgressCircle, type ProgressCircleColor } from \"../progress-circle/progress-circle\";\nimport { useStepperLabels, type StepperLabels } from \"../use-stepper-labels\";\n\nexport type FormStepperProps = {\n /** Active step (1-based). */\n currentStep: number;\n /** Total number of steps. */\n totalSteps: number;\n /** Title of the current step. Rendered as a heading by default (see `titleAs`). */\n title: ReactNode;\n /**\n * Name of the next step. When set, a translated `\"{Next}: {nextStep}\"`\n * subtitle is shown. Omit it on the last step.\n */\n nextStep?: ReactNode;\n /**\n * Called when the back button is pressed. The back button only renders when\n * this is provided, so omit it on the first step or in forms without a back\n * flow.\n */\n onBack?: () => void;\n /** Progress circle colour. Defaults to `primary`. */\n color?: ProgressCircleColor;\n /**\n * Render the title as a `heading` (default) or plain `text`. Prefer `heading`\n * so screen-reader users can navigate to it; only use `text` when another\n * heading already labels the view.\n */\n titleAs?: \"heading\" | \"text\";\n /** Heading level when `titleAs=\"heading\"`. Defaults to `h1`. */\n headingLevel?: \"h1\" | \"h2\" | \"h3\";\n /**\n * Id applied to the title. Defaults to a generated id. Use it to move focus to\n * the title when the step changes so the new step is announced.\n */\n titleId?: string;\n /** Override the built-in translated labels per-instance. */\n labels?: StepperLabels;\n /** Show the divider below the header. Defaults to `true`. */\n showDivider?: boolean;\n};\n\n/**\n * Header for a multi-step form. Shows the current step title (as a heading by\n * default), an optional `\"Next: ...\"` subtitle, an optional back button, and an\n * animated `ProgressCircle`.\n *\n * Accessibility is wired internally: the step count (`\"step X of Y\"`) is added\n * to the title as visually-hidden text and the circle is decorative, so the\n * count is announced once. The title carries `tabIndex={-1}`; move focus to it\n * (via `titleId`) whenever the step changes so screen-reader users hear the new\n * step.\n *\n * ```tsx\n * <FormStepper\n * currentStep={2}\n * totalSteps={5}\n * title=\"Civil status\"\n * nextStep=\"Employment\"\n * onBack={() => history.back()}\n * />\n * ```\n */\nexport const FormStepper = ({\n currentStep,\n totalSteps,\n title,\n nextStep,\n onBack,\n color = \"primary\",\n titleAs = \"heading\",\n headingLevel = \"h1\",\n titleId,\n labels,\n showDivider = true,\n}: FormStepperProps) => {\n const t = useStepperLabels();\n const l = { ...t, ...labels };\n const generatedId = useId();\n const resolvedTitleId = titleId ?? generatedId;\n\n const Title = titleAs === \"heading\" ? headingLevel : \"p\";\n const stepContext = `, ${l.step} ${currentStep} ${l.of} ${totalSteps}`;\n\n return (\n <>\n <div className={styles.Header}>\n <div className={styles.Lead}>\n {onBack && (\n <IconButton\n aria-label={l.back}\n onClick={onBack}\n variant=\"outline\"\n color=\"secondary\"\n >\n <ArrowLeft aria-hidden />\n </IconButton>\n )}\n <div className={styles.Titles}>\n <Title\n id={resolvedTitleId}\n tabIndex={-1}\n className={styles.Title}\n data-title-as={titleAs}\n >\n {title}\n <span className={styles.SrOnly}>{stepContext}</span>\n </Title>\n {nextStep != null && (\n <p className={styles.Next}>\n {l.next}: {nextStep}\n </p>\n )}\n </div>\n </div>\n <ProgressCircle value={currentStep} max={totalSteps} color={color} />\n </div>\n {showDivider && <hr className={styles.Divider} />}\n </>\n );\n};\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n */\n\n.LinearFormStepper {\n --stepper-bar: var(--lea-palette-primary-base);\n --stepper-track: var(--lea-palette-primary-subtle);\n\n display: flex;\n width: 100%;\n flex-direction: column;\n gap: calc(var(--lea-spacing) * 1.5);\n}\n\n.LinearFormStepper[data-color=\"tertiary\"] {\n --stepper-bar: var(--lea-palette-tertiary-base);\n --stepper-track: var(--lea-palette-tertiary-subtle);\n}\n\n.Progress {\n width: 100%;\n}\n\n.Track {\n position: relative;\n overflow: hidden;\n height: calc(var(--lea-spacing) * 0.75);\n border-radius: var(--lea-radius-full);\n background-color: var(--stepper-track);\n}\n\n.Indicator {\n height: 100%;\n border-radius: var(--lea-radius-full);\n background-color: var(--stepper-bar);\n transition: width var(--lea-duration-emphasis) var(--lea-easing-standard);\n}\n\n.Header {\n display: flex;\n align-items: center;\n gap: calc(var(--lea-spacing) * 2);\n}\n\n.Title {\n margin: 0;\n color: var(--lea-palette-text-base);\n /* heading-md's size at medium weight; the heading styles are semibold. */\n font-size: var(--lea-font-step-4);\n font-weight: var(--lea-font-weight-medium);\n line-height: var(--lea-line-height-snug);\n outline: none;\n}\n\n.TitleText {\n animation: stepper-title-in var(--lea-duration-normal)\n var(--lea-easing-standard);\n}\n\n@keyframes stepper-title-in {\n from {\n opacity: 0;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Indicator {\n transition: none;\n }\n\n .TitleText {\n animation: none;\n }\n}\n","\"use client\";\n\nimport { Progress } from \"@base-ui/react/progress\";\nimport { ArrowLeft } from \"lucide-react\";\nimport { useId, type ReactNode } from \"react\";\n\nimport { IconButton } from \"../../buttons/icon-button/icon-button\";\nimport styles from \"./linear-form-stepper.module.css\";\nimport type { ProgressCircleColor } from \"../progress-circle/progress-circle\";\nimport { useStepperLabels, type StepperLabels } from \"../use-stepper-labels\";\n\nexport type LinearFormStepperProps = {\n /** Active step (1-based). */\n currentStep: number;\n /**\n * Total number of steps. May change between renders, e.g. when the next step\n * from the API reveals a different path; the bar animates to the new value.\n */\n totalSteps: number;\n /** Title of the current step. Rendered as a heading and names the progress bar. */\n title: ReactNode;\n /**\n * Called when the back button is pressed. The back button only renders when\n * this is provided, so omit it on the first step or in forms without a back\n * flow.\n */\n onBack?: () => void;\n /**\n * Bar colour.\n * @default \"primary\"\n */\n color?: ProgressCircleColor;\n /**\n * Heading level of the title.\n * @default \"h1\"\n */\n headingLevel?: \"h1\" | \"h2\" | \"h3\";\n /**\n * Id applied to the title. Defaults to a generated id. Use it to move focus to\n * the title when the step changes so the new step is announced. Must be\n * unique in the document.\n */\n titleId?: string;\n /** Override the built-in translated labels per-instance. */\n labels?: StepperLabels;\n};\n\n/**\n * Header for a multi-step form whose steps aren't all known up front: a linear\n * progress bar above the current step's title, with an optional back button.\n * Use `FormStepper` instead when the full step list is known.\n *\n * The bar is a `progressbar` named by the title heading, with the step count\n * (`\"step X of Y\"`) as its value text. The title carries `tabIndex={-1}`; move\n * focus to it (via `titleId`) whenever the step changes so screen-reader users\n * hear the new step.\n *\n * ```tsx\n * <LinearFormStepper\n * currentStep={2}\n * totalSteps={5}\n * title=\"Refinancing\"\n * onBack={() => history.back()}\n * />\n * ```\n */\nexport const LinearFormStepper = ({\n currentStep,\n totalSteps,\n title,\n onBack,\n color = \"primary\",\n headingLevel: Heading = \"h1\",\n titleId,\n labels,\n}: LinearFormStepperProps) => {\n const t = useStepperLabels();\n const l = { ...t, ...labels };\n const generatedId = useId();\n const resolvedTitleId = titleId ?? generatedId;\n\n return (\n <div className={styles.LinearFormStepper} data-color={color}>\n <Progress.Root\n className={styles.Progress}\n value={currentStep}\n max={totalSteps}\n aria-labelledby={resolvedTitleId}\n getAriaValueText={() =>\n `${l.step} ${currentStep} ${l.of} ${totalSteps}`\n }\n >\n <Progress.Track className={styles.Track}>\n <Progress.Indicator className={styles.Indicator} />\n </Progress.Track>\n </Progress.Root>\n <div className={styles.Header}>\n {onBack && (\n <IconButton\n aria-label={l.back}\n onClick={onBack}\n variant=\"outline\"\n color=\"secondary\"\n >\n <ArrowLeft aria-hidden />\n </IconButton>\n )}\n <Heading id={resolvedTitleId} tabIndex={-1} className={styles.Title}>\n <span key={currentStep} className={styles.TitleText}>\n {title}\n </span>\n </Heading>\n </div>\n </div>\n );\n};\n","\"use client\";\n\nimport {\n createContext,\n useContext,\n useEffect,\n useState,\n type Dispatch,\n type ReactNode,\n type SetStateAction,\n} from \"react\";\n\nimport {\n LinearFormStepper,\n type LinearFormStepperProps,\n} from \"./linear-form-stepper\";\n\n/**\n * The step a flow is on, already resolved by the app. The library never\n * defines steps: the app maps its own step (e.g. from the API) to a position\n * and a translated title, then reports it.\n */\nexport type StepProgress = {\n /** Active step (1-based). */\n currentStep: number;\n /** Total number of steps. May change as the flow reveals its path. */\n totalSteps: number;\n /** Translated title of the current step. */\n title: string;\n};\n\nconst StepProgressStateContext = createContext<StepProgress | null>(null);\nconst ReportStepProgressContext = createContext<Dispatch<\n SetStateAction<StepProgress | null>\n> | null>(null);\n\nexport type StepProgressProviderProps = {\n children: ReactNode;\n /**\n * Progress to show on the first render, before any page has reported, e.g.\n * so a server-rendered layout paints the bar straight away.\n */\n initialProgress?: StepProgress;\n};\n\n/**\n * Holds the step a persistent flow header shows. Put it in the flow layout,\n * above the route's loading boundary, so the header survives step navigations\n * instead of remounting with the page, and its bar animates between steps.\n *\n * ```tsx\n * <StepProgressProvider>\n * <StepProgressHeader onBack={router.back} />\n * {children}\n * </StepProgressProvider>\n * ```\n */\nexport const StepProgressProvider = ({\n children,\n initialProgress,\n}: StepProgressProviderProps) => {\n const [progress, setProgress] = useState<StepProgress | null>(\n initialProgress ?? null,\n );\n\n return (\n <ReportStepProgressContext.Provider value={setProgress}>\n <StepProgressStateContext.Provider value={progress}>\n {children}\n </StepProgressStateContext.Provider>\n </ReportStepProgressContext.Provider>\n );\n};\n\n/**\n * The last reported step, or `null` before anything has been reported or\n * outside a `StepProgressProvider`. Use it to render `FormStepper` or a custom\n * header from the same state.\n */\nexport const useStepProgress = () => useContext(StepProgressStateContext);\n\n/**\n * Publishes the current step to the nearest `StepProgressProvider`. Pass\n * `null` while the step isn't resolved yet; nothing is reported then. Never\n * clears on unmount, so the header holds its value while the next route's\n * loading fallback is on screen.\n *\n * ```tsx\n * useReportStepProgress({ currentStep: 2, totalSteps: 5, title: t(\"income\") });\n * ```\n */\nexport const useReportStepProgress = (progress: StepProgress | null) => {\n const report = useContext(ReportStepProgressContext);\n const currentStep = progress?.currentStep;\n const totalSteps = progress?.totalSteps;\n const title = progress?.title;\n\n useEffect(() => {\n if (\n !report ||\n currentStep === undefined ||\n totalSteps === undefined ||\n title === undefined\n ) {\n return;\n }\n report({ currentStep, totalSteps, title });\n }, [report, currentStep, totalSteps, title]);\n};\n\nexport type StepProgressHeaderProps = Omit<\n LinearFormStepperProps,\n \"currentStep\" | \"totalSteps\" | \"title\"\n>;\n\n/**\n * A `LinearFormStepper` driven by the nearest `StepProgressProvider`. Renders\n * nothing until a step has been reported. Everything except the step itself\n * (`onBack`, `color`, `headingLevel`, …) is passed as props.\n */\nexport const StepProgressHeader = (props: StepProgressHeaderProps) => {\n const progress = useStepProgress();\n if (!progress) {\n return null;\n }\n\n return <LinearFormStepper {...props} {...progress} />;\n};\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n */\n\n.Tabs {\n --tabs-pill: var(--lea-palette-primary-base);\n\n /*\n * One column that may be narrower than the tabs in it. Without it the row of\n * tabs is the least width the control can have, and where the layout sizes\n * things by their content (a flex row, a centred column) it pushes the page\n * wider than the screen instead of scrolling inside its track.\n */\n display: grid;\n grid-template-columns: minmax(0, 1fr);\n}\n\n.Tabs[data-color=\"secondary\"] {\n --tabs-pill: var(--lea-palette-secondary-base);\n}\n\n/*\n * The track: a beige pill that holds the tabs. It scrolls sideways when they\n * do not fit, with the scrollbar hidden so the row stays a clean pill.\n */\n.List {\n position: relative;\n display: flex;\n overflow: auto hidden;\n max-width: 100%;\n box-sizing: border-box;\n flex-flow: row nowrap;\n align-items: center;\n padding: var(--lea-spacing-0-5);\n border-radius: var(--lea-radius-full);\n background-color: var(--lea-palette-bg-muted);\n gap: var(--lea-spacing-0-5);\n scrollbar-width: none;\n}\n\n.List::-webkit-scrollbar {\n display: none;\n}\n\n/* Above the pill, so the label of the active tab shows on top of it. */\n.Tab {\n position: relative;\n z-index: 1;\n display: inline-flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n /* 10px: with one line of text it makes a tab 36px tall, 44px with the track. */\n padding-block: calc(var(--lea-spacing) * 1.25);\n padding-inline: var(--lea-spacing-2);\n border: 0;\n border-radius: var(--lea-radius-full);\n margin: 0;\n background: none;\n /*\n * neutral-600 rather than text-subtle, so the inactive label clears 4.5:1\n * against the beige track.\n */\n color: var(--lea-color-neutral-600);\n cursor: pointer;\n font-family: inherit;\n /* label-lg on a line as tall as the text: the padding sets the height. */\n font-size: var(--lea-font-step-0);\n font-weight: var(--lea-font-weight-medium);\n gap: var(--lea-spacing-1);\n line-height: var(--lea-line-height-none);\n transition: color var(--lea-duration-normal) var(--lea-easing-standard);\n user-select: none;\n white-space: nowrap;\n}\n\n.Tab svg {\n display: block;\n flex-shrink: 0;\n}\n\n.Tab[data-active] {\n color: var(--lea-palette-text-contrast);\n}\n\n.Tab[data-disabled] {\n cursor: not-allowed;\n opacity: 0.45;\n pointer-events: none;\n}\n\n.Tab:focus-visible,\n.Panel:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n/*\n * The pill behind the active tab. Base UI writes the place and size of the\n * active tab to the `--active-tab-*` properties on this element.\n */\n.Indicator {\n position: absolute;\n z-index: 0;\n top: var(--active-tab-top);\n left: var(--active-tab-left);\n width: var(--active-tab-width);\n height: var(--active-tab-height);\n border-radius: var(--lea-radius-full);\n background-color: var(--tabs-pill);\n transition: top var(--lea-duration-normal) var(--lea-easing-standard),\n left var(--lea-duration-normal) var(--lea-easing-standard),\n width var(--lea-duration-normal) var(--lea-easing-standard),\n height var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n/* The panel takes focus, so keyboard users can reach content without controls. */\n.Panel {\n padding-block-start: var(--lea-spacing-2);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Tab,\n .Indicator {\n transition-duration: 0.01ms;\n }\n}\n","\"use client\";\n\nimport { Tabs as BaseTabs } from \"@base-ui/react/tabs\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport type { ColorRoles } from \"../../color-roles\";\nimport styles from \"./tabs.module.css\";\n\n/** The colours of the active \"pill\". */\nexport type Color = ColorRoles<\"primary\" | \"secondary\">;\n\nexport type RootProps = Omit<BaseTabs.Root.Props, \"color\"> & {\n /**\n * Colour of the active-tab \"pill\".\n * - `\"primary\"` — filled with the brand purple (default).\n * - `\"secondary\"` — filled with near-black, for a neutral surface.\n * @default \"primary\"\n */\n color?: Color;\n};\n\n/**\n * A segmented, pill-style tabs control built on Base UI's accessible `Tabs`\n * primitive. Renders a beige track with a sliding pill behind the active tab.\n *\n * Compose it from `Tabs.Root`, `Tabs.List` + `Tabs.Tab` (the tab bar) and,\n * optionally, `Tabs.Panel` for built-in content. Roles\n * (`tablist`/`tab`/`tabpanel`), roving `tabindex`, arrow-key navigation,\n * `aria-selected` and the `tab`↔`tabpanel` links are all wired by Base UI, so\n * relationships never dangle.\n *\n * The list scrolls horizontally when the tabs overflow (e.g. on mobile) and the\n * pill slide respects `prefers-reduced-motion`.\n *\n * ```tsx\n * <Tabs.Root defaultValue=\"overview\">\n * <Tabs.List aria-label=\"Account sections\">\n * <Tabs.Tab value=\"overview\">Overview</Tabs.Tab>\n * <Tabs.Tab value=\"activity\">Activity</Tabs.Tab>\n * </Tabs.List>\n * <Tabs.Panel value=\"overview\">Workspace stats and activity.</Tabs.Panel>\n * <Tabs.Panel value=\"activity\">Recent activity.</Tabs.Panel>\n * </Tabs.Root>\n * ```\n */\nexport function Root({ color = \"primary\", className, ...rest }: RootProps) {\n return (\n <BaseTabs.Root\n {...rest}\n data-color={color}\n className={mergeClassName(styles.Tabs, className)}\n />\n );\n}\n\n/**\n * Requires an accessible name so the underlying `tablist` is never nameless.\n * Provide exactly one of `aria-label` or `aria-labelledby`.\n */\ntype TabsListAccessibleName =\n | { \"aria-label\": string; \"aria-labelledby\"?: never }\n | { \"aria-labelledby\": string; \"aria-label\"?: never };\n\nexport type ListProps = Omit<\n BaseTabs.List.Props,\n \"render\" | \"aria-label\" | \"aria-labelledby\"\n> &\n TabsListAccessibleName;\n\n/**\n * Groups the `Tabs.Tab` buttons into the pill track. The sliding indicator is\n * rendered internally so it can never go missing.\n */\nexport function List({ children, className, ...rest }: ListProps) {\n return (\n <BaseTabs.List {...rest} className={mergeClassName(styles.List, className)}>\n {children}\n <Indicator />\n </BaseTabs.List>\n );\n}\n\nexport type TabProps = Omit<BaseTabs.Tab.Props, \"render\">;\n\n/**\n * An individual tab. Pass an icon and label as children; mark decorative icons\n * with `aria-hidden` so they aren't announced.\n *\n * ```tsx\n * <Tabs.Tab value=\"transactions\">\n * <Receipt size={18} aria-hidden />\n * Transaksjoner\n * </Tabs.Tab>\n * ```\n */\nexport function Tab({ className, ...rest }: TabProps) {\n return (\n <BaseTabs.Tab {...rest} className={mergeClassName(styles.Tab, className)} />\n );\n}\n\nexport type IndicatorProps = Omit<BaseTabs.Indicator.Props, \"render\">;\n\n/**\n * The sliding pill behind the active tab. `Tabs.List` already renders one, so\n * you only need this when composing a custom list.\n */\nexport function Indicator({ className, ...rest }: IndicatorProps) {\n return (\n <BaseTabs.Indicator\n {...rest}\n className={mergeClassName(styles.Indicator, className)}\n />\n );\n}\n\nexport type PanelProps = Omit<BaseTabs.Panel.Props, \"render\">;\n\n/**\n * Content shown when the `Tabs.Tab` with the matching `value` is active.\n * Optional — omit it to use `Tabs` purely as a bar that toggles content you\n * render elsewhere (drive it with `value`/`onValueChange`).\n */\nexport function Panel({ className, ...rest }: PanelProps) {\n return (\n <BaseTabs.Panel\n {...rest}\n className={mergeClassName(styles.Panel, className)}\n />\n );\n}\n","/*\n * The website's breadcrumbs: 14px medium, the links grey and the current page\n * black, a \"/\" between them, 4px apart. The grey is `neutral-600` rather than\n * `text.subtle`, so the links pass 4.5:1 on the grey and beige sections and\n * the sidebar as well as on white (6.5, 6.1, 5.4); it is also closest to the\n * website's own grey.\n */\n.Breadcrumbs {\n min-width: 0;\n}\n\n.List {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--lea-spacing-0-5);\n padding: 0;\n margin: 0;\n list-style: none;\n font-size: var(--lea-text-label-md-font-size);\n font-weight: var(--lea-text-label-md-font-weight);\n letter-spacing: var(--lea-text-label-md-letter-spacing);\n line-height: var(--lea-text-label-md-line-height);\n}\n\n.Item {\n display: inline-flex;\n min-width: 0;\n align-items: center;\n gap: var(--lea-spacing-0-5);\n}\n\n.Separator {\n color: var(--lea-color-neutral-600);\n font-weight: var(--lea-font-weight-regular);\n user-select: none;\n}\n\n.Link,\n.Text {\n color: var(--lea-color-neutral-600);\n}\n\n.Link {\n border-radius: var(--lea-radius-sm);\n text-decoration: none;\n transition: color var(--lea-duration-fast) var(--lea-easing-standard);\n}\n\n.Link:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n.Current {\n color: var(--lea-palette-text-base);\n}\n\n@media (hover: hover) {\n .Link:hover {\n color: var(--lea-palette-text-base);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Link {\n transition-duration: 0.01ms;\n }\n}\n","import { createTranslation } from \"../../../i18n\";\n\n/** The accessible name of the `nav` around `Breadcrumbs`. */\nexport const useBreadcrumbsLabels = createTranslation({\n label: {\n no: \"Brødsmulesti\",\n se: \"Brödsmulor\",\n fi: \"Murupolku\",\n es: \"Ruta de navegación\",\n },\n});\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport type { ComponentProps, ReactElement, ReactNode } from \"react\";\n\nimport styles from \"./breadcrumbs.module.css\";\nimport { useBreadcrumbsLabels } from \"./use-breadcrumbs-labels\";\n\nexport type BreadcrumbsItem = {\n /** The text of the crumb. */\n label: ReactNode;\n /** Where the crumb leads. */\n href?: string;\n /**\n * A router link to render instead of an `<a>`, carrying its own `href`:\n * `<NextLink href=\"/lan\" />`.\n */\n render?: ReactElement;\n};\n\nexport type BreadcrumbsProps = Omit<ComponentProps<\"nav\">, \"children\"> & {\n /**\n * The way from the start page to this page, in order. The last item is the\n * current page: it is plain text marked `aria-current=\"page\"`, whatever it\n * is given. An item before it without `href` or `render` is plain text too.\n */\n items: BreadcrumbsItem[];\n};\n\nfunction Crumb({ item }: { item: BreadcrumbsItem }) {\n const isLink = item.href != null || item.render != null;\n\n const link = useRender({\n enabled: isLink,\n render: item.render,\n defaultTagName: \"a\",\n props: {\n href: item.href,\n className: styles.Link,\n children: item.label,\n },\n });\n\n return link ?? <span className={styles.Text}>{item.label}</span>;\n}\n\n/**\n * Where the page sits on the website: a `nav` with a translated name around a\n * list of links, the last one the current page.\n *\n * ```tsx\n * <Breadcrumbs\n * items={[\n * { label: \"Hjem\", href: \"/\" },\n * { label: \"Lån\", render: <NextLink href=\"/lan\" /> },\n * { label: \"Forbrukslån\" },\n * ]}\n * />\n * ```\n *\n * The separators are hidden from screen readers; the list says how many\n * steps there are. Renders nothing without items.\n */\nexport function Breadcrumbs({ items, className, ...rest }: BreadcrumbsProps) {\n const labels = useBreadcrumbsLabels();\n\n if (items.length === 0) return null;\n\n const last = items.length - 1;\n\n return (\n <nav\n aria-label={labels.label}\n {...rest}\n className={[styles.Breadcrumbs, className].filter(Boolean).join(\" \")}\n >\n <ol className={styles.List}>\n {items.map((item, index) => (\n <li key={index} className={styles.Item}>\n {index > 0 && (\n <span aria-hidden className={styles.Separator}>\n /\n </span>\n )}\n {index === last ? (\n <span aria-current=\"page\" className={styles.Current}>\n {item.label}\n </span>\n ) : (\n <Crumb item={item} />\n )}\n </li>\n ))}\n </ol>\n </nav>\n );\n}\n","/*\n * The website's page numbers: 14px pills with a 1px ring, 32px wide at least\n * and 27px tall, 8px apart. The current page is filled white, as in Figma,\n * and its ring turns `border.strong`, so it stands out on a white page too and\n * not by its fill alone.\n */\n.Pagination {\n min-width: 0;\n}\n\n.List {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--lea-spacing-1);\n padding: 0;\n margin: 0;\n list-style: none;\n}\n\n.Item {\n --pagination-tint: color-mix(\n in srgb,\n var(--lea-palette-text-base) calc(var(--lea-opacity-hover) * 100%),\n transparent\n );\n\n box-sizing: border-box;\n display: inline-flex;\n min-width: var(--lea-spacing-4);\n align-items: center;\n justify-content: center;\n padding: var(--lea-spacing-0-5) var(--lea-spacing-1);\n border: 1px solid var(--lea-palette-border-base);\n border-radius: var(--lea-radius-full);\n margin: 0;\n background-color: transparent;\n color: var(--lea-palette-text-base);\n cursor: pointer;\n font-family: inherit;\n font-size: var(--lea-font-step-minus-1);\n font-weight: var(--lea-font-weight-regular);\n /* \"normal\" on the website: the tight line gives its 27px. */\n line-height: var(--lea-line-height-tight);\n text-align: center;\n text-decoration: none;\n white-space: nowrap;\n transition: background-color var(--lea-duration-normal)\n var(--lea-easing-standard),\n border-color var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n.Item:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n.Item[data-current] {\n border-color: var(--lea-palette-border-strong);\n background-color: var(--lea-palette-bg-base);\n cursor: default;\n}\n\n.Item[data-disabled] {\n cursor: not-allowed;\n opacity: 0.4;\n}\n\n@media (hover: hover) {\n .Item:not([data-current], [data-disabled]):hover {\n background-color: var(--pagination-tint);\n }\n}\n\n.Ellipsis {\n padding-inline: var(--lea-spacing-0-5);\n color: var(--lea-palette-text-base);\n font-size: var(--lea-font-step-minus-1);\n line-height: var(--lea-line-height-tight);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Item {\n transition-duration: 0.01ms;\n }\n}\n","import { createTranslation } from \"../../i18n\";\n\n/**\n * The words of `Pagination` and `PaginationDots`. `{page}` is replaced with\n * the page number.\n */\nexport const usePaginationLabels = createTranslation({\n label: { no: \"Sider\", se: \"Sidor\", fi: \"Sivut\", es: \"Páginas\" },\n previous: { no: \"Forrige\", se: \"Föregående\", fi: \"Edellinen\", es: \"Anterior\" },\n next: { no: \"Neste\", se: \"Nästa\", fi: \"Seuraava\", es: \"Siguiente\" },\n page: {\n no: \"Side {page}\",\n se: \"Sida {page}\",\n fi: \"Sivu {page}\",\n es: \"Página {page}\",\n },\n goToPage: {\n no: \"Gå til side {page}\",\n se: \"Gå till sida {page}\",\n fi: \"Siirry sivulle {page}\",\n es: \"Ir a la página {page}\",\n },\n});\n\n/** Puts the page number into a label from `usePaginationLabels`. */\nexport const withPage = (label: string, page: number) =>\n label.replace(\"{page}\", String(page));\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { useState, type ComponentProps, type ReactElement } from \"react\";\n\nimport styles from \"./pagination.module.css\";\nimport { usePaginationLabels, withPage } from \"../use-pagination-labels\";\n\nexport type PaginationProps = Omit<\n ComponentProps<\"nav\">,\n \"children\" | \"onChange\" | \"defaultValue\"\n> & {\n /** How many pages there are. */\n count: number;\n /** The current page, counted from 1. Pass it to control the pagination. */\n value?: number;\n /**\n * The page shown first when the pagination is not controlled.\n * @default 1\n */\n defaultValue?: number;\n /** Called with the page number when another page is chosen. */\n onValueChange?: (page: number) => void;\n /**\n * Renders the pages as links instead of buttons: return the link to a page,\n * with its `href`. An `<a>` or a router link:\n * `renderLink={(page) => <NextLink href={`/okonomiguiden?side=${page}`} />}`.\n * Previous and next become links too, and plain text where there is no\n * page to go to.\n */\n renderLink?: (page: number) => ReactElement;\n};\n\ntype Item = number | \"start-ellipsis\" | \"end-ellipsis\";\n\n/**\n * Every page up to seven; from eight, the first and the last page, the\n * current one with its neighbours, and an ellipsis for each gap. The same\n * pages as the website's blog list.\n */\nfunction paginationItems(page: number, count: number): Item[] {\n if (count <= 7) return Array.from({ length: count }, (_, i) => i + 1);\n\n const start = Math.max(2, page - 1);\n const end = Math.min(count - 1, page + 1);\n const items: Item[] = [1];\n\n if (start > 2) items.push(\"start-ellipsis\");\n for (let i = start; i <= end; i += 1) items.push(i);\n if (end < count - 1) items.push(\"end-ellipsis\");\n items.push(count);\n\n return items;\n}\n\nconst clamp = (page: number, count: number) =>\n Math.min(Math.max(Math.round(page), 1), Math.max(count, 1));\n\ntype PageControlProps = {\n page: number;\n label?: string;\n current?: boolean;\n disabled?: boolean;\n renderLink?: (page: number) => ReactElement;\n onSelect: (page: number) => void;\n children: string | number;\n};\n\n/**\n * One page, previous or next: a link when `renderLink` is given, otherwise\n * a button. A disabled button keeps its focus (`aria-disabled`), so pressing\n * \"Forrige\" on page 2 does not drop focus on the page.\n */\nfunction PageControl({\n page,\n label,\n current = false,\n disabled = false,\n renderLink,\n onSelect,\n children,\n}: PageControlProps) {\n const shared = {\n className: styles.Item,\n \"aria-label\": label,\n \"aria-current\": current ? (\"page\" as const) : undefined,\n \"data-current\": current ? \"\" : undefined,\n \"data-disabled\": disabled ? \"\" : undefined,\n children,\n };\n\n const isLink = renderLink != null && !disabled;\n\n const link = useRender({\n enabled: isLink,\n render: isLink ? renderLink(page) : undefined,\n defaultTagName: \"a\",\n props: { ...shared, onClick: () => onSelect(page) },\n });\n\n if (link) return link;\n\n if (renderLink != null) {\n return <span {...shared} aria-disabled=\"true\" />;\n }\n\n return (\n <button\n {...shared}\n type=\"button\"\n aria-disabled={disabled || undefined}\n onClick={() => {\n if (!disabled) onSelect(page);\n }}\n />\n );\n}\n\n/**\n * Numbered pages with previous and next, for a list too long for one page (the\n * website's blog and job lists). From eight pages it shows the first, the last\n * and the current page with its neighbours.\n *\n * ```tsx\n * const [page, setPage] = useState(1);\n * <Pagination count={12} value={page} onValueChange={setPage} />\n * ```\n *\n * Wrapped in a `nav` with a translated name; the current page carries\n * `aria-current=\"page\"`, and each number is named \"Side 3\" in the\n * `LeaProvider` language. Renders nothing for a single page.\n */\nexport function Pagination({\n count,\n value: valueProp,\n defaultValue = 1,\n onValueChange,\n renderLink,\n className,\n ...rest\n}: PaginationProps) {\n const labels = usePaginationLabels();\n const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);\n const page = clamp(valueProp ?? uncontrolledValue, count);\n\n if (count <= 1) return null;\n\n const select = (next: number) => {\n if (valueProp === undefined) setUncontrolledValue(next);\n if (next !== page) onValueChange?.(next);\n };\n\n return (\n <nav\n aria-label={labels.label}\n {...rest}\n className={[styles.Pagination, className].filter(Boolean).join(\" \")}\n >\n <ul className={styles.List}>\n <li>\n <PageControl\n page={page - 1}\n disabled={page <= 1}\n renderLink={renderLink}\n onSelect={select}\n >\n {labels.previous}\n </PageControl>\n </li>\n {paginationItems(page, count).map((item) =>\n typeof item === \"number\" ? (\n <li key={item}>\n <PageControl\n page={item}\n label={withPage(labels.page, item)}\n current={item === page}\n renderLink={renderLink}\n onSelect={select}\n >\n {item}\n </PageControl>\n </li>\n ) : (\n <li key={item} aria-hidden className={styles.Ellipsis}>\n …\n </li>\n ),\n )}\n <li>\n <PageControl\n page={page + 1}\n disabled={page >= count}\n renderLink={renderLink}\n onSelect={select}\n >\n {labels.next}\n </PageControl>\n </li>\n </ul>\n </nav>\n );\n}\n","/*\n * The website's dots: 8px, 8px apart, the current one `text.base`. Each\n * button is 16 × 24 with its dot in the middle, so the dots keep their 8px\n * gap while each answers to twice its width and three times its height. The\n * negative margins give the row the size of the dots themselves (a row of\n * four is 56 × 8), so it lines up as in Figma.\n *\n * The other dots are `neutral-500`, not the website's 30 % black: a dot is the\n * whole of its button, so it needs 3:1 against the section (4.6 on white, 4.3\n * on `bg.subtle`, 3.8 on `bg.muted`; 30 % black is 2.0).\n */\n.PaginationDots {\n display: flex;\n align-items: center;\n margin-block: calc(var(--lea-spacing) * -1);\n margin-inline: calc(var(--lea-spacing-0-5) * -1);\n}\n\n.Dot {\n --dot-color: var(--lea-color-neutral-500);\n\n display: grid;\n width: var(--lea-spacing-2);\n height: var(--lea-spacing-3);\n flex-shrink: 0;\n place-items: center;\n padding: 0;\n border: 0;\n margin: 0;\n background: none;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n}\n\n.Dot::before {\n width: var(--lea-spacing-1);\n height: var(--lea-spacing-1);\n border-radius: var(--lea-radius-full);\n background-color: var(--dot-color);\n content: \"\";\n transition: background-color var(--lea-duration-fast)\n var(--lea-easing-standard);\n}\n\n.Dot[data-current] {\n --dot-color: var(--lea-palette-text-base);\n\n cursor: default;\n}\n\n/* The ring goes round the dot, not the larger button. */\n.Dot:focus-visible {\n outline: none;\n}\n\n.Dot:focus-visible::before {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n@media (hover: hover) {\n .Dot:not([data-current]):hover {\n --dot-color: var(--lea-color-neutral-700);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Dot::before {\n transition-duration: 0.01ms;\n }\n}\n","\"use client\";\n\nimport { useState, type ComponentProps } from \"react\";\n\nimport styles from \"./pagination-dots.module.css\";\nimport { usePaginationLabels, withPage } from \"../use-pagination-labels\";\n\nexport type PaginationDotsProps = Omit<\n ComponentProps<\"div\">,\n \"children\" | \"onChange\" | \"defaultValue\"\n> & {\n /** How many pages there are. */\n count: number;\n /** The current page, counted from 1. Pass it to control the dots. */\n value?: number;\n /**\n * The page shown first when the dots are not controlled.\n * @default 1\n */\n defaultValue?: number;\n /** Called with the page number when a dot is pressed. */\n onValueChange?: (page: number) => void;\n /**\n * The accessible name of each dot, when \"Gå til side 3\" does not say\n * enough: `getItemLabel={(page) => \"Vis omtale \" + page}`.\n */\n getItemLabel?: (page: number) => string;\n};\n\n/**\n * A row of dots, one per page, for a carousel or a paged list (the website's\n * testimonials and partners). The current page is black.\n *\n * ```tsx\n * <PaginationDots count={4} value={page} onValueChange={setPage} />\n * ```\n *\n * Each dot is a button named \"Gå til side 3\" in the `LeaProvider` language,\n * the current one marked `aria-current`, in a group named \"Sider\". The dots\n * are 8px, 8px apart, and each answers to a 16 × 24 area around it. Put them\n * beside previous and next buttons, which are easier to hit. Renders nothing\n * for a single page.\n */\nexport function PaginationDots({\n count,\n value: valueProp,\n defaultValue = 1,\n onValueChange,\n getItemLabel,\n className,\n ...rest\n}: PaginationDotsProps) {\n const labels = usePaginationLabels();\n const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);\n const page = valueProp ?? uncontrolledValue;\n\n if (count <= 1) return null;\n\n const select = (next: number) => {\n if (valueProp === undefined) setUncontrolledValue(next);\n if (next !== page) onValueChange?.(next);\n };\n\n return (\n <div\n role=\"group\"\n aria-label={labels.label}\n {...rest}\n className={[styles.PaginationDots, className].filter(Boolean).join(\" \")}\n >\n {Array.from({ length: count }, (_, index) => {\n const dot = index + 1;\n const current = dot === page;\n\n return (\n <button\n key={dot}\n type=\"button\"\n className={styles.Dot}\n aria-label={getItemLabel?.(dot) ?? withPage(labels.goToPage, dot)}\n aria-current={current || undefined}\n data-current={current ? \"\" : undefined}\n onClick={() => select(dot)}\n />\n );\n })}\n </div>\n );\n}\n",".AppHeader {\n display: flex;\n flex-direction: column;\n width: 100%;\n}\n\n.AppHeader:not([data-brand-divider]) {\n border-bottom: 1px solid var(--lea-palette-border-base);\n}\n\n.AppHeaderContent {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: calc(var(--lea-spacing) * 2);\n padding-block: var(--lea-spacing);\n padding-inline: calc(var(--lea-spacing) * 2);\n}\n\n.AppHeaderContent[data-fluid] {\n max-width: none;\n}\n\n.AppHeader[data-has-children] .AppHeaderContent {\n justify-content: space-between;\n}\n\n.AppHeaderLogo {\n display: flex;\n flex-shrink: 0;\n}\n\n.AppHeaderActions {\n display: flex;\n align-items: center;\n gap: var(--lea-spacing);\n}\n","import { Children, type ReactNode } from \"react\";\n\nimport { Container } from \"../../layout/container/container\";\nimport styles from \"./app-header.module.css\";\nimport { BrandDivider } from \"../../data-display/brand-divider/brand-divider\";\n\nexport type AppHeaderProps = {\n /**\n * Shows the brand divider under the header. Without it, the header gets a\n * bottom border instead.\n * @default true\n */\n brandDivider?: boolean;\n /**\n * Lets the content span the full width instead of stopping at the `lg`\n * breakpoint. The side padding stays.\n * @default false\n */\n fluid?: boolean;\n /**\n * Content for the right-hand side, e.g. buttons. Without children the logo\n * is centred.\n */\n children?: ReactNode;\n /**\n * The logo to display in the header.\n */\n logo?: ReactNode;\n /**\n * Additional classes to apply to the header.\n */\n className?: string;\n};\n\nexport const AppHeader = ({\n brandDivider = true,\n fluid = false,\n children,\n logo,\n className,\n}: AppHeaderProps) => {\n const hasChildren = Children.toArray(children).length > 0;\n\n return (\n <header\n className={[styles.AppHeader, className].filter(Boolean).join(\" \")}\n data-brand-divider={brandDivider || undefined}\n data-has-children={hasChildren || undefined}\n >\n <Container\n className={styles.AppHeaderContent}\n data-fluid={fluid || undefined}\n >\n <div className={styles.AppHeaderLogo}>\n {logo ? logo : <img src=\"/leabank-logo.png\" alt=\"Logo\" width={100} />}\n </div>\n {hasChildren && (\n <div className={styles.AppHeaderActions}>{children}</div>\n )}\n </Container>\n {brandDivider && <BrandDivider size=\"sm\" />}\n </header>\n );\n};\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n * `--lea-spacing` is the 0.5rem base unit, multiplied to match `spacing(n)`.\n */\n\n/*\n * Each variant overrides the local properties below; the defaults are\n * `outline`. `.Item` and `.Header` only read them, so hover and active states\n * are one rule each rather than one per variant.\n */\n.Root {\n --accordion-gap: var(--lea-spacing);\n --accordion-item-bg: transparent;\n --accordion-item-border: var(--lea-palette-border-subtle);\n --accordion-item-radius: var(--lea-radius-2xl);\n --accordion-header-radius: var(--lea-radius-2xl);\n --accordion-hover-border: var(--lea-palette-border-strong);\n --accordion-hover-bg: transparent;\n --accordion-active-bg: var(--lea-color-purple-50);\n\n display: flex;\n width: 100%;\n flex-direction: column;\n gap: var(--accordion-gap);\n}\n\n.Root[data-variant=\"solid\"] {\n --accordion-item-bg: var(--lea-palette-bg-subtle);\n --accordion-item-border: transparent;\n --accordion-hover-border: transparent;\n --accordion-hover-bg: var(--lea-color-neutral-100);\n --accordion-active-bg: var(--lea-color-neutral-200);\n}\n\n/* White cards on a grey section: one step darker on hover and press. The\n website's FAQ: 16px apart, 16px inside the header, 18px semibold titles. */\n.Root[data-variant=\"card\"] {\n --accordion-gap: var(--lea-spacing-2);\n --accordion-item-bg: var(--lea-palette-bg-base);\n --accordion-item-border: transparent;\n --accordion-item-radius: var(--lea-radius-4xl);\n --accordion-header-radius: var(--lea-radius-4xl);\n --accordion-hover-border: transparent;\n --accordion-hover-bg: var(--lea-palette-bg-subtle);\n --accordion-active-bg: var(--lea-color-neutral-100);\n}\n\n/* No surface of their own, so hover and pressed lighten whatever the accordion\n sits on with translucent white; the hint of text colour keeps them visible\n on white. */\n.Root[data-variant=\"divided\"],\n.Root[data-variant=\"ghost\"] {\n --accordion-highlight: color-mix(\n in srgb,\n var(--lea-color-white) calc(var(--lea-opacity-highlight) * 100%),\n transparent\n );\n --accordion-item-border: transparent;\n --accordion-header-radius: var(--lea-radius-sm);\n --accordion-hover-border: transparent;\n --accordion-hover-bg: color-mix(\n in srgb,\n var(--lea-palette-text-base) calc(var(--lea-opacity-hover) * 100%),\n var(--accordion-highlight)\n );\n --accordion-active-bg: color-mix(\n in srgb,\n var(--lea-palette-text-base) calc(var(--lea-opacity-pressed) * 100%),\n var(--accordion-highlight)\n );\n}\n\n.Root[data-variant=\"divided\"] {\n --accordion-gap: 0;\n --accordion-item-radius: 0;\n}\n\n.Item {\n border: 1px solid var(--accordion-item-border);\n border-radius: var(--accordion-item-radius);\n background-color: var(--accordion-item-bg);\n}\n\n.Root[data-variant=\"divided\"] > .Item:not(:last-child) {\n border-block-end-color: var(--lea-palette-border-subtle);\n}\n\n/*\n * The row lives on the header rather than the trigger so a control rendered\n * beside the trigger (a Switch, say) sits inside the same hover outline.\n */\n.Header {\n display: flex;\n align-items: center;\n gap: calc(var(--lea-spacing) * 2);\n padding: calc(var(--lea-spacing) * 1.7) calc(var(--lea-spacing) * 2);\n border: 1px solid transparent;\n border-radius: var(--accordion-header-radius);\n margin: 0;\n transition: border-color var(--lea-duration-emphasis)\n var(--lea-easing-standard),\n background-color var(--lea-duration-emphasis) var(--lea-easing-standard);\n}\n\n@media (hover: hover) {\n .Header:has(.Trigger:hover:not([data-disabled])) {\n border-color: var(--accordion-hover-border);\n background-color: var(--accordion-hover-bg);\n }\n}\n\n.Header:has(.Trigger:active:not([data-disabled])) {\n background-color: var(--accordion-active-bg);\n}\n\n.Trigger {\n display: flex;\n min-width: 0;\n flex: 1;\n align-items: center;\n justify-content: space-between;\n gap: var(--lea-spacing);\n padding: 0;\n border: none;\n margin: 0;\n background-color: transparent;\n color: var(--lea-palette-text-base);\n cursor: pointer;\n font-family: inherit;\n /* label-lg with room to wrap: the label styles have a tight line height. */\n font-size: var(--lea-font-step-0);\n font-weight: var(--lea-font-weight-medium);\n line-height: var(--lea-line-height-normal);\n text-align: left;\n}\n\n.Trigger:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n.Trigger[data-disabled] {\n color: var(--lea-palette-text-muted);\n cursor: not-allowed;\n}\n\n.Label {\n min-width: 0;\n}\n\n.Icon {\n flex-shrink: 0;\n transition: transform var(--lea-duration-emphasis) var(--lea-easing-standard);\n}\n\n.Trigger[data-panel-open] .Icon {\n transform: rotate(180deg);\n}\n\n/* Arrow before the title, so it stays next to the text it belongs to. */\n.Root[data-icon-position=\"start\"] .Trigger {\n justify-content: flex-start;\n}\n\n.Root[data-icon-position=\"start\"] .Icon {\n order: -1;\n}\n\n.Panel {\n height: var(--accordion-panel-height);\n overflow: hidden;\n transition: height var(--lea-duration-emphasis) var(--lea-easing-standard);\n}\n\n.Panel[data-starting-style],\n.Panel[data-ending-style] {\n height: 0;\n}\n\n.Content {\n padding-inline: calc(var(--lea-spacing) * 2);\n padding-block: var(--lea-spacing);\n color: var(--lea-palette-text-base);\n font-size: var(--lea-text-body-md-font-size);\n font-weight: var(--lea-text-body-md-font-weight);\n line-height: var(--lea-text-body-md-line-height);\n letter-spacing: var(--lea-text-body-md-letter-spacing);\n}\n\n/*\n * The card's header and answer are spaced as on leabank.no: 16px round the\n * title, 16px between title and arrow, 32px from the title to the answer and\n * under it. The title is 16px semibold on a phone and 18px from 768px, the\n * website's tablet width.\n */\n/* 16px from the card's edge to the title and to the answer: the card has no\n border, and the header's own 1px transparent border counts. */\n.Root[data-variant=\"card\"] .Item {\n border-width: 0;\n}\n\n.Root[data-variant=\"card\"] .Header {\n padding: calc(var(--lea-spacing-2) - 1px);\n}\n\n.Root[data-variant=\"card\"] .Trigger {\n gap: var(--lea-spacing-2);\n font-size: var(--lea-font-step-0);\n font-weight: var(--lea-font-weight-semibold);\n line-height: var(--lea-line-height-snug);\n}\n\n@media (min-width: 768px) {\n .Root[data-variant=\"card\"] .Trigger {\n font-size: var(--lea-font-step-1);\n }\n}\n\n.Root[data-variant=\"card\"] .Content {\n padding: var(--lea-spacing-2) var(--lea-spacing-2) var(--lea-spacing-4);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Header,\n .Icon,\n .Panel {\n transition-duration: 0.01ms;\n }\n}\n","\"use client\";\n\nimport { Accordion as BaseAccordion } from \"@base-ui/react/accordion\";\nimport { ChevronDown } from \"lucide-react\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport styles from \"./accordion.module.css\";\n\nexport type Variant = \"outline\" | \"solid\" | \"divided\" | \"ghost\" | \"card\";\n\nexport type IconPosition = \"start\" | \"end\";\n\nexport type RootProps = BaseAccordion.Root.Props & {\n /**\n * Surface treatment of each item.\n * - `\"outline\"` — transparent cards with a light `border.subtle` border\n * (default). Hovering outlines the header.\n * - `\"solid\"` — borderless cards filled light grey (`bg.subtle`). Hovering\n * darkens the header instead of outlining it.\n * - `\"divided\"` — a flush list with no cards, items separated by thin lines.\n * Suits long FAQ lists.\n * - `\"ghost\"` — no border or fill; only the hover tint shows. Use it on\n * tinted backgrounds.\n * - `\"card\"` — borderless white cards with larger corners, for a list of\n * questions on a grey (`bg.subtle`) section of the website. Spaced as the\n * website's FAQ: 16px apart and inside, with 18px semibold titles (16px\n * below 768px).\n * @default \"outline\"\n */\n variant?: Variant;\n /**\n * Which side of the title the expand arrow sits on. Use `\"start\"` when the\n * header also holds a control (such as a `Switch`), so the arrow stays next\n * to the title it belongs to.\n * @default \"end\"\n */\n iconPosition?: IconPosition;\n};\n\n/**\n * A set of collapsible panels built on Base UI's accessible `Accordion`\n * primitive, styled with Lea's design tokens.\n *\n * Heading semantics, `aria-expanded`, `aria-controls` and the unique ids\n * linking each trigger to its panel are generated by Base UI, so the\n * relationships can never dangle and consumers never supply ids by hand.\n * Only one panel opens at a time unless you pass `multiple`.\n *\n * ```tsx\n * <Accordion.Root defaultValue={[\"shipping\"]}>\n * <Accordion.Item value=\"shipping\">\n * <Accordion.Header>\n * <Accordion.Trigger>How long does shipping take?</Accordion.Trigger>\n * </Accordion.Header>\n * <Accordion.Panel>Three to five business days.</Accordion.Panel>\n * </Accordion.Item>\n * </Accordion.Root>\n * ```\n *\n * For the common title-and-content case, reach for `SimpleAccordion` instead.\n */\nexport function Root({\n variant = \"outline\",\n iconPosition = \"end\",\n className,\n ...rest\n}: RootProps) {\n return (\n <BaseAccordion.Root\n {...rest}\n data-variant={variant}\n data-icon-position={iconPosition}\n className={mergeClassName(styles.Root, className)}\n />\n );\n}\n\nexport type ItemProps = BaseAccordion.Item.Props;\n\n/**\n * One panel of the accordion, pairing a `Header` with a `Panel`. Give it a\n * `value` to open it initially or to control it programmatically.\n */\nexport function Item({ className, ...rest }: ItemProps) {\n return (\n <BaseAccordion.Item\n {...rest}\n className={mergeClassName(styles.Item, className)}\n />\n );\n}\n\nexport type HeaderProps = BaseAccordion.Header.Props;\n\n/**\n * The heading that labels the panel. Renders an `<h3>` and must wrap a\n * `Trigger` so the accordion stays navigable by heading.\n *\n * Anything rendered after the `Trigger` sits on the same row, which is where a\n * per-item control belongs. It must go beside the trigger rather than inside\n * it, because the trigger is a `<button>` and nesting controls is invalid.\n *\n * ```tsx\n * <Accordion.Header>\n * <Accordion.Trigger>Analyse</Accordion.Trigger>\n * <Switch aria-label=\"Analyse-informasjonskapsler\" />\n * </Accordion.Header>\n * ```\n */\nexport function Header({ className, ...rest }: HeaderProps) {\n return (\n <BaseAccordion.Header\n {...rest}\n className={mergeClassName(styles.Header, className)}\n />\n );\n}\n\nexport type TriggerProps = BaseAccordion.Trigger.Props;\n\n/**\n * The button that opens and closes its panel. Pass the question or label as\n * children; the chevron is rendered internally and hidden from assistive\n * technology.\n */\nexport function Trigger({ children, className, ...rest }: TriggerProps) {\n return (\n <BaseAccordion.Trigger\n {...rest}\n className={mergeClassName(styles.Trigger, className)}\n >\n <span className={styles.Label}>{children}</span>\n <ChevronDown className={styles.Icon} size={20} aria-hidden />\n </BaseAccordion.Trigger>\n );\n}\n\nexport type PanelProps = BaseAccordion.Panel.Props;\n\n/**\n * The collapsible content of an item. Padding sits on an inner element so the\n * open/close height animation stays smooth.\n */\nexport function Panel({ children, className, ...rest }: PanelProps) {\n return (\n <BaseAccordion.Panel\n {...rest}\n className={mergeClassName(styles.Panel, className)}\n >\n <div className={styles.Content}>{children}</div>\n </BaseAccordion.Panel>\n );\n}\n","\"use client\";\n\nimport type { ReactNode } from \"react\";\n\nimport * as Accordion from \"./accordion\";\n\nexport type SimpleAccordionItem = {\n /** The heading shown on the row. */\n title: ReactNode;\n /** The collapsible body revealed when the row is opened. */\n content: ReactNode;\n /**\n * A control rendered on the row beside the title, typically a `Switch`.\n * It sits outside the trigger, so operating it doesn't open the panel.\n */\n action?: ReactNode;\n /**\n * Identifies the item for `defaultValue` / `value`. Defaults to the item's\n * position, so only set it when you open or control items programmatically.\n * Only meaningful inside `items`.\n */\n value?: string;\n /** Whether this item can be opened. Only meaningful inside `items`. */\n disabled?: boolean;\n};\n\n/**\n * The single-item form. `value` and `disabled` are left to `Accordion.Root`\n * (they'd otherwise collide with the root's own props of the same name); the\n * lone item always has the value `\"0\"`, so `defaultValue={[\"0\"]}` opens it.\n */\ntype SingleItemProps = Omit<SimpleAccordionItem, \"value\" | \"disabled\"> & {\n items?: never;\n};\n\ntype MultipleItemsProps = {\n items: SimpleAccordionItem[];\n title?: never;\n content?: never;\n action?: never;\n};\n\n// The root props inherit every global HTML attribute, two of which (`title`\n// and the RDFa `content`) share a name with ours and would narrow our\n// `ReactNode` values to `string`. Drop them so our own types win.\nexport type SimpleAccordionProps = Omit<\n Accordion.RootProps,\n \"children\" | \"title\" | \"content\"\n> &\n (SingleItemProps | MultipleItemsProps);\n\n/**\n * The props-driven form of `Accordion` for the common case: a title and some\n * content, with an optional control on the row.\n *\n * Pass `title` and `content` for a single collapsible panel, or `items` for\n * several. Everything `Accordion.Root` accepts — `variant`, `iconPosition`,\n * `multiple`, `defaultValue` — is forwarded. Compose `Accordion.Root` by hand\n * when you need markup the `title` / `content` pair can't express.\n *\n * ```tsx\n * <SimpleAccordion\n * iconPosition=\"start\"\n * title=\"Nødvendige\"\n * content={<Text>Alltid påskrudd.</Text>}\n * action={<Switch aria-label=\"Nødvendige\" checked disabled />}\n * />\n *\n * <SimpleAccordion\n * multiple\n * items={[\n * { title: \"Levering\", content: \"Tre til fem virkedager.\" },\n * { title: \"Retur\", content: \"30 dagers angrerett.\" },\n * ]}\n * />\n * ```\n */\nexport function SimpleAccordion({\n items,\n title,\n content,\n action,\n ...rest\n}: SimpleAccordionProps) {\n const entries: SimpleAccordionItem[] = items ?? [\n { title, content, action } as SimpleAccordionItem,\n ];\n\n return (\n <Accordion.Root {...rest}>\n {entries.map((item, index) => {\n const itemValue = item.value ?? String(index);\n return (\n <Accordion.Item\n key={itemValue}\n value={itemValue}\n disabled={item.disabled}\n >\n <Accordion.Header>\n <Accordion.Trigger>{item.title}</Accordion.Trigger>\n {item.action}\n </Accordion.Header>\n <Accordion.Panel>{item.content}</Accordion.Panel>\n </Accordion.Item>\n );\n })}\n </Accordion.Root>\n );\n}\n","/*\n * The website's white box (FreeBlock, FlexibleContentBlock and SimpleTable\n * with \"Rounded\" on, the Hero card, FeaturePaymentBlock): `huge` corners and\n * 64px inside, `5xl` and 32px below 768px. Flat: no border, no shadow, as an\n * in-flow surface. Its content stacks 16px apart, as the Figma slot.\n */\n.Panel {\n --panel-padding: var(--lea-spacing-8);\n --panel-radius: var(--lea-radius-huge);\n\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n gap: var(--lea-spacing-2);\n padding: var(--panel-padding);\n border-radius: var(--panel-radius);\n background-color: var(--lea-palette-bg-base);\n color: var(--lea-palette-text-base);\n}\n\n.Panel[data-size=\"sm\"] {\n --panel-padding: var(--lea-spacing-4);\n --panel-radius: var(--lea-radius-5xl);\n}\n\n@media (max-width: 767.95px) {\n .Panel:not([data-size]) {\n --panel-padding: var(--lea-spacing-4);\n --panel-radius: var(--lea-radius-5xl);\n }\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport type { Ref } from \"react\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport styles from \"./panel.module.css\";\n\nexport type PanelSize = \"sm\" | \"lg\";\n\nexport type PanelProps = Omit<useRender.ComponentProps<\"div\">, \"ref\"> & {\n /** The rendered element: the `<div>`, or the element passed to `render`. */\n ref?: Ref<HTMLElement>;\n /**\n * `\"lg\"`: 64px corners and 64px inside. `\"sm\"`: 32px corners and 32px\n * inside, for a narrow column such as the content beside a `SideNav`. Left\n * out, the panel is `lg` and turns `sm` below 768px, as on the website.\n */\n size?: PanelSize;\n};\n\n/**\n * The website's white box: a white surface with large round corners and\n * generous room inside, around text, a table or a form on a grey, beige or\n * dark section. What is inside stacks with 16px between.\n *\n * ```tsx\n * <Panel>\n * <Text render={<h2 />} textStyle=\"heading-xl\">Priseksempler</Text>\n * <Table>…</Table>\n * </Panel>\n * ```\n *\n * It is always white, so it disappears on a white section: put it on a\n * coloured one. It takes the width it is given. Renders a `div`; pass\n * `render` for another element, e.g. `render={<section />}`.\n */\nexport function Panel({ render, ref, className, size, ...rest }: PanelProps) {\n return useRender({\n render,\n ref,\n defaultTagName: \"div\",\n props: {\n ...rest,\n \"data-size\": size,\n className: mergeClassName(styles.Panel, className)({}),\n },\n });\n}\n","import {\n createContext,\n useContext,\n useMemo,\n type ReactNode,\n} from \"react\";\n\nimport { COUNTRIES, type Country, type Currency } from \"./countries\";\n\nexport interface CountryContextValue {\n country: Country;\n locale: string;\n currency: Currency;\n}\n\nconst CountryContext = createContext<CountryContextValue | null>(null);\n\nexport interface CountryProviderProps {\n country: Country;\n children: ReactNode;\n}\n\n/**\n * Supplies the active Lea Bank country to locale-aware formatting hooks.\n */\nexport function CountryProvider({ country, children }: CountryProviderProps) {\n const value = useMemo<CountryContextValue>(() => {\n const config = COUNTRIES[country];\n\n return {\n country: config.country,\n locale: config.locale,\n currency: config.currency,\n };\n }, [country]);\n\n return (\n <CountryContext.Provider value={value}>{children}</CountryContext.Provider>\n );\n}\n\n/**\n * Reads the active country, locale, and default currency from the nearest\n * `CountryProvider`.\n */\nexport function useCountry(): CountryContextValue {\n const context = useContext(CountryContext);\n\n if (!context) {\n throw new Error(\n \"Country hooks must be used within a <CountryProvider>.\",\n );\n }\n\n return context;\n}\n","import { useCallback, useMemo } from \"react\";\n\nimport { type Currency } from \"./country/countries\";\nimport { useCountry } from \"./country/country-context\";\n\nexport type FormatCurrencyOptions = Omit<\n Intl.NumberFormatOptions,\n \"style\" | \"currency\"\n> & {\n currency?: Currency;\n};\n\n/**\n * Returns a locale-aware currency formatter using the active country's locale\n * and default currency. Pass `currency` in options to override per call.\n */\nexport function useFormatCurrency() {\n const { locale, currency: defaultCurrency } = useCountry();\n\n const defaultFormatter = useMemo(\n () =>\n new Intl.NumberFormat(locale, {\n style: \"currency\",\n currency: defaultCurrency,\n }),\n [locale, defaultCurrency],\n );\n\n return useCallback(\n (value: number, options?: FormatCurrencyOptions) => {\n const { currency = defaultCurrency, ...formatOptions } = options ?? {};\n\n if (\n currency === defaultCurrency &&\n Object.keys(formatOptions).length === 0\n ) {\n return defaultFormatter.format(value);\n }\n\n return new Intl.NumberFormat(locale, {\n style: \"currency\",\n currency,\n ...formatOptions,\n }).format(value);\n },\n [defaultCurrency, defaultFormatter, locale],\n );\n}\n","import { useCallback, useMemo } from \"react\";\n\nimport { useCountry } from \"./country/country-context\";\n\nconst DEFAULT_DATE_OPTIONS: Intl.DateTimeFormatOptions = {\n year: \"numeric\",\n month: \"short\",\n day: \"numeric\",\n};\n\nfunction toDate(value: Date | number | string): Date {\n return value instanceof Date ? value : new Date(value);\n}\n\n/**\n * Returns a locale-aware date formatter using the active country's locale.\n */\nexport function useFormatDate() {\n const { locale } = useCountry();\n\n const defaultFormatter = useMemo(\n () => new Intl.DateTimeFormat(locale, DEFAULT_DATE_OPTIONS),\n [locale],\n );\n\n return useCallback(\n (value: Date | number | string, options?: Intl.DateTimeFormatOptions) => {\n const date = toDate(value);\n\n if (!options) {\n return defaultFormatter.format(date);\n }\n\n return new Intl.DateTimeFormat(locale, options).format(date);\n },\n [defaultFormatter, locale],\n );\n}\n","import { useCallback, useMemo } from \"react\";\n\nimport { useCountry } from \"./country/country-context\";\n\n/**\n * Returns a locale-aware plain-number formatter using the active country's\n * locale.\n */\nexport function useFormatNumber() {\n const { locale } = useCountry();\n\n const defaultFormatter = useMemo(\n () => new Intl.NumberFormat(locale),\n [locale],\n );\n\n return useCallback(\n (value: number, options?: Intl.NumberFormatOptions) => {\n if (!options) {\n return defaultFormatter.format(value);\n }\n\n return new Intl.NumberFormat(locale, options).format(value);\n },\n [defaultFormatter, locale],\n );\n}\n\n/**\n * Returns a locale-aware percentage formatter using the active country's\n * locale.\n */\nexport function useFormatPercentage() {\n const { locale } = useCountry();\n\n const defaultFormatter = useMemo(\n () =>\n new Intl.NumberFormat(locale, {\n style: \"percent\",\n minimumFractionDigits: 2,\n maximumFractionDigits: 2,\n }),\n [locale],\n );\n\n return useCallback(\n (value: number, options?: Intl.NumberFormatOptions) => {\n if (!options) {\n return defaultFormatter.format(value);\n }\n\n return new Intl.NumberFormat(locale, {\n style: \"percent\",\n ...options,\n }).format(value);\n },\n [defaultFormatter, locale],\n );\n}\n","import { useCallback } from \"react\";\n\nimport { useCountry } from \"../country/country-context\";\nimport {\n isValidBankAccount,\n isValidNationalId,\n isValidPhoneNumber,\n validateBankAccount,\n validateNationalId,\n validatePhoneNumber,\n type ValidationResult,\n} from \"../../validation\";\n\n/**\n * Returns a national-id validator bound to the active country (from\n * `CountryProvider`). The returned function reports a boolean; use\n * {@link useValidateNationalId}'s `.validate` for the normalized value.\n */\nexport function useValidateNationalId() {\n const { country } = useCountry();\n\n const isValid = useCallback(\n (value: string): boolean => isValidNationalId(country, value),\n [country],\n );\n\n const validate = useCallback(\n (value: string): ValidationResult => validateNationalId(country, value),\n [country],\n );\n\n return Object.assign(isValid, { validate });\n}\n\n/**\n * Returns a phone-number validator bound to the active country.\n */\nexport function useValidatePhoneNumber() {\n const { country } = useCountry();\n\n const isValid = useCallback(\n (value: string): boolean => isValidPhoneNumber(country, value),\n [country],\n );\n\n const validate = useCallback(\n (value: string): ValidationResult => validatePhoneNumber(country, value),\n [country],\n );\n\n return Object.assign(isValid, { validate });\n}\n\n/**\n * Returns a bank-account validator bound to the active country.\n */\nexport function useValidateBankAccount() {\n const { country } = useCountry();\n\n const isValid = useCallback(\n (value: string): boolean => isValidBankAccount(country, value),\n [country],\n );\n\n const validate = useCallback(\n (value: string): ValidationResult => validateBankAccount(country, value),\n [country],\n );\n\n return Object.assign(isValid, { validate });\n}\n","\"use client\";\n\nimport { type ReactNode } from \"react\";\nimport { LanguageContext } from \"../i18n/language-context\";\nimport { DEFAULT_LANGUAGE, type Language } from \"../i18n/languages\";\n\nexport type LeaProviderProps = {\n /** Language for the library's built-in translations and date locales. @default \"no\" */\n language?: Language;\n children: ReactNode;\n};\n\n/**\n * Sets the language of the library's own texts (`createTranslation`) and of\n * the components that format by locale. That is all it does: no component\n * needs it to render, and without it the language is Norwegian.\n */\nexport function LeaProvider({\n language = DEFAULT_LANGUAGE,\n children,\n}: LeaProviderProps) {\n return (\n <LanguageContext.Provider value={language}>\n {children}\n </LanguageContext.Provider>\n );\n}\n","/**\n * Viewport widths in pixels where layouts may change. Mobile-first: each value\n * is the `min-width` at which the breakpoint starts. The values match MUI's\n * defaults, so the MUI wrappers and the CSS Modules switch at the same widths.\n *\n * CSS custom properties can't be used in `@media`, so CSS Modules write the\n * pixel value literally; tests/tokens.test.ts checks it against this scale.\n */\nexport const breakpoints = {\n xs: 0,\n sm: 600,\n md: 900,\n lg: 1200,\n xl: 1536,\n} as const;\n\nexport type Breakpoint = keyof typeof breakpoints;\n\n/**\n * The website's own widths, for the website components only, so a layout on\n * leabank.no switches where it does today. Named as in the website's theme.\n * There each is a `max-width` (`mobile` is up to 640px); here, as everywhere in\n * the library, a CSS Module writes them mobile-first: `@media (min-width:\n * 640px)` starts above `mobile`, and `max-width: 639.95px` ends below it. That\n * moves each switch by one pixel, at exactly the width itself.\n *\n * They are not part of `Breakpoint`, so `Container` sizes and the responsive\n * props of `Stack` and `Grid` keep their five steps.\n */\nexport const websiteBreakpoints = {\n mobile: 640,\n tablet: 768,\n desktop: 1024,\n /** The website's `mobileHeader`: below it the header is the mobile one. */\n header: 1312,\n wide: 1440,\n} as const;\n\nexport type WebsiteBreakpoint = keyof typeof websiteBreakpoints;\n","export type FormatDecimals = { value: number; decimalPlaces?: number };\n\n/**\n\n * Formats a number to a fixed number of decimal places (default is 2)\n * by using exponential notation\n *\n * ⚠️ Note: We avoid using `toFixed` for rounding because it can introduce rounding errors.\n * \n * @param value - The number to format.\n * @param decimalPlaces - Number of decimal places to show (default: 2).\n * @returns The number with fixed decimal places.\n */\nexport const fixDecimals = ({ value, decimalPlaces = 2 }: FormatDecimals) => {\n return Number(\n Math.round(parseFloat(value + \"e\" + decimalPlaces)) + \"e-\" + decimalPlaces\n );\n};\n","import { fixDecimals, FormatDecimals } from \"./format-decimal\";\n\n/**\n * Formats a number to a fixed number of decimal places (default is 2).\n *\n * Ensures that string representation of number always has the set number of decimals (even eg. .00)\n *\n * @param value - The number or string to format.\n * @param decimalPlaces - Number of decimal places to show (default: 2).\n * @returns A string representation of the number with fixed decimal places.\n */\nexport const fixDecimalsToString = ({\n value,\n decimalPlaces = 2,\n}: FormatDecimals) => {\n return fixDecimals({ value, decimalPlaces }).toFixed(decimalPlaces);\n};\nexport const formatDecimalString = ({\n value,\n decimalPlaces = 2,\n}: FormatDecimals): string => {\n return fixDecimals({ value, decimalPlaces }) % 1 === 0\n ? fixDecimalsToString({ value, decimalPlaces: 0 }).replace(\".\", \",\")\n : fixDecimalsToString({ value, decimalPlaces }).replace(\".\", \",\");\n};\n","import { formatDecimalString } from \"./format-decimal-string\";\n\nexport const formatPercentage = (value: number, decimalPlaces: number = 2) => {\n return formatDecimalString({ value, decimalPlaces }) + \"%\";\n};\n","export function parseExpirationDate(expirationDate: string): {\n month: string;\n year: string;\n} {\n const match = expirationDate.match(/^([0-9]{2})([0-9]{2})$/);\n if (!match) return { month: \"\", year: \"\" };\n return { month: match[1], year: match[2] };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"component-library.es.js","names":[],"sources":["../src/theme/tokens/colors.ts","../src/theme/tokens/palette.ts","../src/components/animations/animations.module.css","../src/components/animations/shared.tsx","../src/i18n/languages.ts","../src/i18n/language-context.tsx","../src/i18n/create-translation.ts","../src/components/animations/use-animation-labels.ts","../src/components/animations/check-animation/check-animation.tsx","../src/components/animations/spinner.tsx","../src/components/animations/pulse-animation/pulse-animation.tsx","../src/components/animations/loader/loader.tsx","../src/components/animations/status-animation/status-animation.tsx","../src/components/animations/status-icon/status-icon.tsx","../src/utils/merge-class-name.ts","../src/components/buttons/action-button/action-button.module.css","../src/components/buttons/button.module.css","../src/components/buttons/button-base.tsx","../src/components/buttons/action-button/action-button.tsx","../src/components/buttons/button/button.tsx","../src/components/feedback/use-close-label.ts","../src/components/buttons/icon-button/icon-button.tsx","../src/components/buttons/close-button/close-button.tsx","../src/utils/download-file.ts","../src/components/buttons/download-trigger/use-download-labels.ts","../src/components/buttons/download-trigger/download-trigger.tsx","../src/components/buttons/skip-link/skip-link.module.css","../src/components/buttons/skip-link/skip-link.tsx","../src/theme/tokens/font-sizes.ts","../src/components/typography/text/text.module.css","../src/components/typography/text/text.tsx","../src/components/data-display/data-field/data-field.module.css","../src/components/data-display/data-field/data-field.tsx","../src/components/data-display/card/card.module.css","../src/components/data-display/card/card.tsx","../src/components/data-display/chip/chip.module.css","../src/components/data-display/chip/use-chip-labels.ts","../src/components/data-display/chip/chip.tsx","../src/components/data-display/tooltip/tooltip.module.css","../src/components/data-display/tooltip/tooltip.tsx","../src/components/data-display/avatar/avatar.module.css","../src/components/data-display/avatar/avatar.tsx","../src/components/data-display/star-rating/star-rating.module.css","../src/components/data-display/star-rating/use-star-rating-labels.ts","../src/components/data-display/star-rating/star-rating.tsx","../src/components/data-display/table/table.module.css","../src/components/data-display/table/table.tsx","../src/components/data-display/rich-text/rich-text.module.css","../src/components/data-display/rich-text/rich-text.tsx","../src/components/data-display/featured-image/featured-image.module.css","../src/components/data-display/featured-image/featured-image.tsx","../src/components/data-display/video-player/video-player.module.css","../src/components/data-display/video-player/use-video-player-labels.ts","../src/components/data-display/video-player/video-player.tsx","../src/components/data-display/link-card/arrow-circle.module.css","../src/components/data-display/link-card/arrow-circle.tsx","../src/components/data-display/link-card/link-card.module.css","../src/components/data-display/link-card/link-card.tsx","../src/components/data-display/media-card/media-card.module.css","../src/components/data-display/website-card.ts","../src/components/data-display/media-card/media-card.tsx","../src/components/data-display/service-card/service-card.module.css","../src/components/data-display/service-card/service-card.tsx","../src/components/data-display/testimonial-card/testimonial-card.module.css","../src/components/data-display/testimonial-card/testimonial-card.tsx","../src/components/data-display/article-card/article-card.module.css","../src/components/data-display/article-card/article-card.tsx","../src/components/data-display/partner-row/partner-row.module.css","../src/components/data-display/partner-row/partner-row.tsx","../src/components/data-display/brand-divider/brand-divider.module.css","../src/components/data-display/brand-divider/brand-divider.tsx","../src/components/feedback/alert.module.css","../src/components/feedback/alert-parts.tsx","../src/components/feedback/alert/alert.tsx","../src/components/feedback/dialog/dialog.module.css","../src/components/feedback/dialog/dialog.tsx","../src/components/feedback/drawer/drawer.module.css","../src/components/feedback/drawer/drawer.tsx","../src/components/layout/container/container.module.css","../src/components/layout/container/container.tsx","../src/components/feedback/global-alert/global-alert.tsx","../src/components/feedback/dialog/simple-dialog.tsx","../src/components/feedback/drawer/simple-drawer.tsx","../src/components/feedback/toast/toast.module.css","../src/components/feedback/toast/toast.tsx","../src/components/feedback/skeleton/skeleton.module.css","../src/components/feedback/skeleton/skeleton.tsx","../src/components/form/amount-slider/amount-slider.module.css","../src/components/form/slider-parts.module.css","../src/components/form/slider-parts.tsx","../src/components/form/amount-slider/amount-slider.tsx","../src/components/form/combo-field.module.css","../src/components/form/field-parts.module.css","../src/components/form/use-tooltip-trigger-labels.ts","../src/components/form/field-parts.tsx","../src/components/form/input.module.css","../src/components/form/option-list.module.css","../src/components/form/use-autocomplete-labels.ts","../src/components/form/use-blur-outside.ts","../src/components/form/combo-field.tsx","../src/components/form/autocomplete/autocomplete.tsx","../src/components/form/checkbox.module.css","../src/components/form/checkbox-box.tsx","../src/components/form/choice-row.module.css","../src/components/form/checkbox/checkbox.tsx","../src/validation/types.ts","../src/validation/national-id.ts","../src/validation/mobile-patterns.ts","../src/validation/phone.ts","../src/validation/modulo-97.ts","../src/validation/bank-account.ts","../src/validation/reference-number.ts","../src/validation/email.ts","../src/components/typography/link/link.module.css","../src/components/typography/link/link.tsx","../src/components/typography/lists/lists.module.css","../src/components/typography/lists/lists.tsx","../src/components/form/email-input/email-input.module.css","../src/components/form/email-input/email-input.tsx","../src/components/form/input/input.tsx","../src/components/form/select.module.css","../src/components/form/select-field.tsx","../src/components/form/multi-select/multi-select.tsx","../src/hooks/country/countries.ts","../src/components/form/number-input/number-input-unit.ts","../src/components/form/number-input/number-input.module.css","../src/components/form/number-input/number-input.tsx","../src/components/form/phone-number-input/countries.ts","../src/components/form/phone-number-input/phone-number-input.module.css","../src/components/form/phone-number-input/phone-number-input.tsx","../src/components/form/radio.module.css","../src/components/form/radio-circle.tsx","../src/components/form/radio-group-root.module.css","../src/components/form/radio-group-root.tsx","../src/components/form/radio/radio.tsx","../src/components/form/radio-card/radio-card.module.css","../src/components/form/radio-card/radio-card.tsx","../src/components/form/select/select.tsx","../src/components/form/slider/slider.module.css","../src/components/form/slider/slider.tsx","../src/components/form/switch/switch.module.css","../src/components/form/switch/switch.tsx","../src/components/layout/grid/grid.module.css","../src/components/layout/responsive.ts","../src/components/layout/grid/grid.tsx","../src/components/layout/stack/stack.module.css","../src/components/layout/stack/stack.tsx","../src/components/navigation/form-stepper/form-stepper.module.css","../src/components/navigation/progress-circle/progress-circle.module.css","../src/components/navigation/progress-circle/progress-circle.tsx","../src/components/navigation/use-stepper-labels.ts","../src/components/navigation/form-stepper/form-stepper.tsx","../src/components/navigation/linear-form-stepper/linear-form-stepper.module.css","../src/components/navigation/linear-form-stepper/linear-form-stepper.tsx","../src/components/navigation/linear-form-stepper/step-progress.tsx","../src/components/navigation/tabs/tabs.module.css","../src/components/navigation/tabs/tabs.tsx","../src/components/navigation/breadcrumbs/breadcrumbs.module.css","../src/components/navigation/breadcrumbs/use-breadcrumbs-labels.ts","../src/components/navigation/breadcrumbs/breadcrumbs.tsx","../src/components/navigation/pagination/pagination.module.css","../src/components/navigation/use-pagination-labels.ts","../src/components/navigation/pagination/pagination.tsx","../src/components/navigation/pagination-dots/pagination-dots.module.css","../src/components/navigation/pagination-dots/pagination-dots.tsx","../src/components/navigation/app-header/app-header.module.css","../src/components/navigation/app-header/app-header.tsx","../src/components/surfaces/accordion/accordion.module.css","../src/components/surfaces/accordion/accordion.tsx","../src/components/surfaces/accordion/simple-accordion.tsx","../src/components/surfaces/panel/panel.module.css","../src/components/surfaces/panel/panel.tsx","../src/hooks/country/country-context.tsx","../src/hooks/use-format-currency.ts","../src/hooks/use-format-date.ts","../src/hooks/use-format-number.ts","../src/hooks/validation/use-validation.ts","../src/provider/lea-provider.tsx","../src/theme/tokens/breakpoints.ts","../src/utils/format-decimal.ts","../src/utils/format-decimal-string.ts","../src/utils/format-percentage.ts","../src/utils/parse-expiration-date.ts"],"sourcesContent":["export const colors = {\n purple: {\n 50: \"#FAF7FB\",\n 100: \"#E2D8E4\",\n 200: \"#C5B1CA\",\n 300: \"#C29AC2\",\n 400: \"#9E6DA8\",\n 500: \"#855888\",\n 600: \"#70427B\",\n 700: \"#5A3463\",\n 800: \"#422349\",\n 900: \"#2E1834\",\n 950: \"#1A0E1E\",\n },\n teal: {\n 50: \"#F0FCFA\",\n 100: \"#DAF6F1\",\n 200: \"#CEF3ED\",\n 300: \"#08C2A7\",\n 400: \"#46D1BD\",\n 500: \"#06A892\",\n 600: \"#058F7D\",\n 700: \"#047568\",\n 800: \"#035C53\",\n 900: \"#02433E\",\n 950: \"#012A28\",\n },\n blue: {\n 50: \"#EFF6FF\",\n 100: \"#CBE5FD\",\n 200: \"#93CBFC\",\n 300: \"#4DA8F7\",\n 400: \"#057EF2\",\n 500: \"#0468C9\",\n 600: \"#0354A3\",\n 700: \"#02417D\",\n 800: \"#012E57\",\n 900: \"#011B31\",\n 950: \"#000E19\",\n },\n orange: {\n 50: \"#FFF5F2\",\n 100: \"#FFDFD7\",\n 200: \"#FFC4B5\",\n 300: \"#FF9E87\",\n 400: \"#FE653E\",\n 500: \"#E54E28\",\n 600: \"#C43E1C\",\n 700: \"#A33218\",\n 800: \"#822814\",\n 900: \"#611E0F\",\n 950: \"#401408\",\n },\n amber: {\n 50: \"#FFF9EB\",\n 100: \"#FFEFC7\",\n 200: \"#FFE299\",\n 300: \"#FFCF5C\",\n 400: \"#F7A800\",\n 500: \"#D98F00\",\n 600: \"#B37400\",\n 700: \"#8C5A00\",\n 800: \"#6B4500\",\n 900: \"#4A3000\",\n 950: \"#2E1D00\",\n },\n red: {\n 50: \"#FEF2F2\",\n 100: \"#FCDCDC\",\n 200: \"#F9BBBB\",\n 300: \"#F28888\",\n 400: \"#E85555\",\n 500: \"#D32F2F\",\n 600: \"#AD2121\",\n 700: \"#8A1919\",\n 800: \"#6F1313\",\n 900: \"#4D0E0E\",\n 950: \"#2E0808\",\n },\n beige: {\n 50: \"#FAF7F5\",\n 100: \"#F0E9E5\",\n 200: \"#E0D4CD\",\n 300: \"#C9B8AD\",\n 400: \"#B09A8C\",\n 500: \"#977C6B\",\n 600: \"#7E6556\",\n 700: \"#654F42\",\n 800: \"#4C3A32\",\n 900: \"#332722\",\n 950: \"#1A1411\",\n },\n neutral: {\n 50: \"#F7F7F6\",\n 100: \"#EBEBEB\",\n 200: \"#DBDBDB\",\n 300: \"#C4C4C4\",\n 400: \"#A2A2A2\",\n 500: \"#757575\",\n 600: \"#5E5E5E\",\n 700: \"#454545\",\n 800: \"#2E2E2E\",\n 900: \"#1A1A1A\",\n 950: \"#0F1010\",\n },\n white: \"#FFFFFF\",\n black: \"#0F1010\",\n} as const;\n","import { Palette } from \"../types\";\nimport { colors } from \"./colors\";\n\nconst { purple, teal, blue, amber, red, neutral, white, beige } = colors;\n\n/** A scale colour at an opacity, written `rgba(15, 16, 16, 0.5)`. */\nconst alpha = (hex: string, opacity: number) => {\n const [r, g, b] = [1, 3, 5].map((i) => parseInt(hex.slice(i, i + 2), 16));\n\n return `rgba(${r}, ${g}, ${b}, ${opacity})`;\n};\n\n/**\n * The website's hover colours, kept at its exact values rather than the\n * nearest step of the purple scale (purple 100 `#E2D8E4`, 950 `#1A0E1E` and\n * 300 `#C29AC2` are close, not the same). The Figma variables `bg/hover`,\n * `bg/contrast-hover` and `text/contrast-hover` hold the same raw values.\n */\nconst website = {\n hover: \"#E3D8DA\",\n contrastHover: \"#241B26\",\n textContrastHover: \"#CD86D6\",\n} as const;\n\nexport const palette: Palette = {\n bg: {\n base: white,\n subtle: neutral[50],\n muted: beige[100],\n contrast: neutral[950],\n accent: purple[100],\n overlay: alpha(neutral[950], 0.5),\n hover: website.hover,\n \"contrast-hover\": website.contrastHover,\n \"contrast-accent\": purple[800],\n },\n text: {\n base: neutral[950],\n subtle: neutral[500],\n muted: neutral[400],\n accent: purple[600],\n contrast: white,\n \"contrast-hover\": website.textContrastHover,\n },\n border: {\n base: neutral[200],\n subtle: neutral[100],\n strong: neutral[950],\n accent: purple[600],\n contrast: white,\n },\n primary: {\n base: purple[600],\n hover: purple[700],\n subtle: purple[100],\n contrast: white,\n },\n secondary: {\n base: neutral[950],\n hover: neutral[800],\n subtle: neutral[100],\n contrast: white,\n },\n tertiary: {\n base: teal[300],\n hover: teal[400],\n subtle: teal[100],\n contrast: white,\n },\n status: {\n error: {\n base: red[500],\n subtle: red[100],\n },\n warning: {\n base: amber[400],\n subtle: amber[100],\n },\n info: {\n base: blue[400],\n subtle: blue[100],\n },\n success: {\n base: teal[400],\n subtle: teal[100],\n },\n },\n} as const;\n",".StatusIcon,\n.Check,\n.Pulse {\n --animation-base: var(--lea-palette-primary-base);\n --animation-contrast: var(--lea-palette-primary-contrast);\n --animation-shadow: rgba(255, 255, 255, 0.1) 0 1px 1px 0 inset,\n rgba(50, 50, 93, 0.25) 0 30px 60px -20px,\n rgba(0, 0, 0, 0.25) 0 18px 36px -18px;\n}\n\n.StatusIcon[data-color=\"tertiary\"],\n.Check[data-color=\"tertiary\"],\n.Pulse[data-color=\"tertiary\"] {\n --animation-base: var(--lea-palette-tertiary-base);\n /* White on teal is 2.3:1, too faint for the check and spinner; `text.base` is 8.4:1. */\n --animation-contrast: var(--lea-palette-text-base);\n}\n\n.Badge {\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: var(--lea-radius-full);\n background-color: var(--animation-base);\n color: var(--animation-contrast);\n}\n\n.Spinner {\n display: inline-block;\n animation: animation-spin 1.4s linear infinite;\n}\n\n.SpinnerSvg {\n display: block;\n}\n\n@keyframes animation-spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n/* StatusIcon */\n\n.StatusIcon {\n position: relative;\n display: inline-flex;\n overflow: visible;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n color: var(--animation-base);\n}\n\n.State {\n position: absolute;\n display: flex;\n align-items: center;\n justify-content: center;\n inset: 0;\n}\n\n.Ring {\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n border-style: solid;\n border-color: var(--lea-palette-border-base);\n border-radius: var(--lea-radius-full);\n}\n\n/* CheckAnimation */\n\n.Check {\n position: relative;\n display: flex;\n overflow: visible;\n align-items: center;\n justify-content: center;\n}\n\n.Halo {\n position: absolute;\n border-radius: var(--lea-radius-full);\n animation: check-halo 2.6s var(--lea-easing-standard) infinite;\n background-color: var(--animation-base);\n opacity: 0.25;\n}\n\n.CheckBadge {\n animation: check-breathe 2.6s var(--lea-easing-standard) infinite;\n box-shadow: var(--animation-shadow);\n}\n\n@keyframes check-halo {\n 0%,\n 100% {\n opacity: 0.28;\n transform: scale(1);\n }\n\n 50% {\n opacity: 0;\n transform: scale(1.45);\n }\n}\n\n@keyframes check-breathe {\n 0%,\n 100% {\n transform: scale(1);\n }\n\n 50% {\n transform: scale(1.04);\n }\n}\n\n/* PulseAnimation */\n\n.Pulse {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.PulseRing,\n.PulseCentre {\n position: absolute;\n border-radius: var(--lea-radius-full);\n background-color: var(--animation-base);\n}\n\n.PulseRing {\n animation: pulse-ring 2s var(--lea-easing-standard) infinite;\n opacity: 0.5;\n}\n\n.PulseCentre {\n box-shadow: var(--animation-shadow);\n}\n\n/* Positioned, so it paints above the centre circle whether or not it turns. */\n.PulseSpinner {\n position: relative;\n color: var(--animation-contrast);\n}\n\n@keyframes pulse-ring {\n 0% {\n opacity: 0.6;\n transform: scale(0.4);\n }\n\n 100% {\n opacity: 0;\n transform: scale(1.6);\n }\n}\n\n/* StatusAnimation */\n\n.Status {\n position: relative;\n}\n\n.Layer {\n position: absolute;\n display: flex;\n align-items: center;\n justify-content: center;\n inset: 0;\n}\n\n.SrOnly {\n position: absolute;\n overflow: hidden;\n width: 1px;\n height: 1px;\n padding: 0;\n border: 0;\n margin: -1px;\n clip-path: inset(50%);\n white-space: nowrap;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Halo {\n animation: none;\n opacity: 0;\n }\n\n .CheckBadge,\n .PulseRing {\n animation: none;\n }\n}\n","import { Check } from \"lucide-react\";\nimport { motion } from \"motion/react\";\nimport { type ReactNode } from \"react\";\n\nimport type { ColorRoles } from \"../color-roles\";\nimport styles from \"./animations.module.css\";\n\nexport type AnimationSize = \"sm\" | \"lg\";\n\nexport type AnimationColor = ColorRoles<\"primary\" | \"tertiary\">;\n\nexport type ConfettiParticle = {\n id: number;\n x: number;\n y: number;\n dotSize: number;\n round: boolean;\n colour?: string;\n spin?: number;\n};\n\ntype BuildParticlesOptions = {\n count: number;\n burst: number;\n distanceAt: (index: number) => number;\n dotSizeAt: (index: number) => number;\n colourAt?: (index: number) => string | undefined;\n withSpin?: boolean;\n};\n\nexport const buildParticles = ({\n count,\n burst,\n distanceAt,\n dotSizeAt,\n colourAt,\n withSpin = false,\n}: BuildParticlesOptions): ConfettiParticle[] =>\n Array.from({ length: count }, (_, i) => {\n const angle = (i / count) * Math.PI * 2;\n const distance = burst * distanceAt(i);\n return {\n id: i,\n x: Math.cos(angle) * distance,\n y: Math.sin(angle) * distance,\n dotSize: dotSizeAt(i),\n round: i % 2 === 0,\n ...(colourAt ? { colour: colourAt(i) } : {}),\n ...(withSpin ? { spin: (i % 2 === 0 ? 1 : -1) * 120 } : {}),\n };\n });\n\ntype ConfettiParticlesProps = {\n particles: ConfettiParticle[];\n /** Illustration confetti drifts down slightly and spins. */\n variant?: \"illustration\" | \"icon\";\n delay?: number;\n duration?: number;\n /** When set, particles use currentColor instead of per-particle colours. */\n useCurrentColor?: boolean;\n};\n\nexport const ConfettiParticles = ({\n particles,\n variant = \"illustration\",\n delay = variant === \"illustration\" ? 0.18 : 0.1,\n duration = variant === \"illustration\" ? 1.1 : 0.8,\n useCurrentColor = variant === \"icon\",\n}: ConfettiParticlesProps) =>\n particles.map((p) => (\n <motion.span\n key={p.id}\n initial={{ x: 0, y: 0, scale: 0, opacity: 0 }}\n animate={\n variant === \"illustration\"\n ? {\n x: [0, p.x],\n y: [0, p.y, p.y + 10],\n scale: [0, 1, 0.7],\n opacity: [0, 1, 0],\n rotate: p.spin != null ? [0, p.spin] : undefined,\n }\n : {\n x: [0, p.x],\n y: [0, p.y],\n scale: [0, 1, 0.6],\n opacity: [0, 1, 0],\n }\n }\n transition={{\n duration,\n ease: \"easeOut\",\n times: variant === \"illustration\" ? [0, 0.25, 1] : [0, 0.3, 1],\n delay,\n }}\n style={{\n position: \"absolute\",\n width: p.dotSize,\n height: p.dotSize,\n borderRadius: p.round ? \"50%\" : 1,\n backgroundColor: useCurrentColor ? \"currentColor\" : p.colour,\n pointerEvents: \"none\",\n }}\n />\n ));\n\ntype AnimationBadgeProps = {\n /** Diameter in px. */\n size: number;\n iconSize: number;\n className?: string;\n children?: ReactNode;\n};\n\nexport const AnimationBadge = ({\n size,\n iconSize,\n className,\n children,\n}: AnimationBadgeProps) => (\n <span\n className={[styles.Badge, className].filter(Boolean).join(\" \")}\n style={{ width: size, height: size }}\n >\n {children ?? <Check size={iconSize} strokeWidth={3} aria-hidden />}\n </span>\n);\n","/**\n * Languages supported by the component library's built-in translations. The\n * consumer selects one via `LeaProvider`'s `language` prop; components read it\n * with `useLanguage` (usually through `createTranslation`).\n */\nexport const LANGUAGES = [\"no\", \"se\", \"fi\", \"es\"] as const;\n\nexport type Language = (typeof LANGUAGES)[number];\n\n/** Fallback language used when no provider is present or a key is missing. */\nexport const DEFAULT_LANGUAGE = \"no\" satisfies Language;\n","\"use client\";\n\nimport { createContext, useContext } from \"react\";\nimport { DEFAULT_LANGUAGE, type Language } from \"./languages\";\n\n/**\n * Holds the currently selected language. Provided by `LeaProvider` and read by\n * components (typically via `createTranslation`). Defaults to `DEFAULT_LANGUAGE`\n * so components still render sensibly when used without the provider.\n */\nexport const LanguageContext = createContext<Language>(DEFAULT_LANGUAGE);\n\n/** Returns the language from the nearest `LeaProvider`, or `DEFAULT_LANGUAGE`. */\nexport const useLanguage = (): Language => useContext(LanguageContext);\n","\"use client\";\n\nimport { useLanguage } from \"./language-context\";\nimport { DEFAULT_LANGUAGE, type Language } from \"./languages\";\n\n/**\n * Translations for a single string, keyed by language. The `no` key is required\n * so the `DEFAULT_LANGUAGE` fallback always resolves; the others are optional.\n */\nexport type TranslationMap = { no: string } & Partial<Record<Language, string>>;\n\n/** A named set of translatable strings, each keyed by language. */\nexport type TranslationSchema = Record<string, TranslationMap>;\n\n/**\n * Creates a hook that returns co-located strings for the current language,\n * each falling back to `no`. Define the schema at module scope and call the\n * returned hook inside a component:\n *\n * ```tsx\n * const useLabels = createTranslation({\n * increase: { no: \"Øk\", se: \"Öka\", fi: \"Lisää\", es: \"Aumentar\" },\n * decrease: { no: \"Reduser\", se: \"Minska\", fi: \"Vähennä\", es: \"Disminuir\" },\n * });\n *\n * function Stepper() {\n * const t = useLabels();\n * return <button aria-label={t.increase} />;\n * }\n * ```\n */\nexport function createTranslation<T extends TranslationSchema>(schema: T) {\n return function useTranslation(): Record<keyof T, string> {\n const language = useLanguage();\n const result = {} as Record<keyof T, string>;\n (Object.keys(schema) as (keyof T)[]).forEach((key) => {\n const map = schema[key];\n result[key] = map[language] ?? map[DEFAULT_LANGUAGE];\n });\n return result;\n };\n}\n","import { createTranslation } from \"../../i18n\";\n\nexport const useAnimationLabels = createTranslation({\n pending: {\n no: \"Venter\",\n se: \"Väntar\",\n fi: \"Odottaa\",\n es: \"Esperando\",\n },\n loading: {\n no: \"Laster\",\n se: \"Laddar\",\n fi: \"Ladataan\",\n es: \"Cargando\",\n },\n done: { no: \"Ferdig\", se: \"Klart\", fi: \"Valmis\", es: \"Completado\" },\n confirmed: {\n no: \"Bekreftet\",\n se: \"Bekräftat\",\n fi: \"Vahvistettu\",\n es: \"Confirmado\",\n },\n});\n","\"use client\";\n\nimport { motion, useReducedMotion } from \"motion/react\";\nimport { useMemo } from \"react\";\n\nimport { colors } from \"../../../theme/tokens/colors\";\nimport { palette } from \"../../../theme/tokens/palette\";\nimport styles from \"../animations.module.css\";\nimport {\n AnimationBadge,\n type AnimationColor,\n type AnimationSize,\n buildParticles,\n ConfettiParticles,\n} from \"../shared\";\nimport { useAnimationLabels } from \"../use-animation-labels\";\n\ntype CheckAnimationProps = {\n /**\n * `\"sm\"` is 90px, `\"lg\"` 150px.\n * @default \"lg\"\n */\n size?: AnimationSize;\n /**\n * Colour of the badge and its halo.\n * @default \"primary\"\n */\n color?: AnimationColor;\n /**\n * Accessible name for the illustration; defaults to the translated word for\n * \"confirmed\" in the `LeaProvider` language.\n */\n label?: string;\n};\n\n// Shared square footprint with PulseAnimation: `box` is the rendered square and\n// `badge` matches the pulse centre circle, so both animations line up at a given\n// size. The confetti is free to overflow the square.\nconst sizeMap = {\n sm: { box: 90, badge: 60, icon: 30, burst: 60 },\n lg: { box: 150, badge: 100, icon: 50, burst: 100 },\n};\n\nconst CONFETTI_COUNT = 14;\n\n// The four brand colours. Blue and orange have no semantic token of their own;\n// confetti is not a status, so it does not read `status.info` or\n// `status.warning`.\nconst confettiColours = [\n palette.primary.base,\n palette.tertiary.base,\n colors.blue[400],\n colors.orange[400],\n];\n\nexport const CheckAnimation = ({\n size = \"lg\",\n color = \"primary\",\n label,\n}: CheckAnimationProps) => {\n const prefersReducedMotion = useReducedMotion();\n const labels = useAnimationLabels();\n const { box, badge, icon, burst } = sizeMap[size];\n\n const particles = useMemo(\n () =>\n buildParticles({\n count: CONFETTI_COUNT,\n burst,\n distanceAt: (i) => 0.6 + (i % 3) * 0.15,\n dotSizeAt: (i) => 5 + (i % 3) * 2,\n colourAt: (i) => confettiColours[i % confettiColours.length],\n withSpin: true,\n }),\n [burst],\n );\n\n return (\n <div\n role=\"img\"\n aria-label={label ?? labels.confirmed}\n className={styles.Check}\n data-color={color}\n style={{ width: box, height: box }}\n >\n <span className={styles.Halo} style={{ width: badge, height: badge }} />\n\n {!prefersReducedMotion && (\n <ConfettiParticles particles={particles} variant=\"illustration\" />\n )}\n\n <motion.div\n initial={prefersReducedMotion ? false : { scale: 0, rotate: -25 }}\n animate={\n prefersReducedMotion ? {} : { scale: [0, 1.12, 1], rotate: [-25, 0] }\n }\n transition={{\n duration: 0.55,\n ease: \"easeOut\",\n times: [0, 0.65, 1],\n }}\n style={{ display: \"flex\" }}\n >\n <AnimationBadge\n size={badge}\n iconSize={icon}\n className={styles.CheckBadge}\n />\n </motion.div>\n </div>\n );\n};\n","import styles from \"./animations.module.css\";\n\ntype SpinnerProps = {\n /** Diameter in px. */\n size: number;\n /** Stroke width, in the units of a 44px circle. */\n thickness?: number;\n /**\n * Accessible name. With one, the spinner is a `progressbar`; without, it is\n * hidden from assistive tech and its parent says what is loading.\n */\n label?: string;\n className?: string;\n};\n\nconst BOX = 44;\n\n/**\n * An indeterminate spinner: an arc in the current colour that turns at a\n * constant speed. Internal to the animations.\n */\nexport const Spinner = ({\n size,\n thickness = 3.6,\n label,\n className,\n}: SpinnerProps) => (\n <span\n {...(label != null\n ? { role: \"progressbar\", \"aria-label\": label }\n : { \"aria-hidden\": true })}\n className={[styles.Spinner, className].filter(Boolean).join(\" \")}\n style={{ width: size, height: size }}\n >\n <svg\n className={styles.SpinnerSvg}\n viewBox={`${BOX / 2} ${BOX / 2} ${BOX} ${BOX}`}\n >\n <circle\n cx={BOX}\n cy={BOX}\n r={(BOX - thickness) / 2}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={thickness}\n strokeDasharray=\"80 200\"\n />\n </svg>\n </span>\n);\n","\"use client\";\n\nimport styles from \"../animations.module.css\";\nimport { type AnimationColor, type AnimationSize } from \"../shared\";\nimport { Spinner } from \"../spinner\";\nimport { useAnimationLabels } from \"../use-animation-labels\";\n\ntype PulseAnimationProps = {\n /**\n * `\"sm\"` is 90px, `\"lg\"` 150px.\n * @default \"lg\"\n */\n size?: AnimationSize;\n /**\n * Colour of the rings and the centre circle.\n * @default \"primary\"\n */\n color?: AnimationColor;\n /**\n * Accessible name for the spinner; defaults to the translated word for\n * \"loading\" in the `LeaProvider` language. `Loader` hides the pulse from\n * assistive tech and announces its own status text instead.\n */\n label?: string;\n};\n\n// Ratios are kept constant across variants so the pulse expands the same\n// amount relative to the centre circle, and the spinner stays proportional.\nconst sizeMap = {\n sm: { container: 90, circle: 60, spinner: 28 },\n lg: { container: 150, circle: 100, spinner: 48 },\n};\n\nexport const PulseAnimation = ({\n size = \"lg\",\n color = \"primary\",\n label,\n}: PulseAnimationProps) => {\n const { container, circle, spinner } = sizeMap[size];\n const labels = useAnimationLabels();\n\n const circleSize = { width: circle, height: circle };\n\n return (\n <div\n className={styles.Pulse}\n data-color={color}\n style={{ width: container, height: container }}\n >\n <span className={styles.PulseRing} style={circleSize} />\n <span\n className={styles.PulseRing}\n style={{ ...circleSize, animationDelay: \"0.4s\" }}\n />\n <span className={styles.PulseCentre} style={circleSize} />\n <Spinner\n size={spinner}\n label={label ?? labels.loading}\n className={styles.PulseSpinner}\n />\n </div>\n );\n};\n","\"use client\";\n\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\";\nimport { useEffect, useRef, useState } from \"react\";\n\nimport styles from \"../animations.module.css\";\nimport { CheckAnimation } from \"../check-animation/check-animation\";\nimport { PulseAnimation } from \"../pulse-animation/pulse-animation\";\nimport { type AnimationColor, type AnimationSize } from \"../shared\";\nimport { useAnimationLabels } from \"../use-animation-labels\";\n\nexport type LoaderStatus = \"loading\" | \"done\";\n\nexport type LoaderProps = {\n /**\n * `\"loading\"` shows the pulse. `\"done\"` cross-fades into the check and fires\n * its one-time confetti.\n * @default \"loading\"\n */\n status?: LoaderStatus;\n /**\n * `\"sm\"` is 90px, `\"lg\"` 150px.\n * @default \"lg\"\n */\n size?: AnimationSize;\n /**\n * Colour of the pulse and of the check (and its halo).\n * @default \"primary\"\n */\n color?: AnimationColor;\n /**\n * Minimum time (ms) the pulse stays visible once mounted in the loading\n * state, so a very fast response doesn't make the spinner flash. The check\n * appears as soon as `status` is `\"done\"` *and* this minimum has elapsed.\n */\n minLoadingMs?: number;\n /**\n * Visually-hidden status text announced while loading. Defaults to the\n * translated word for \"loading\" in the `LeaProvider` language.\n */\n loadingLabel?: string;\n /**\n * Visually-hidden status text announced once done. Defaults to the translated\n * word for \"done\".\n */\n doneLabel?: string;\n};\n\nconst boxSize = { sm: 90, lg: 150 };\n\n/**\n * The library's loading indicator: shows {@link PulseAnimation} while\n * `status` is `\"loading\"`, then cross-fades into {@link CheckAnimation}\n * (firing its one-time confetti) once `status` becomes `\"done\"`. Announces\n * state changes politely and respects `prefers-reduced-motion`.\n */\nexport const Loader = ({\n status = \"loading\",\n size = \"lg\",\n color = \"primary\",\n minLoadingMs = 0,\n loadingLabel,\n doneLabel,\n}: LoaderProps) => {\n const loading = status === \"loading\";\n const prefersReducedMotion = useReducedMotion();\n const labels = useAnimationLabels();\n const [done, setDone] = useState(!loading);\n const loadingStart = useRef<number | null>(loading ? Date.now() : null);\n\n useEffect(() => {\n if (loading) {\n loadingStart.current = Date.now();\n setDone(false);\n return;\n }\n\n const elapsed =\n loadingStart.current != null\n ? Date.now() - loadingStart.current\n : minLoadingMs;\n const remaining = Math.max(0, minLoadingMs - elapsed);\n\n if (remaining === 0) {\n setDone(true);\n return;\n }\n\n const timer = window.setTimeout(() => setDone(true), remaining);\n return () => window.clearTimeout(timer);\n }, [loading, minLoadingMs]);\n\n const side = boxSize[size];\n const duration = prefersReducedMotion ? 0 : 0.35;\n\n return (\n <div\n role=\"status\"\n aria-live=\"polite\"\n className={styles.Status}\n style={{ width: side, height: side }}\n >\n <AnimatePresence mode=\"sync\">\n {done ? (\n <motion.div\n key=\"check\"\n aria-hidden\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration }}\n className={styles.Layer}\n >\n <CheckAnimation size={size} color={color} />\n </motion.div>\n ) : (\n <motion.div\n key=\"pulse\"\n aria-hidden\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration }}\n className={styles.Layer}\n >\n <PulseAnimation size={size} color={color} />\n </motion.div>\n )}\n </AnimatePresence>\n <span className={styles.SrOnly}>\n {done ? (doneLabel ?? labels.done) : (loadingLabel ?? labels.loading)}\n </span>\n </div>\n );\n};\n","\"use client\";\n\nimport { Loader } from \"../loader/loader\";\nimport { type AnimationColor, type AnimationSize } from \"../shared\";\n\ntype StatusAnimationProps = {\n /** While `true`, shows the pulse; when it flips to `false`, transitions to the check. */\n loading: boolean;\n /**\n * `\"sm\"` is 90px, `\"lg\"` 150px.\n * @default \"lg\"\n */\n size?: AnimationSize;\n /**\n * Colour of the pulse and of the resulting check (and its halo).\n * @default \"primary\"\n */\n color?: AnimationColor;\n /**\n * Minimum time (ms) the pulse stays visible once mounted in the loading\n * state, so a very fast response doesn't make the spinner flash. The check\n * appears as soon as `loading` is `false` *and* this minimum has elapsed.\n */\n minLoadingMs?: number;\n /**\n * Visually-hidden status text announced while loading. Defaults to the\n * translated word for \"loading\" in the `LeaProvider` language.\n */\n loadingLabel?: string;\n /**\n * Visually-hidden status text announced once done. Defaults to the translated\n * word for \"done\".\n */\n doneLabel?: string;\n};\n\n/**\n * A controlled loading → success indicator: the pulse while `loading`, then\n * the check once `loading` becomes `false`.\n *\n * @deprecated Use {@link Loader}, which renders the same thing:\n * `<Loader status={loading ? \"loading\" : \"done\"} />`.\n */\nexport const StatusAnimation = ({ loading, ...rest }: StatusAnimationProps) => (\n <Loader status={loading ? \"loading\" : \"done\"} {...rest} />\n);\n","\"use client\";\n\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\";\nimport { useMemo } from \"react\";\n\nimport styles from \"../animations.module.css\";\nimport {\n AnimationBadge,\n type AnimationColor,\n buildParticles,\n ConfettiParticles,\n} from \"../shared\";\nimport { Spinner } from \"../spinner\";\nimport { useAnimationLabels } from \"../use-animation-labels\";\n\ntype StatusIconStatus = \"pending\" | \"loading\" | \"done\";\n\ntype StatusIconProps = {\n status: StatusIconStatus;\n /** Rendered size in px. */\n size?: number;\n /**\n * Colour of the spinner and of the filled check.\n * @default \"primary\"\n */\n color?: AnimationColor;\n /**\n * Accessible name; defaults to the translated label for the current status in\n * the `LeaProvider` language.\n */\n label?: string;\n};\n\nconst CONFETTI_COUNT = 8;\n\n/**\n * A small, inline status icon for step / progress lists. Transitions through\n * `pending` (empty ring) → `loading` (spinner) → `done` (filled check with a\n * one-time confetti pop). One-shot, not infinite. Respects\n * `prefers-reduced-motion`.\n */\nexport const StatusIcon = ({\n status,\n size = 24,\n color = \"primary\",\n label,\n}: StatusIconProps) => {\n const prefersReducedMotion = useReducedMotion();\n const labels = useAnimationLabels();\n\n const ring = Math.max(2, Math.round(size * 0.09));\n const thickness = (ring * 44) / size;\n const checkSize = Math.round(size * 0.6);\n const burst = size;\n\n const particles = useMemo(\n () =>\n buildParticles({\n count: CONFETTI_COUNT,\n burst,\n distanceAt: (i) => 0.7 + (i % 2) * 0.25,\n dotSizeAt: (i) => Math.max(2, Math.round(size * 0.1)) + (i % 2),\n }),\n [burst, size],\n );\n\n const fadeDuration = prefersReducedMotion ? 0 : 0.2;\n\n return (\n <span\n role=\"img\"\n aria-label={label ?? labels[status]}\n className={styles.StatusIcon}\n data-color={color}\n style={{ width: size, height: size }}\n >\n <AnimatePresence mode=\"wait\">\n {status === \"pending\" && (\n <motion.span\n key=\"pending\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: fadeDuration }}\n className={styles.State}\n >\n <span className={styles.Ring} style={{ borderWidth: ring }} />\n </motion.span>\n )}\n\n {status === \"loading\" && (\n <motion.span\n key=\"loading\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: fadeDuration }}\n className={styles.State}\n >\n <Spinner size={size} thickness={thickness} />\n </motion.span>\n )}\n\n {status === \"done\" && (\n <motion.span\n key=\"done\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: fadeDuration }}\n className={styles.State}\n >\n <motion.span\n initial={prefersReducedMotion ? false : { scale: 0 }}\n animate={prefersReducedMotion ? {} : { scale: [0, 1.18, 1] }}\n transition={{\n duration: 0.4,\n ease: \"easeOut\",\n times: [0, 0.6, 1],\n }}\n style={{ display: \"flex\" }}\n >\n <AnimationBadge size={size} iconSize={checkSize} />\n </motion.span>\n </motion.span>\n )}\n </AnimatePresence>\n\n {!prefersReducedMotion && status === \"done\" && (\n <ConfettiParticles particles={particles} variant=\"icon\" />\n )}\n </span>\n );\n};\n","type ClassNameProp<State> =\n | string\n | ((state: State) => string | undefined)\n | undefined;\n\n/**\n * Base UI replaces `className` wholesale, which would drop the module class of\n * a wrapper component. Returning a callback keeps the state argument working\n * for consumers who pass a function.\n *\n * Internal helper — not part of the public API, so it isn't re-exported from\n * `src/utils/index.ts`.\n */\nexport const mergeClassName =\n <State,>(base: string, className: ClassNameProp<State>) =>\n (state: State) =>\n [base, typeof className === \"function\" ? className(state) : className]\n .filter(Boolean)\n .join(\" \");\n","/*\n * The website's call-to-action: an 18px semibold uppercase label, 32px from the\n * start edge and 32px from the arrow, which sits in a black disc inside a 4px\n * rim. The sizes and the hover come from the website's CTAButton.\n */\n.ActionButton {\n --action-button-height: 3.5rem;\n --action-button-disc: var(--lea-spacing-6);\n --action-button-arrow: var(--lea-spacing-3);\n\n position: relative;\n display: inline-flex;\n min-width: calc(var(--lea-spacing) * 8);\n min-height: var(--action-button-height);\n box-sizing: border-box;\n align-items: center;\n justify-content: center;\n gap: var(--lea-spacing-4);\n padding: var(--lea-spacing-0-5);\n padding-inline-start: var(--lea-spacing-4);\n border: 0;\n border-radius: var(--lea-radius-full);\n background-color: var(--lea-palette-bg-base);\n box-shadow: 0 0 0 1px var(--lea-palette-border-strong);\n color: var(--lea-palette-text-base);\n cursor: pointer;\n font-family: inherit;\n /* 16px on a phone, 18px from the website's `mobile` width up. */\n font-size: var(--lea-font-step-0);\n font-weight: var(--lea-font-weight-semibold);\n line-height: var(--lea-line-height-tight);\n text-decoration: none;\n text-transform: uppercase;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n}\n\n@media (min-width: 640px) {\n .ActionButton {\n font-size: var(--lea-font-step-1);\n }\n}\n\n.ActionButton[data-size=\"sm\"] {\n --action-button-height: 2.5rem;\n --action-button-disc: var(--lea-spacing-4);\n --action-button-arrow: var(--lea-spacing-2-5);\n}\n\n/* Only the arrow: a circle as tall as the button, with the label kept for\n screen readers. */\n.ActionButton[data-icon-only] {\n min-width: var(--action-button-height);\n padding-inline-start: var(--lea-spacing-0-5);\n}\n\n.ActionButton[data-icon-only] .Label {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n}\n\n.ActionButton:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n.ActionButton:active:not([data-disabled]) {\n box-shadow: 0 0 0 2px var(--lea-palette-primary-base);\n}\n\n.ActionButton[data-disabled] {\n background-color: var(--lea-palette-bg-subtle);\n color: var(--lea-palette-text-subtle);\n cursor: not-allowed;\n}\n\n.Label {\n min-width: 0;\n transition: translate var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n.Disc {\n display: flex;\n width: var(--action-button-disc);\n height: var(--action-button-disc);\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n border-radius: var(--lea-radius-full);\n background-color: var(--lea-palette-bg-contrast);\n color: var(--lea-palette-text-contrast);\n transition: background-color var(--lea-duration-normal)\n var(--lea-easing-standard);\n}\n\n.Arrow {\n display: block;\n width: var(--action-button-arrow);\n height: var(--action-button-arrow);\n transition: rotate var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n@media (hover: hover) {\n .ActionButton:hover:not([data-disabled]) .Label {\n translate: var(--lea-spacing-1) 0;\n }\n\n .ActionButton:hover:not([data-disabled]) .Disc {\n background-color: var(--lea-palette-primary-base);\n }\n\n .ActionButton:hover:not([data-disabled]) .Arrow {\n rotate: -45deg;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Label,\n .Disc,\n .Arrow {\n transition-duration: 0.01ms;\n }\n}\n",".Button {\n --button-solid: var(--lea-palette-primary-base);\n --button-solid-hover: var(--lea-palette-primary-hover);\n --button-on-solid: var(--lea-palette-primary-contrast);\n --button-fg: var(--lea-palette-primary-base);\n --button-border: var(--lea-palette-primary-base);\n --button-tint: color-mix(\n in srgb,\n var(--button-fg) calc(var(--lea-opacity-hover) * 100%),\n color-mix(\n in srgb,\n var(--lea-color-white) calc(var(--lea-opacity-highlight) * 100%),\n transparent\n )\n );\n\n --button-height: 2.75rem;\n --button-icon-size: 1.25rem;\n --button-padding-inline: calc(var(--lea-spacing) * 2.5);\n\n box-sizing: border-box;\n display: inline-flex;\n min-height: var(--button-height);\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n gap: var(--lea-spacing);\n padding-block: calc(var(--lea-spacing) * 0.5);\n padding-inline: var(--button-padding-inline);\n margin: 0;\n border: 1px solid transparent;\n border-radius: var(--lea-radius-full);\n background-color: transparent;\n color: var(--button-fg);\n font-family: inherit;\n font-size: var(--lea-text-label-lg-font-size);\n font-weight: var(--lea-text-label-lg-font-weight);\n line-height: var(--lea-text-label-lg-line-height);\n letter-spacing: var(--lea-text-label-lg-letter-spacing);\n text-align: center;\n text-decoration: none;\n cursor: pointer;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n transition: background-color var(--lea-duration-normal)\n var(--lea-easing-standard),\n border-color var(--lea-duration-normal) var(--lea-easing-standard),\n color var(--lea-duration-normal) var(--lea-easing-standard),\n opacity var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n.Button svg {\n width: var(--button-icon-size);\n height: var(--button-icon-size);\n flex-shrink: 0;\n}\n\n.Button:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n/* Colours */\n\n.Button[data-color=\"secondary\"] {\n --button-solid: var(--lea-palette-secondary-base);\n --button-solid-hover: var(--lea-palette-secondary-hover);\n --button-on-solid: var(--lea-palette-secondary-contrast);\n --button-fg: var(--lea-palette-secondary-base);\n --button-border: var(--lea-palette-secondary-base);\n}\n\n.Button[data-color=\"error\"] {\n --button-solid: var(--lea-color-red-600);\n --button-solid-hover: var(--lea-color-red-500);\n --button-on-solid: var(--lea-palette-text-contrast);\n --button-fg: var(--lea-color-red-600);\n --button-border: var(--lea-color-red-600);\n}\n\n/* Variants */\n\n.Button[data-variant=\"solid\"] {\n background-color: var(--button-solid);\n color: var(--button-on-solid);\n}\n\n.Button[data-variant=\"outline\"] {\n border-color: var(--button-border);\n}\n\n/* `IconButton` only: a white disc that floats over a picture or a section,\n with the website's soft shadow. */\n.Button[data-variant=\"elevated\"] {\n background-color: var(--lea-palette-bg-base);\n box-shadow: var(--lea-shadow-soft);\n}\n\n@media (hover: hover) {\n .Button[data-variant=\"solid\"]:not([data-disabled]):hover {\n background-color: var(--button-solid-hover);\n }\n\n .Button[data-variant=\"outline\"]:not([data-disabled]):hover,\n .Button[data-variant=\"ghost\"]:not([data-disabled]):hover {\n background-color: var(--button-tint);\n }\n\n /* The tint mixed into white, so the disc stays opaque over a picture. */\n .Button[data-variant=\"elevated\"]:not([data-disabled]):hover {\n background-color: color-mix(\n in srgb,\n var(--button-fg) calc(var(--lea-opacity-hover) * 100%),\n var(--lea-palette-bg-base)\n );\n }\n}\n\n/* Sizes */\n\n.Button[data-size=\"sm\"] {\n --button-height: 2.25rem;\n --button-icon-size: 1rem;\n --button-padding-inline: calc(var(--lea-spacing) * 2);\n}\n\n.Button[data-size=\"lg\"] {\n --button-height: 3.25rem;\n --button-icon-size: 1.5rem;\n --button-padding-inline: calc(var(--lea-spacing) * 2.5);\n}\n\n.Button[data-start-icon] {\n padding-inline-start: calc(var(--button-padding-inline) - var(--lea-spacing));\n}\n\n.Button[data-end-icon] {\n padding-inline-end: calc(var(--button-padding-inline) - var(--lea-spacing));\n}\n\n.Button.IconButton {\n width: var(--button-height);\n height: var(--button-height);\n padding: 0;\n}\n\n/* The website's 40px disc (carousel arrows, video button); sm and lg keep the\n heights of the other variants. */\n.Button.IconButton[data-variant=\"elevated\"][data-size=\"md\"] {\n --button-height: 2.5rem;\n}\n\n.Button[data-disabled]:not([data-loading]) {\n color: var(--lea-color-neutral-400);\n cursor: not-allowed;\n}\n\n.Button[data-variant=\"solid\"][data-disabled]:not([data-loading]) {\n background-color: var(--lea-color-neutral-200);\n color: var(--lea-color-neutral-600);\n}\n\n.Button[data-variant=\"outline\"][data-disabled]:not([data-loading]) {\n border-color: var(--lea-palette-border-base);\n}\n\n.Button[data-loading] {\n cursor: progress;\n opacity: 0.7;\n}\n\n.Spinner {\n box-sizing: border-box;\n display: inline-block;\n width: var(--button-icon-size);\n height: var(--button-icon-size);\n flex-shrink: 0;\n border: 2px solid currentColor;\n border-inline-end-color: transparent;\n border-radius: var(--lea-radius-full);\n animation: button-spin 0.8s linear infinite;\n}\n\n@keyframes button-spin {\n to {\n rotate: 360deg;\n }\n}\n\n.SrOnly {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n border: 0;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Button {\n transition-duration: 0.01ms;\n }\n\n .Spinner {\n animation: none;\n }\n}\n","\"use client\";\n\nimport { Button as BaseButton } from \"@base-ui/react/button\";\nimport { useRender } from \"@base-ui/react/use-render\";\nimport {\n isValidElement,\n type MouseEvent,\n type ReactElement,\n type Ref,\n} from \"react\";\n\nimport { mergeClassName } from \"../../utils/merge-class-name\";\nimport type { ColorRoles } from \"../color-roles\";\nimport styles from \"./button.module.css\";\n\nexport type ButtonVariant = \"solid\" | \"outline\" | \"ghost\";\nexport type ButtonColor = ColorRoles<\"primary\" | \"secondary\" | \"error\">;\nexport type ButtonSize = \"sm\" | \"md\" | \"lg\";\n\nexport type ButtonRootProps = Omit<BaseButton.Props, \"color\" | \"ref\"> & {\n /**\n * Shows the busy state: the button is disabled, keeps its colour at reduced\n * opacity and sets `aria-busy`.\n * @default false\n */\n loading?: boolean;\n /** The rendered element: the `<button>`, or the link passed to `render`. */\n ref?: Ref<HTMLElement>;\n};\n\nexport type ButtonBaseProps = ButtonRootProps & {\n /**\n * Visual weight.\n * - `\"solid\"` — filled, for the main action.\n * - `\"outline\"` — bordered, no fill.\n * - `\"ghost\"` — no border or fill until hovered.\n * @default \"solid\"\n */\n variant?: ButtonVariant;\n /**\n * Colour scheme: `\"primary\"` is the brand purple, `\"secondary\"` the\n * near-black, `\"error\"` is for destructive actions.\n * @default \"primary\"\n */\n color?: ButtonColor;\n /**\n * @default \"md\"\n */\n size?: ButtonSize;\n};\n\n/**\n * A `render` element carrying `href` (an `<a>`, Next.js `Link`) or `to`\n * (React Router `Link`) is a link. Base UI's `Button` would give it\n * `role=\"button\"`, so links are rendered without button semantics.\n */\nconst isLinkElement = (\n render: ButtonRootProps[\"render\"],\n): render is ReactElement<{ href?: unknown; to?: unknown }> =>\n isValidElement<{ href?: unknown; to?: unknown }>(render) &&\n (render.props.href != null || render.props.to != null);\n\nconst preventDefault = (event: MouseEvent) => event.preventDefault();\n\n/**\n * A button, or the link passed to `render`, with the disabled and busy states\n * wired. It has no look of its own: the class comes from the caller. Internal\n * — not exported from the package.\n */\nexport function ButtonRoot({\n loading = false,\n disabled,\n className,\n render,\n nativeButton,\n focusableWhenDisabled,\n ref,\n ...rest\n}: ButtonRootProps) {\n const isDisabled = Boolean(disabled || loading);\n const isLink = isLinkElement(render);\n\n const shared = {\n \"aria-busy\": loading || undefined,\n \"data-loading\": loading || undefined,\n };\n\n const link = useRender({\n enabled: isLink,\n render: isLink ? render : undefined,\n ref,\n defaultTagName: \"a\",\n props: {\n ...rest,\n ...shared,\n \"aria-disabled\": isDisabled || undefined,\n \"data-disabled\": isDisabled ? \"\" : undefined,\n className:\n typeof className === \"function\"\n ? className({ disabled: isDisabled })\n : className,\n onClick: isDisabled ? preventDefault : rest.onClick,\n },\n });\n\n if (link) return link;\n\n return (\n <BaseButton\n {...rest}\n {...shared}\n ref={ref}\n render={render}\n nativeButton={nativeButton}\n focusableWhenDisabled={focusableWhenDisabled}\n disabled={isDisabled}\n className={className}\n />\n );\n}\n\n/**\n * The root shared by `Button`, `IconButton`, `CloseButton` and\n * `DownloadTrigger`: `ButtonRoot` with the button look. Internal — not\n * exported from the package. It also takes `\"elevated\"`, which only\n * `IconButton` offers.\n */\nexport function ButtonBase({\n variant = \"solid\",\n color = \"primary\",\n size = \"md\",\n className,\n ...rest\n}: Omit<ButtonBaseProps, \"variant\"> & {\n variant?: ButtonVariant | \"elevated\";\n}) {\n return (\n <ButtonRoot\n {...rest}\n data-variant={variant}\n data-color={color}\n data-size={size}\n className={mergeClassName(styles.Button, className)}\n />\n );\n}\n","\"use client\";\n\nimport { ArrowRight } from \"lucide-react\";\nimport type { ReactNode } from \"react\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport styles from \"./action-button.module.css\";\nimport { ButtonRoot, type ButtonRootProps } from \"../button-base\";\n\nexport type ActionButtonSize = \"sm\" | \"lg\";\n\n/**\n * With `iconOnly` the label is hidden but stays the accessible name, so it is\n * required there.\n */\ntype ActionButtonLabel =\n | {\n /**\n * Shows only the arrow: a round button, with the label kept for screen\n * readers. Used where space is short, such as a collapsed\n * `ExpandingCards` item.\n * @default false\n */\n iconOnly?: false;\n children?: ReactNode;\n }\n | { iconOnly: true; children: ReactNode };\n\nexport type ActionButtonProps = Omit<ButtonRootProps, \"loading\" | \"children\"> &\n ActionButtonLabel & {\n /**\n * `\"lg\"` is 56px tall with a 48px arrow; `\"sm\"` is 40px tall with a 32px\n * arrow, for headers and tight rows.\n * @default \"lg\"\n */\n size?: ActionButtonSize;\n };\n\n/**\n * A pill-shaped call-to-action button with a built-in arrow icon.\n *\n * Used for primary navigation actions on the website, such as hero sections\n * and landing pages. On hover the label slides towards the arrow and the arrow\n * turns purple and points up and to the right. To render a link, pass it\n * through `render`; an element with `href` or `to` is rendered as a real link,\n * without button semantics:\n *\n * ```tsx\n * <ActionButton render={<a href=\"/lan\" />}>Se våra lån</ActionButton>\n * <ActionButton render={<NextLink href=\"/lan\" />}>Se våra lån</ActionButton>\n * <ActionButton render={<RouterLink to=\"/lan\" />}>Se våra lån</ActionButton>\n * <ActionButton size=\"sm\" iconOnly render={<a href=\"/lan\" />}>Se våra lån</ActionButton>\n * ```\n */\nexport function ActionButton({\n children,\n className,\n size = \"lg\",\n iconOnly = false,\n ...rest\n}: ActionButtonProps) {\n return (\n <ButtonRoot\n {...rest}\n data-size={size}\n data-icon-only={iconOnly || undefined}\n className={mergeClassName(styles.ActionButton, className)}\n >\n {children != null && <span className={styles.Label}>{children}</span>}\n <span aria-hidden className={styles.Disc}>\n <ArrowRight className={styles.Arrow} />\n </span>\n </ButtonRoot>\n );\n}\n","\"use client\";\n\nimport type { ReactNode } from \"react\";\n\nimport { ButtonBase, type ButtonBaseProps } from \"../button-base\";\nimport styles from \"../button.module.css\";\n\nexport type {\n ButtonColor,\n ButtonSize,\n ButtonVariant,\n} from \"../button-base\";\n\nexport type ButtonProps = ButtonBaseProps & {\n /** Icon before the label. Replaced by the spinner while `loading`. */\n startIcon?: ReactNode;\n /** Icon after the label. */\n endIcon?: ReactNode;\n /**\n * Text announced to screen readers while `loading`, through a\n * visually-hidden `role=\"status\"` region, e.g. \"Saving…\".\n */\n loadingLabel?: string;\n};\n\n/**\n * A button built on Base UI's `Button` primitive and styled with Lea's design\n * tokens. Pick a `variant` (`solid`, `outline`, `ghost`), a `color`\n * (`primary`, `secondary`, `error`) and a `size` (`sm`, `md`, `lg`).\n *\n * ```tsx\n * <Button variant=\"outline\" startIcon={<Plus aria-hidden />}>Add</Button>\n * ```\n *\n * To render a link that looks like a button, pass the link through `render`.\n * An element with `href` or `to` is rendered as a real link, without button\n * semantics:\n *\n * ```tsx\n * <Button render={<a href=\"/about\" />}>About</Button>\n * <Button render={<NextLink href=\"/about\" />}>About</Button>\n * <Button render={<RouterLink to=\"/about\" />}>About</Button>\n * ```\n *\n * For a link inside running text, use `Link` instead.\n */\nexport function Button({\n startIcon,\n endIcon,\n loading = false,\n loadingLabel,\n children,\n ...rest\n}: ButtonProps) {\n return (\n <>\n <ButtonBase\n {...rest}\n loading={loading}\n data-start-icon={loading || startIcon != null || undefined}\n data-end-icon={endIcon != null || undefined}\n >\n {loading ? <span className={styles.Spinner} aria-hidden /> : startIcon}\n {children}\n {endIcon}\n </ButtonBase>\n {loadingLabel != null && (\n <span role=\"status\" className={styles.SrOnly}>\n {loading ? loadingLabel : null}\n </span>\n )}\n </>\n );\n}\n","import { createTranslation } from \"../../i18n\";\n\n/**\n * Translated accessible name for the close (X) button shared by the feedback\n * overlays (`SimpleDialog`, `SimpleDrawer`). Consumers can override per-instance\n * via a `closeLabel` prop.\n */\nexport const useCloseLabel = createTranslation({\n close: {\n no: \"Lukk\",\n se: \"Stäng\",\n fi: \"Sulje\",\n es: \"Cerrar\",\n },\n});\n","\"use client\";\n\nimport type { ReactNode } from \"react\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport {\n ButtonBase,\n type ButtonBaseProps,\n type ButtonVariant,\n} from \"../button-base\";\nimport styles from \"../button.module.css\";\n\n/**\n * The variants of `Button`, plus `\"elevated\"`: a white disc with the website's\n * soft shadow, for controls that float over a picture or a coloured section\n * (the arrows of a carousel, the play button of a video). At `md` it is 40px,\n * the website's size; `sm` and `lg` match the other variants.\n */\nexport type IconButtonVariant = ButtonVariant | \"elevated\";\n\n/**\n * Requires an accessible name so the button is never nameless. Provide exactly\n * one of `aria-label` or `aria-labelledby`.\n */\ntype IconButtonAccessibleName =\n | { \"aria-label\": string; \"aria-labelledby\"?: never }\n | { \"aria-labelledby\": string; \"aria-label\"?: never };\n\nexport type IconButtonProps = Omit<\n ButtonBaseProps,\n \"children\" | \"aria-label\" | \"aria-labelledby\" | \"variant\"\n> &\n IconButtonAccessibleName & {\n /** The icon. Mark it `aria-hidden` — the name comes from `aria-label`. */\n children: ReactNode;\n /**\n * Visual weight: `\"solid\"`, `\"outline\"` and `\"ghost\"` as on `Button`, or\n * `\"elevated\"`, a white disc with the website's soft shadow\n * (`shadow-soft`) and the icon in `color`; 40px at `md`.\n * @default \"solid\"\n */\n variant?: IconButtonVariant;\n };\n\n/**\n * A square, icon-only button with the same variants, colours and sizes as\n * `Button`. Because it has no visible text, TypeScript requires an\n * `aria-label` or `aria-labelledby`.\n *\n * ```tsx\n * <IconButton aria-label=\"Add\" variant=\"outline\">\n * <Plus aria-hidden />\n * </IconButton>\n * ```\n */\nexport function IconButton({\n loading = false,\n className,\n children,\n ...rest\n}: IconButtonProps) {\n return (\n <ButtonBase\n {...rest}\n loading={loading}\n className={mergeClassName(styles.IconButton, className)}\n >\n {loading ? <span className={styles.Spinner} aria-hidden /> : children}\n </ButtonBase>\n );\n}\n","\"use client\";\n\nimport { X } from \"lucide-react\";\n\nimport { useCloseLabel } from \"../../feedback/use-close-label\";\nimport type { ButtonBaseProps } from \"../button-base\";\nimport { IconButton } from \"../icon-button/icon-button\";\n\nexport type CloseButtonProps = Omit<\n ButtonBaseProps,\n \"children\" | \"aria-label\" | \"aria-labelledby\"\n> & {\n /** Accessible name. Defaults to a translated \"Close\". */\n label?: string;\n};\n\n/**\n * An `IconButton` with a built-in X icon. It always has an accessible name —\n * a translated \"Close\" unless you pass `label`.\n *\n * ```tsx\n * <CloseButton onClick={() => setOpen(false)} />\n * ```\n */\nexport function CloseButton({\n label,\n variant = \"ghost\",\n color = \"secondary\",\n ...rest\n}: CloseButtonProps) {\n const t = useCloseLabel();\n\n return (\n <IconButton\n {...rest}\n variant={variant}\n color={color}\n aria-label={label ?? t.close}\n >\n <X aria-hidden />\n </IconButton>\n );\n}\n","export type DownloadableData =\n | string\n | Blob\n | File\n | ArrayBuffer\n | Uint8Array;\n\nexport type DownloadDataSource =\n | DownloadableData\n | (() => DownloadableData | Promise<DownloadableData>);\n\nexport async function resolveDownloadData(\n data: DownloadDataSource,\n): Promise<DownloadableData> {\n return typeof data === \"function\" ? data() : data;\n}\n\nexport function downloadFile(\n data: DownloadableData,\n fileName: string,\n mimeType?: string,\n): void {\n const blob =\n data instanceof Blob\n ? data\n : new Blob([data as BlobPart], mimeType ? { type: mimeType } : undefined);\n\n const url = URL.createObjectURL(blob);\n const anchor = document.createElement(\"a\");\n anchor.href = url;\n anchor.download = fileName;\n anchor.rel = \"noopener\";\n anchor.click();\n URL.revokeObjectURL(url);\n}\n","import { createTranslation } from \"../../../i18n\";\n\n/**\n * Translated default copy for `DownloadTrigger`. Consumers can override it by\n * passing `children`.\n */\nexport const useDownloadLabels = createTranslation({\n download: {\n no: \"Last ned\",\n se: \"Ladda ner\",\n fi: \"Lataa\",\n es: \"Descargar\",\n },\n});\n","\"use client\";\n\nimport {\n downloadFile,\n resolveDownloadData,\n type DownloadDataSource,\n} from \"@/utils/download-file\";\nimport { useState } from \"react\";\n\nimport { Button, type ButtonProps } from \"../button/button\";\nimport { useDownloadLabels } from \"./use-download-labels\";\n\ntype DownloadTriggerProps = Omit<ButtonProps, \"onClick\"> & {\n /** File contents, or a function/promise that resolves to them. */\n data: DownloadDataSource;\n /** File name. */\n fileName: string;\n /** MIME type. */\n mimeType?: string;\n /** Callback for when the download starts. */\n onDownload?: () => void;\n /** Callback for when the download fails. */\n onDownloadError?: (error: unknown) => void;\n};\n\n/**\n * A button that triggers a download of a file. Shares `Button`'s variants,\n * colours and sizes, and shows the loading state while `data` resolves.\n *\n * Without `children` the label is the translated word for \"download\" in the\n * `LeaProvider` language.\n */\n\nexport const DownloadTrigger = ({\n data,\n fileName,\n mimeType,\n onDownload,\n onDownloadError,\n loading: loadingProp,\n children,\n ...rest\n}: DownloadTriggerProps) => {\n const labels = useDownloadLabels();\n const [pending, setPending] = useState(false);\n const loading = loadingProp ?? pending;\n\n const handleClick = async () => {\n setPending(true);\n try {\n const resolved = await resolveDownloadData(data);\n downloadFile(resolved, fileName, mimeType);\n onDownload?.();\n } catch (error) {\n onDownloadError?.(error);\n } finally {\n setPending(false);\n }\n };\n\n return (\n <Button {...rest} loading={loading} onClick={handleClick}>\n {children ?? labels.download}\n </Button>\n );\n};\n",".SkipLink {\n position: absolute;\n z-index: 1000;\n inset-block-start: var(--lea-spacing-1);\n inset-inline-start: var(--lea-spacing-1);\n}\n\n.SkipLink:not(:focus) {\n clip-path: inset(50%);\n pointer-events: none;\n}\n","\"use client\";\n\nimport type { ReactNode } from \"react\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport { ButtonBase, type ButtonBaseProps } from \"../button-base\";\nimport styles from \"./skip-link.module.css\";\n\nexport type SkipLinkProps = Pick<ButtonBaseProps, \"className\" | \"style\"> & {\n /** The id of the main content, as a fragment: `#main`. */\n href: string;\n children: ReactNode;\n};\n\n/**\n * A link for keyboard users to jump past the header to the main content. It\n * stays hidden until it receives focus, so place it first in the page and it\n * is the first thing the Tab key reaches:\n *\n * ```tsx\n * <body>\n * <SkipLink href=\"#main\">Hopp til hovedinnhold</SkipLink>\n * <AppHeader />\n * <main id=\"main\" tabIndex={-1}>…</main>\n * </body>\n * ```\n *\n * It is positioned against the page, so no ancestor should be positioned.\n */\nexport function SkipLink({ href, children, className, style }: SkipLinkProps) {\n return (\n <ButtonBase\n render={<a href={href} />}\n color=\"secondary\"\n className={mergeClassName(styles.SkipLink, className)}\n style={style}\n >\n {children}\n </ButtonBase>\n );\n}\n","/**\n * The first font-size scale, from before the text styles.\n *\n * @deprecated Use a text style (`textStyles`, `--lea-text-{role}-{size}-*`,\n * `text-heading-lg`) or, for a size on its own, a step on the type scale\n * (`fontStep`, `--lea-font-step-{n}`). Kept for the components that still use\n * it, and removed in a later major.\n */\nexport const fontSizes = {\n xs: \"0.75rem\",\n sm: \"0.875rem\",\n md: \"1rem\",\n lg: \"1.25rem\",\n xl: \"1.5rem\",\n \"2xl\": \"2rem\",\n \"3xl\": \"2.5rem\",\n \"4xl\": \"3rem\",\n \"5xl\": \"3.5rem\",\n \"6xl\": \"4rem\",\n} as const;\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n */\n\n/* Without a `textStyle`, text is `body-md`. Every Text sets all four\n properties itself, so one nested in another does not take its parent's. */\n.Text {\n margin: 0;\n color: var(--lea-palette-text-base);\n font-size: var(--lea-text-body-md-font-size);\n font-weight: var(--lea-text-body-md-font-weight);\n line-height: var(--lea-text-body-md-line-height);\n letter-spacing: var(--lea-text-body-md-letter-spacing);\n}\n\n/* One rule per text style. tests/tokens.test.ts checks the list against\n src/theme/tokens/text-styles.ts. */\n.Text[data-text-style=\"display-sm\"] {\n font-size: var(--lea-text-display-sm-font-size);\n font-weight: var(--lea-text-display-sm-font-weight);\n line-height: var(--lea-text-display-sm-line-height);\n letter-spacing: var(--lea-text-display-sm-letter-spacing);\n}\n\n.Text[data-text-style=\"display-md\"] {\n font-size: var(--lea-text-display-md-font-size);\n font-weight: var(--lea-text-display-md-font-weight);\n line-height: var(--lea-text-display-md-line-height);\n letter-spacing: var(--lea-text-display-md-letter-spacing);\n}\n\n.Text[data-text-style=\"heading-xs\"] {\n font-size: var(--lea-text-heading-xs-font-size);\n font-weight: var(--lea-text-heading-xs-font-weight);\n line-height: var(--lea-text-heading-xs-line-height);\n letter-spacing: var(--lea-text-heading-xs-letter-spacing);\n}\n\n.Text[data-text-style=\"heading-sm\"] {\n font-size: var(--lea-text-heading-sm-font-size);\n font-weight: var(--lea-text-heading-sm-font-weight);\n line-height: var(--lea-text-heading-sm-line-height);\n letter-spacing: var(--lea-text-heading-sm-letter-spacing);\n}\n\n.Text[data-text-style=\"heading-md\"] {\n font-size: var(--lea-text-heading-md-font-size);\n font-weight: var(--lea-text-heading-md-font-weight);\n line-height: var(--lea-text-heading-md-line-height);\n letter-spacing: var(--lea-text-heading-md-letter-spacing);\n}\n\n.Text[data-text-style=\"heading-lg\"] {\n font-size: var(--lea-text-heading-lg-font-size);\n font-weight: var(--lea-text-heading-lg-font-weight);\n line-height: var(--lea-text-heading-lg-line-height);\n letter-spacing: var(--lea-text-heading-lg-letter-spacing);\n}\n\n.Text[data-text-style=\"heading-xl\"] {\n font-size: var(--lea-text-heading-xl-font-size);\n font-weight: var(--lea-text-heading-xl-font-weight);\n line-height: var(--lea-text-heading-xl-line-height);\n letter-spacing: var(--lea-text-heading-xl-letter-spacing);\n}\n\n.Text[data-text-style=\"title-md\"] {\n font-size: var(--lea-text-title-md-font-size);\n font-weight: var(--lea-text-title-md-font-weight);\n line-height: var(--lea-text-title-md-line-height);\n letter-spacing: var(--lea-text-title-md-letter-spacing);\n}\n\n.Text[data-text-style=\"title-xl\"] {\n font-size: var(--lea-text-title-xl-font-size);\n font-weight: var(--lea-text-title-xl-font-weight);\n line-height: var(--lea-text-title-xl-line-height);\n letter-spacing: var(--lea-text-title-xl-letter-spacing);\n}\n\n.Text[data-text-style=\"body-xs\"] {\n font-size: var(--lea-text-body-xs-font-size);\n font-weight: var(--lea-text-body-xs-font-weight);\n line-height: var(--lea-text-body-xs-line-height);\n letter-spacing: var(--lea-text-body-xs-letter-spacing);\n}\n\n.Text[data-text-style=\"body-sm\"] {\n font-size: var(--lea-text-body-sm-font-size);\n font-weight: var(--lea-text-body-sm-font-weight);\n line-height: var(--lea-text-body-sm-line-height);\n letter-spacing: var(--lea-text-body-sm-letter-spacing);\n}\n\n.Text[data-text-style=\"body-md\"] {\n font-size: var(--lea-text-body-md-font-size);\n font-weight: var(--lea-text-body-md-font-weight);\n line-height: var(--lea-text-body-md-line-height);\n letter-spacing: var(--lea-text-body-md-letter-spacing);\n}\n\n.Text[data-text-style=\"body-lg\"] {\n font-size: var(--lea-text-body-lg-font-size);\n font-weight: var(--lea-text-body-lg-font-weight);\n line-height: var(--lea-text-body-lg-line-height);\n letter-spacing: var(--lea-text-body-lg-letter-spacing);\n}\n\n.Text[data-text-style=\"label-sm\"] {\n font-size: var(--lea-text-label-sm-font-size);\n font-weight: var(--lea-text-label-sm-font-weight);\n line-height: var(--lea-text-label-sm-line-height);\n letter-spacing: var(--lea-text-label-sm-letter-spacing);\n}\n\n.Text[data-text-style=\"label-md\"] {\n font-size: var(--lea-text-label-md-font-size);\n font-weight: var(--lea-text-label-md-font-weight);\n line-height: var(--lea-text-label-md-line-height);\n letter-spacing: var(--lea-text-label-md-letter-spacing);\n}\n\n.Text[data-text-style=\"label-lg\"] {\n font-size: var(--lea-text-label-lg-font-size);\n font-weight: var(--lea-text-label-lg-font-weight);\n line-height: var(--lea-text-label-lg-line-height);\n letter-spacing: var(--lea-text-label-lg-letter-spacing);\n}\n\n/* After the text styles, so a `fontWeight` wins over the style's own. */\n.Text[data-font-weight=\"thin\"] {\n font-weight: var(--lea-font-weight-thin);\n}\n\n.Text[data-font-weight=\"light\"] {\n font-weight: var(--lea-font-weight-light);\n}\n\n.Text[data-font-weight=\"regular\"] {\n font-weight: var(--lea-font-weight-regular);\n}\n\n.Text[data-font-weight=\"medium\"] {\n font-weight: var(--lea-font-weight-medium);\n}\n\n.Text[data-font-weight=\"semibold\"] {\n font-weight: var(--lea-font-weight-semibold);\n}\n\n.Text[data-font-weight=\"bold\"] {\n font-weight: var(--lea-font-weight-bold);\n}\n\n.Text[data-font-weight=\"black\"] {\n font-weight: var(--lea-font-weight-black);\n}\n\n.Text[data-color=\"inherit\"] {\n color: inherit;\n}\n\n.Text[data-align=\"start\"] {\n text-align: start;\n}\n\n.Text[data-align=\"center\"] {\n text-align: center;\n}\n\n.Text[data-align=\"end\"] {\n text-align: end;\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport type { Ref } from \"react\";\n\nimport type { ColorPath } from \"../../../theme/token-types\";\nimport { fontSizes } from \"../../../theme/tokens/font-sizes\";\nimport type { fontWeights } from \"../../../theme/tokens/font-weights\";\nimport type { TextStyleToken } from \"../../../theme/tokens/text-styles\";\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport styles from \"./text.module.css\";\n\ntype FontSizeToken = keyof typeof fontSizes;\ntype FontWeightToken = keyof typeof fontWeights;\n\n/** A palette token such as `text.subtle`, or `inherit` for the parent's colour. */\nexport type TextColor = ColorPath | \"inherit\";\nexport type TextAlign = \"start\" | \"center\" | \"end\";\n\nexport type TextProps = Omit<useRender.ComponentProps<\"p\">, \"ref\" | \"color\"> & {\n /**\n * A text style such as `heading-lg` or `body-sm`: size, weight, line height\n * and letter spacing as one token. It says how the text looks, not which\n * element it is, so pick the element with `render` from the page hierarchy.\n * @default \"body-md\"\n */\n textStyle?: TextStyleToken;\n /** A font weight token, when the text style's own weight does not fit. */\n fontWeight?: FontWeightToken;\n /**\n * A colour token from the palette, written as its path. Use `inherit` to\n * take the colour of the surrounding text.\n * @default \"text.base\"\n */\n color?: TextColor;\n /** Text alignment, following the reading direction. */\n align?: TextAlign;\n /**\n * @deprecated Use `textStyle`. The `fontSize` scale (`xs` to `6xl`) is from\n * before the text styles and is removed in a later major.\n */\n fontSize?: FontSizeToken;\n /** The rendered element: the `<p>`, or the element passed to `render`. */\n ref?: Ref<HTMLElement>;\n};\n\nconst paletteVariable = (path: ColorPath) =>\n `var(--lea-palette-${path.replaceAll(\".\", \"-\")})`;\n\n/**\n * Text set in one of Lea's text styles. Renders a `p` with no margin; pass\n * `render` for another element:\n *\n * ```tsx\n * <Text>Vi svarer innen to virkedager.</Text>\n * <Text render={<h1 />} textStyle=\"heading-lg\">Mine lån</Text>\n * <Text render={<span />} textStyle=\"body-sm\" color=\"text.subtle\">\n * Sist oppdatert i dag\n * </Text>\n * ```\n *\n * Spacing around the text belongs to the layout: use `Stack` and its `gap`, or\n * `className` and `style`.\n */\nexport function Text({\n textStyle,\n fontWeight,\n color,\n align,\n fontSize,\n render,\n ref,\n className,\n style,\n ...rest\n}: TextProps) {\n return useRender({\n render,\n ref,\n defaultTagName: \"p\",\n props: {\n ...rest,\n \"data-text-style\": textStyle,\n \"data-font-weight\": fontWeight,\n \"data-align\": align,\n \"data-color\": color === \"inherit\" ? color : undefined,\n className: mergeClassName(styles.Text, className)({}),\n style: {\n ...(color != null && color !== \"inherit\"\n ? { color: paletteVariable(color) }\n : undefined),\n ...(fontSize != null ? { fontSize: fontSizes[fontSize] } : undefined),\n ...style,\n },\n },\n });\n}\n",".DataField {\n box-sizing: border-box;\n display: flex;\n width: 100%;\n align-items: center;\n justify-content: space-between;\n gap: var(--lea-spacing-2);\n padding-block: calc(var(--lea-spacing) * 1.75);\n padding-inline: var(--lea-spacing-1);\n}\n\n.DataField[data-divider] {\n border-block-end: 1px solid var(--lea-palette-border-base);\n}\n\n.Label {\n min-width: 0;\n flex: 1;\n}\n\n.Value {\n min-width: 0;\n text-align: end;\n}\n","import type { ComponentProps, ReactNode } from \"react\";\n\nimport { Text } from \"../../typography/text/text\";\nimport styles from \"./data-field.module.css\";\n\nexport type DataFieldProps = Omit<ComponentProps<\"div\">, \"children\"> & {\n label: ReactNode;\n value: ReactNode;\n /**\n * Draws a line under the row, for stacking several into a summary.\n * @default true\n */\n divider?: boolean;\n};\n\nconst isText = (content: ReactNode) =>\n typeof content === \"string\" || typeof content === \"number\";\n\n/**\n * A single label/value row for presenting read-only details, with an optional\n * bottom divider so several fields can be stacked into a summary list.\n *\n * ```tsx\n * <DataField label=\"Lånebeløp\" value=\"150 000 kr\" />\n * <DataField label=\"Månedsbeløp\" value=\"1 316 kr\" divider={false} />\n * ```\n *\n * Text passed as `label` or `value` is set in the label and value styles. Any\n * other element is rendered as it is, for a side that needs richer content.\n */\nexport const DataField = ({\n label,\n value,\n divider = true,\n className,\n ...rest\n}: DataFieldProps) => (\n <div\n {...rest}\n className={[styles.DataField, className].filter(Boolean).join(\" \")}\n data-divider={divider || undefined}\n >\n <div className={styles.Label}>\n {isText(label) ? (\n <Text render={<span />} color=\"text.subtle\">\n {label}\n </Text>\n ) : (\n label\n )}\n </div>\n <div className={styles.Value}>\n {isText(value) ? (\n <Text render={<span />} fontWeight=\"medium\">\n {value}\n </Text>\n ) : (\n value\n )}\n </div>\n </div>\n);\n","/*\n * `--card-tint` is the fill and `--card-line` the border of the `outlined`\n * variant. The colours read the same values as `Chip`.\n */\n.Card {\n --card-tint: var(--lea-palette-bg-base);\n --card-line: var(--lea-palette-border-base);\n\n box-sizing: border-box;\n overflow: hidden;\n border: 1px solid var(--card-line);\n border-radius: var(--lea-radius-4xl);\n background-color: var(--card-tint);\n color: var(--lea-palette-text-base);\n}\n\n/* The border stays, transparent, so both variants take the same room. */\n.Card[data-variant=\"filled\"] {\n border-color: transparent;\n}\n\n.Card[data-color=\"primary\"] {\n --card-tint: var(--lea-palette-primary-subtle);\n --card-line: var(--lea-color-purple-600);\n}\n\n.Card[data-color=\"tertiary\"] {\n --card-tint: var(--lea-palette-tertiary-subtle);\n --card-line: var(--lea-color-teal-700);\n}\n\n.Card[data-color=\"neutral\"] {\n --card-tint: var(--lea-color-neutral-100);\n --card-line: var(--lea-color-neutral-400);\n}\n\n.Card[data-color=\"success\"] {\n --card-tint: var(--lea-palette-status-success-subtle);\n --card-line: var(--lea-color-teal-700);\n}\n\n.Card[data-color=\"info\"] {\n --card-tint: var(--lea-palette-status-info-subtle);\n --card-line: var(--lea-color-blue-600);\n}\n\n.Card[data-color=\"warning\"] {\n --card-tint: var(--lea-palette-status-warning-subtle);\n --card-line: var(--lea-color-amber-700);\n}\n\n.Card[data-color=\"error\"] {\n --card-tint: var(--lea-palette-status-error-subtle);\n --card-line: var(--lea-color-red-600);\n}\n\n.Content {\n box-sizing: border-box;\n padding: var(--lea-spacing-2);\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport type { Ref } from \"react\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport type { ColorRoles } from \"../../color-roles\";\nimport styles from \"./card.module.css\";\n\nexport type CardColor = ColorRoles<\n \"primary\" | \"tertiary\" | \"neutral\" | \"success\" | \"info\" | \"warning\" | \"error\"\n>;\nexport type CardVariant = \"outlined\" | \"filled\";\n\nexport type CardProps = Omit<\n useRender.ComponentProps<\"div\">,\n \"ref\" | \"color\"\n> & {\n /** The rendered element: the `<div>`, or the element passed to `render`. */\n ref?: Ref<HTMLElement>;\n /**\n * A tint for the card. Left out, the card is white.\n * - A brand tint: `\"primary\"` (purple), `\"tertiary\"` (teal) or `\"neutral\"`\n * (grey).\n * - A status: `\"success\"` (teal), `\"info\"` (blue), `\"warning\"` (amber) or\n * `\"error\"` (red).\n */\n color?: CardColor;\n /**\n * With a 1px border, or without one. The card keeps its size either way, so\n * the two line up in a row.\n * @default \"outlined\"\n */\n variant?: CardVariant;\n};\n\n/**\n * A flat surface that groups related content: white, with a 1px border and\n * rounded corners. It has no padding of its own, so an image or a divider can\n * run edge to edge; put text in `CardContent`.\n *\n * Set `color` for a tint, and `variant=\"filled\"` to drop the border:\n *\n * ```tsx\n * <Card color=\"primary\" variant=\"filled\">…</Card>\n * ```\n *\n * ```tsx\n * <Card>\n * <CardContent>\n * <Text textStyle=\"heading-sm\">Forbrukslån</Text>\n * <Text color=\"text.subtle\">150 000 kr over 10 år</Text>\n * </CardContent>\n * </Card>\n * ```\n *\n * Renders a `div`; pass `render` for another element, e.g.\n * `render={<section />}` or `render={<li />}`.\n */\nexport function Card({\n render,\n ref,\n className,\n color,\n variant = \"outlined\",\n ...rest\n}: CardProps) {\n return useRender({\n render,\n ref,\n props: {\n ...rest,\n \"data-color\": color,\n \"data-variant\": variant,\n className: mergeClassName(styles.Card, className)({}),\n },\n });\n}\n\nexport type CardContentProps = Omit<useRender.ComponentProps<\"div\">, \"ref\"> & {\n /** The rendered element: the `<div>`, or the element passed to `render`. */\n ref?: Ref<HTMLElement>;\n};\n\n/**\n * The padded area of a `Card`. A card can hold several, separated by whatever\n * sits between them.\n */\nexport function CardContent({\n render,\n ref,\n className,\n ...rest\n}: CardContentProps) {\n return useRender({\n render,\n ref,\n props: {\n ...rest,\n className: mergeClassName(styles.Content, className)({}),\n },\n });\n}\n",".Chip {\n --chip-tint: var(--lea-palette-primary-subtle);\n --chip-shade: var(--lea-color-purple-800);\n --chip-line: var(--lea-color-purple-600);\n --chip-ink: var(--lea-color-purple-600);\n --chip-outlined-disc: var(--chip-ink);\n --chip-outlined-cross: var(--chip-tint);\n\n --chip-edge: calc(var(--lea-spacing) * 0.75);\n --chip-gap: calc(var(--lea-spacing) * 0.75);\n --chip-label-edge: calc(var(--lea-spacing) * 1.75);\n --chip-padding-block: calc(var(--lea-spacing) * 0.25);\n --chip-delete-size: 1.25rem;\n\n display: inline-flex;\n max-width: 100%;\n box-sizing: border-box;\n align-items: center;\n padding-block: var(--chip-padding-block);\n border: 1px solid transparent;\n border-radius: var(--lea-radius-full);\n background-color: var(--chip-tint);\n color: var(--chip-shade);\n font-size: var(--lea-text-label-md-font-size);\n font-weight: var(--lea-text-label-md-font-weight);\n gap: var(--chip-gap);\n letter-spacing: var(--lea-text-label-md-letter-spacing);\n line-height: var(--lea-line-height-normal);\n vertical-align: middle;\n white-space: nowrap;\n}\n\n.Chip[data-size=\"sm\"] {\n --chip-edge: var(--lea-spacing-0-5);\n --chip-gap: var(--lea-spacing-0-5);\n --chip-label-edge: var(--lea-spacing-1);\n --chip-padding-block: 0px;\n --chip-delete-size: 1.125rem;\n}\n\n/*\n * The website's pill: 16px semibold, 4px above and below the text and 16px at\n * the sides, each counted with the 1px border (hence the minus 1px), 28px tall.\n */\n.Chip[data-size=\"lg\"] {\n --chip-edge: var(--lea-spacing-1);\n --chip-gap: var(--lea-spacing-1);\n --chip-label-edge: calc(var(--lea-spacing-2) - 1px);\n --chip-padding-block: calc(var(--lea-spacing-0-5) - 1px);\n\n /* heading-xs, at the tight line height of a label. */\n font-size: var(--lea-text-heading-xs-font-size);\n font-weight: var(--lea-text-heading-xs-font-weight);\n letter-spacing: var(--lea-text-heading-xs-letter-spacing);\n line-height: var(--lea-line-height-tight);\n}\n\n.Chip[data-color=\"tertiary\"] {\n --chip-tint: var(--lea-palette-tertiary-subtle);\n --chip-shade: var(--lea-color-teal-800);\n --chip-line: var(--lea-color-teal-700);\n --chip-ink: var(--lea-color-teal-700);\n}\n\n.Chip[data-color=\"neutral\"] {\n --chip-tint: var(--lea-color-neutral-100);\n --chip-shade: var(--lea-color-neutral-800);\n --chip-line: var(--lea-color-neutral-400);\n --chip-ink: var(--lea-color-neutral-700);\n}\n\n/* The website's beige pill: the words stay `text.base`. */\n.Chip[data-color=\"muted\"] {\n --chip-tint: var(--lea-palette-bg-muted);\n --chip-shade: var(--lea-palette-text-base);\n --chip-line: var(--lea-color-beige-400);\n --chip-ink: var(--lea-palette-text-base);\n}\n\n.Chip[data-color=\"success\"] {\n --chip-tint: var(--lea-palette-status-success-subtle);\n --chip-shade: var(--lea-color-teal-800);\n --chip-line: var(--lea-color-teal-700);\n --chip-ink: var(--lea-color-teal-700);\n}\n\n.Chip[data-color=\"info\"] {\n --chip-tint: var(--lea-palette-status-info-subtle);\n --chip-shade: var(--lea-color-blue-800);\n --chip-line: var(--lea-color-blue-600);\n --chip-ink: var(--lea-color-blue-600);\n --chip-outlined-disc: var(--lea-color-white);\n --chip-outlined-cross: var(--lea-color-blue-600);\n}\n\n.Chip[data-color=\"warning\"] {\n --chip-tint: var(--lea-palette-status-warning-subtle);\n --chip-shade: var(--lea-color-amber-800);\n --chip-line: var(--lea-color-amber-700);\n --chip-ink: var(--lea-color-amber-700);\n}\n\n.Chip[data-color=\"error\"] {\n --chip-tint: var(--lea-palette-status-error-subtle);\n --chip-shade: var(--lea-color-red-800);\n --chip-line: var(--lea-color-red-600);\n --chip-ink: var(--lea-color-red-600);\n}\n\n.Chip[data-variant=\"outlined\"] {\n border-color: var(--chip-line);\n background-color: transparent;\n color: var(--chip-ink);\n}\n\n.Chip[data-disabled] {\n opacity: 0.38;\n}\n\n.Chip:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n.Label {\n overflow: hidden;\n min-width: 0;\n padding-inline: var(--chip-label-edge);\n text-overflow: ellipsis;\n}\n\n.Icon + .Label {\n padding-inline-start: 0;\n}\n\n.Label:has(+ .Delete) {\n padding-inline-end: 0;\n}\n\n.Icon {\n display: inline-flex;\n flex-shrink: 0;\n margin-inline-start: var(--chip-edge);\n}\n\n.Icon svg {\n display: block;\n}\n\n.Delete {\n display: inline-flex;\n width: var(--chip-delete-size);\n height: var(--chip-delete-size);\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n padding: 0;\n border: 0;\n border-radius: var(--lea-radius-full);\n margin-inline-end: var(--chip-edge);\n background: none;\n color: var(--chip-shade);\n cursor: pointer;\n transition: opacity var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n.Delete svg {\n display: block;\n width: 100%;\n height: 100%;\n fill: var(--chip-tint);\n scale: calc(24 / 22);\n}\n\n.Chip[data-variant=\"outlined\"] .Delete {\n color: var(--chip-outlined-cross);\n}\n\n.Chip[data-variant=\"outlined\"] .Delete svg {\n fill: var(--chip-outlined-disc);\n}\n\n.Delete:disabled {\n cursor: not-allowed;\n}\n\n.Delete:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n@media (hover: hover) {\n .Delete:hover:not(:disabled) {\n opacity: 0.85;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Delete {\n transition-duration: 0.01ms;\n }\n}\n","import { createTranslation } from \"../../../i18n\";\n\n/**\n * Translated accessible name for `Chip`'s delete affordance, which MUI renders\n * as an unlabelled icon. Consumers can override it by passing their own\n * `deleteIcon`.\n */\nexport const useChipLabels = createTranslation({\n remove: { no: \"Fjern\", se: \"Ta bort\", fi: \"Poista\", es: \"Eliminar\" },\n});\n","\"use client\";\n\nimport { CircleX } from \"lucide-react\";\nimport type { ComponentProps, MouseEvent, ReactNode } from \"react\";\n\nimport type { ColorRoles } from \"../../color-roles\";\nimport styles from \"./chip.module.css\";\nimport { useChipLabels } from \"./use-chip-labels\";\n\nexport type ChipColor = ColorRoles<\n | \"primary\"\n | \"tertiary\"\n | \"neutral\"\n | \"muted\"\n | \"success\"\n | \"info\"\n | \"warning\"\n | \"error\"\n>;\nexport type ChipVariant = \"filled\" | \"outlined\";\nexport type ChipSize = \"sm\" | \"md\" | \"lg\";\n\nexport type ChipProps = Omit<ComponentProps<\"span\">, \"children\" | \"color\"> & {\n /** The text of the chip. */\n label: ReactNode;\n /**\n * What the chip stands for.\n * - A tag without a status: `\"primary\"` (purple), `\"tertiary\"` (teal) or\n * `\"neutral\"` (grey); on the website, `\"muted\"` (beige), as the category\n * and date of an article (with `size=\"lg\"`).\n * - A status: `\"success\"` (teal), `\"info\"` (blue), `\"warning\"` (amber) or\n * `\"error\"` (red).\n * @default \"primary\"\n */\n color?: ChipColor;\n /**\n * Tinted fill, or a border on a transparent background.\n * @default \"filled\"\n */\n variant?: ChipVariant;\n /**\n * `sm` for dense rows and inside other controls, `md` on its own, `lg` for\n * the website's pills (16px semibold, 28px tall), as the category and date\n * of an article or the facts of a job opening. Every colour and variant\n * comes in every size.\n * @default \"md\"\n */\n size?: ChipSize;\n /** Icon before the label. Mark a decorative icon `aria-hidden`. */\n icon?: ReactNode;\n /**\n * Shows a remove button after the label and is called when it is pressed.\n * The button is named in the `LeaProvider` language and includes the label,\n * so a row of chips reads unambiguously: \"Fjern Medlånetaker\".\n */\n onDelete?: (event: MouseEvent<HTMLButtonElement>) => void;\n /** Accessible name of the remove button, when the default does not fit. */\n deleteLabel?: string;\n /** Dims the chip and disables its remove button. */\n disabled?: boolean;\n};\n\n/**\n * A compact label for tags, statuses, or filter selections.\n *\n * ```tsx\n * <Chip label=\"Medlånetaker\" />\n * <Chip label=\"Verifisert\" color=\"success\" icon={<CircleCheck size={16} aria-hidden />} />\n * <Chip label=\"Oslo\" variant=\"outlined\" onDelete={() => remove(\"oslo\")} />\n * ```\n *\n * The chip itself is not interactive; only its remove button is. Inside a\n * widget that moves focus itself, `tabIndex={-1}` takes the chip and its\n * remove button out of the tab order.\n */\nexport function Chip({\n label,\n color = \"primary\",\n variant = \"filled\",\n size = \"md\",\n icon,\n onDelete,\n deleteLabel,\n disabled = false,\n className,\n tabIndex,\n ...rest\n}: ChipProps) {\n const labels = useChipLabels();\n\n const deleteName =\n deleteLabel ??\n (typeof label === \"string\" ? `${labels.remove} ${label}` : labels.remove);\n\n return (\n <span\n {...rest}\n tabIndex={tabIndex}\n className={[styles.Chip, className].filter(Boolean).join(\" \")}\n data-color={color}\n data-variant={variant}\n data-size={size}\n data-disabled={disabled ? \"\" : undefined}\n >\n {icon != null && <span className={styles.Icon}>{icon}</span>}\n <span className={styles.Label}>{label}</span>\n {onDelete && (\n <button\n type=\"button\"\n aria-label={deleteName}\n className={styles.Delete}\n disabled={disabled}\n tabIndex={tabIndex === -1 ? -1 : undefined}\n onClick={onDelete}\n >\n <CircleX aria-hidden />\n </button>\n )}\n </span>\n );\n}\n",".Positioner {\n z-index: 1500;\n}\n\n.Popup {\n --tooltip-bg: var(--lea-palette-bg-base);\n --tooltip-fg: var(--lea-palette-text-base);\n\n box-sizing: border-box;\n max-width: 18.75rem;\n padding: var(--lea-spacing-1);\n border-radius: var(--lea-radius-sm);\n background-color: var(--tooltip-bg);\n box-shadow: var(--lea-shadow-md);\n color: var(--tooltip-fg);\n font-size: var(--lea-text-body-sm-font-size);\n font-weight: var(--lea-text-body-sm-font-weight);\n letter-spacing: var(--lea-text-body-sm-letter-spacing);\n line-height: var(--lea-text-body-sm-line-height);\n outline: none;\n overflow-wrap: break-word;\n transform-origin: var(--transform-origin);\n transition: opacity var(--lea-duration-fast) var(--lea-easing-standard),\n scale var(--lea-duration-fast) var(--lea-easing-standard);\n}\n\n.Popup[data-surface=\"dark\"] {\n --tooltip-bg: var(--lea-palette-bg-contrast);\n --tooltip-fg: var(--lea-palette-text-contrast);\n}\n\n.Popup[data-starting-style],\n.Popup[data-ending-style] {\n opacity: 0;\n scale: 0.95;\n}\n\n/* A square turned on its corner, half of it behind the edge of the bubble. */\n.Arrow {\n width: 0.625rem;\n height: 0.625rem;\n background-color: var(--tooltip-bg);\n rotate: 45deg;\n}\n\n.Arrow[data-side=\"top\"] {\n bottom: -0.3125rem;\n}\n\n.Arrow[data-side=\"bottom\"] {\n top: -0.3125rem;\n}\n\n.Arrow[data-side=\"left\"] {\n right: -0.3125rem;\n}\n\n.Arrow[data-side=\"right\"] {\n left: -0.3125rem;\n}\n\n/* The built-in trigger */\n\n.TriggerBox {\n display: inline-flex;\n}\n\n/*\n * A 44px target around a 16px icon. The negative margin gives the extra\n * 14px on each side back, so the button takes the room of the icon alone and\n * lines up with the text next to it.\n */\n.Trigger {\n display: inline-flex;\n width: calc(var(--lea-spacing) * 5.5);\n height: calc(var(--lea-spacing) * 5.5);\n box-sizing: border-box;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n padding: 0;\n border: 0;\n border-radius: var(--lea-radius-full);\n margin: calc(var(--lea-spacing) * -1.75);\n background-color: transparent;\n color: var(--lea-palette-primary-base);\n cursor: pointer;\n line-height: 0;\n transition: color var(--lea-duration-fast) var(--lea-easing-standard);\n}\n\n.Trigger[data-variant=\"filled\"] {\n color: var(--lea-palette-primary-contrast);\n}\n\n.Trigger[data-variant=\"outlined\"] {\n color: var(--lea-palette-text-base);\n}\n\n.Trigger:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n@media (hover: hover) {\n .Trigger:hover {\n color: var(--lea-palette-primary-hover);\n }\n\n .Trigger[data-variant=\"filled\"]:hover {\n color: var(--lea-palette-primary-contrast);\n }\n\n .Trigger[data-variant=\"outlined\"]:hover {\n color: var(--lea-palette-primary-base);\n }\n\n .Trigger[data-variant=\"filled\"]:hover .Mark {\n background-color: var(--lea-palette-primary-hover);\n }\n}\n\n/* The default icon: an \"i\" in a circle, drawn from two bars. */\n.Mark {\n position: relative;\n display: inline-flex;\n width: var(--lea-spacing-2);\n height: var(--lea-spacing-2);\n box-sizing: border-box;\n flex-shrink: 0;\n border: 1px solid var(--lea-palette-primary-base);\n border-radius: var(--lea-radius-full);\n color: var(--lea-palette-primary-base);\n transition: background-color var(--lea-duration-fast)\n var(--lea-easing-standard);\n}\n\n.Mark::before,\n.Mark::after {\n position: absolute;\n width: calc(var(--lea-spacing) * 0.25);\n border-radius: var(--lea-radius-full);\n background-color: currentColor;\n content: \"\";\n inset: 50% auto auto 50%;\n}\n\n/* The stem and the dot, placed by eye around the centre of the circle. */\n.Mark::before {\n height: calc(var(--lea-spacing) * 0.625);\n translate: -50% -8%;\n}\n\n.Mark::after {\n height: calc(var(--lea-spacing) * 0.25);\n translate: -50% -190%;\n}\n\n.Trigger[data-variant=\"filled\"] .Mark {\n border-color: transparent;\n background-color: var(--lea-palette-primary-base);\n color: var(--lea-palette-primary-contrast);\n}\n\n.Trigger[data-variant=\"outlined\"] .Mark {\n width: calc(var(--lea-spacing) * 2.25);\n height: calc(var(--lea-spacing) * 2.25);\n border-color: var(--lea-palette-border-strong);\n background-color: var(--lea-palette-bg-base);\n color: var(--lea-palette-text-base);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Popup,\n .Trigger,\n .Mark {\n transition-duration: 0.01ms;\n }\n}\n","\"use client\";\n\nimport { Popover } from \"@base-ui/react/popover\";\nimport { Tooltip as BaseTooltip } from \"@base-ui/react/tooltip\";\nimport {\n useId,\n useState,\n type CSSProperties,\n type KeyboardEvent,\n type ReactElement,\n type ReactNode,\n} from \"react\";\n\nimport styles from \"./tooltip.module.css\";\n\nexport type TooltipTriggerVariant = \"icon\" | \"filled\" | \"outlined\";\nexport type TooltipSurfaceVariant = \"light\" | \"dark\";\nexport type TooltipOpenOn = \"hover\" | \"click\";\n\ntype Side = \"top\" | \"bottom\" | \"left\" | \"right\";\nexport type TooltipPlacement = Side | `${Side}-start` | `${Side}-end`;\n\ntype SharedProps = {\n /** What the tooltip says. Nothing is shown while it is empty. */\n title: ReactNode;\n /**\n * How the tooltip opens.\n * - `\"hover\"`: on mouse hover and on keyboard focus. It does not open on\n * touch, so use it for a hint the control works without.\n * - `\"click\"`: on click, tap, Enter or Space, and on mouse hover too. It\n * stays open until dismissed, so use it for information people need.\n *\n * Defaults to `\"hover\"` around `children` and to `\"click\"` for the\n * built-in trigger.\n */\n openOn?: TooltipOpenOn;\n /**\n * Where the tooltip sits against its trigger. It moves to another side when\n * there is no room.\n * @default \"top\"\n */\n placement?: TooltipPlacement;\n /**\n * Colours of the bubble: black text on white, or white text on black.\n * @default \"light\"\n */\n surfaceVariant?: TooltipSurfaceVariant;\n};\n\ntype WrappedTooltipProps = SharedProps & {\n /**\n * The trigger: one element that takes a `ref` and spreads its props, such\n * as a `Button`. It needs an accessible name of its own.\n */\n children: ReactElement<TriggerElementProps>;\n ariaLabel?: never;\n triggerIcon?: never;\n triggerVariant?: never;\n className?: never;\n style?: never;\n};\n\ntype TriggerTooltipProps = SharedProps & {\n children?: never;\n /** Accessible name for the built-in trigger button. */\n ariaLabel: string;\n /** Trigger icon or content. Defaults to an \"i\" in a circle. */\n triggerIcon?: ReactNode;\n /**\n * Visual style of the built-in trigger button.\n * @default \"icon\"\n */\n triggerVariant?: TooltipTriggerVariant;\n /** Class on the built-in trigger button. */\n className?: string;\n /** Inline style on the built-in trigger button. */\n style?: CSSProperties;\n};\n\nexport type TooltipProps = WrappedTooltipProps | TriggerTooltipProps;\n\ntype TriggerElementProps = {\n id?: string;\n \"aria-describedby\"?: string;\n};\n\n/** MUI showed nothing for these either, so an optional text needs no branch. */\nconst isEmpty = (title: ReactNode) =>\n title == null || title === false || title === \"\";\n\nconst position = (placement: TooltipPlacement) => {\n const [side, align = \"center\"] = placement.split(\"-\") as [\n Side,\n (\"start\" | \"end\")?,\n ];\n return { side, align, sideOffset: OFFSET } as const;\n};\n\nconst TABBABLE =\n 'a[href], button:not([disabled]), input, select, textarea, [tabindex]:not([tabindex=\"-1\"])';\n\n// The distance from the trigger to the bubble; the arrow fills half of it.\nconst OFFSET = 14;\n// Short enough to feel immediate, long enough not to flash while the pointer\n// crosses the trigger on its way elsewhere.\nconst HOVER_DELAY = 100;\n\n/**\n * A small bubble of text anchored to a trigger, on Base UI's `Tooltip` and\n * `Popover`.\n *\n * Use it two ways:\n * - **Around a child:** pass `children` to add a hint to a control you already\n * have. It opens on hover and keyboard focus and describes the control.\n * - **Built-in trigger:** omit `children` and pass `ariaLabel`; a small info\n * button is rendered for you. It opens on click, so it works on touch.\n *\n * ```tsx\n * <Tooltip title=\"Kopier kontonummer\">\n * <IconButton aria-label=\"Kopier\">…</IconButton>\n * </Tooltip>\n *\n * <Tooltip title=\"Beløpet du betaler hver måned.\" ariaLabel=\"Mer informasjon\" />\n * ```\n *\n * `openOn` switches either one: `openOn=\"click\"` around a button makes it a\n * disclosure, and `openOn=\"hover\"` makes the info button a plain hint. On\n * hover the trigger must work without the tooltip, since touch never shows it.\n */\nexport function Tooltip({\n title,\n openOn,\n placement = \"top\",\n surfaceVariant = \"light\",\n children,\n ariaLabel,\n triggerIcon,\n triggerVariant = \"icon\",\n className,\n style,\n}: TooltipProps) {\n const [open, setOpen] = useState(false);\n const generatedId = useId();\n const popupId = useId();\n\n const builtInTrigger = (\n <button\n type=\"button\"\n aria-label={ariaLabel}\n className={[styles.Trigger, className].filter(Boolean).join(\" \")}\n style={style}\n data-variant={triggerVariant}\n >\n {triggerIcon ?? <span aria-hidden className={styles.Mark} />}\n </button>\n );\n\n const trigger = children ?? builtInTrigger;\n // The negative margin of the built-in trigger needs a box of its own to pull\n // against, so it takes the room of the icon wherever it is placed.\n const wrap = (node: ReactNode) =>\n children ? node : <span className={styles.TriggerBox}>{node}</span>;\n\n if (isEmpty(title)) return wrap(trigger);\n\n const resolvedOpenOn = openOn ?? (children ? \"hover\" : \"click\");\n\n if (resolvedOpenOn === \"click\") {\n const triggerId = children?.props.id ?? generatedId;\n\n // Tab past the end of the bubble closes it, which puts focus back on the\n // trigger. Left to Base UI 1.6, focus is passed between its focus guards\n // without end when nothing on the page can take focus after the trigger.\n const closeOnTabOut = (event: KeyboardEvent<HTMLDivElement>) => {\n if (event.key !== \"Tab\" || event.shiftKey) return;\n const tabbable = event.currentTarget.querySelectorAll(TABBABLE);\n const last = tabbable[tabbable.length - 1];\n if (last == null || event.target === last) {\n event.preventDefault();\n setOpen(false);\n }\n };\n\n return (\n <Popover.Root open={open} onOpenChange={setOpen}>\n {wrap(\n <Popover.Trigger\n render={trigger}\n id={triggerId}\n openOnHover\n delay={HOVER_DELAY}\n />,\n )}\n <Popover.Portal>\n <Popover.Positioner\n {...position(placement)}\n className={styles.Positioner}\n >\n <Popover.Popup\n // Named by its trigger: \"Mer informasjon\", then the text.\n aria-labelledby={triggerId}\n onKeyDown={closeOnTabOut}\n className={styles.Popup}\n data-surface={surfaceVariant}\n >\n <Popover.Arrow className={styles.Arrow} />\n <Popover.Description render={<div />}>\n {title}\n </Popover.Description>\n </Popover.Popup>\n </Popover.Positioner>\n </Popover.Portal>\n </Popover.Root>\n );\n }\n\n const describedBy =\n [children?.props[\"aria-describedby\"], open && popupId]\n .filter(Boolean)\n .join(\" \") || undefined;\n\n return (\n <BaseTooltip.Root open={open} onOpenChange={setOpen}>\n {wrap(\n <BaseTooltip.Trigger\n render={trigger}\n delay={HOVER_DELAY}\n // Base UI leaves the tooltip out of the accessibility tree; this\n // makes it the description of its trigger while it shows.\n aria-describedby={describedBy}\n />,\n )}\n <BaseTooltip.Portal>\n <BaseTooltip.Positioner\n {...position(placement)}\n className={styles.Positioner}\n >\n <BaseTooltip.Popup\n id={popupId}\n role=\"tooltip\"\n className={styles.Popup}\n data-surface={surfaceVariant}\n >\n <BaseTooltip.Arrow className={styles.Arrow} />\n {title}\n </BaseTooltip.Popup>\n </BaseTooltip.Positioner>\n </BaseTooltip.Portal>\n </BaseTooltip.Root>\n );\n}\n","/* The website's testimonial avatar: a 48px disc, grey until the photo shows. */\n.Avatar {\n display: inline-flex;\n overflow: hidden;\n width: var(--lea-spacing-6);\n height: var(--lea-spacing-6);\n flex-shrink: 0;\n border-radius: var(--lea-radius-full);\n background-color: var(--lea-palette-bg-subtle);\n vertical-align: middle;\n}\n\n.Image {\n display: block;\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n\n.Fallback {\n display: block;\n width: 100%;\n height: 100%;\n}\n","\"use client\";\n\nimport { Avatar as BaseAvatar } from \"@base-ui/react/avatar\";\nimport type { ReactElement } from \"react\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport styles from \"./avatar.module.css\";\n\nexport type AvatarProps = Omit<BaseAvatar.Root.Props, \"children\"> & {\n /**\n * The photo. Without one, while it loads and when it fails, the avatar is a\n * grey disc.\n */\n src?: string;\n /**\n * What the photo shows, for screen readers. Use `\"\"` when the person's name\n * is written beside it, as in a testimonial, so it is not read twice.\n */\n alt: string;\n /**\n * The image element to render instead of an `<img>`, such as `next/image`:\n * `imageRender={<NextImage src={photo} alt=\"\" width={48} height={48} />}`.\n * Pass the photo in `src` as well: the avatar loads it to know when to\n * show the image.\n */\n imageRender?: ReactElement;\n};\n\n/**\n * A round 48px photo of a person, on Base UI's `Avatar`: the photo when it has\n * loaded, a grey disc until then or without one.\n *\n * ```tsx\n * <Avatar src=\"/kunder/kari.jpg\" alt=\"\" />\n * ```\n *\n * `alt` is required. With a name in it, the disc carries the name while\n * there is no photo; with `\"\"`, the avatar is hidden from screen readers.\n */\nexport function Avatar({\n src,\n alt,\n imageRender,\n className,\n ...rest\n}: AvatarProps) {\n return (\n <BaseAvatar.Root\n {...rest}\n className={mergeClassName(styles.Avatar, className)}\n >\n {src != null && (\n <BaseAvatar.Image\n src={src}\n alt={alt}\n render={imageRender}\n className={styles.Image}\n />\n )}\n <BaseAvatar.Fallback\n className={styles.Fallback}\n role={alt ? \"img\" : undefined}\n aria-label={alt || undefined}\n aria-hidden={alt ? undefined : true}\n />\n </BaseAvatar.Root>\n );\n}\n","/*\n * The website's stars: 20 × 19, 2px apart, filled with the text colour, so\n * they follow the surface they sit on.\n */\n.StarRating {\n display: inline-flex;\n align-items: center;\n gap: calc(var(--lea-spacing) * 0.25);\n vertical-align: middle;\n}\n\n.Star {\n display: block;\n width: 1.25rem;\n height: 1.1875rem;\n flex-shrink: 0;\n fill: currentColor;\n}\n","import { createTranslation } from \"../../../i18n\";\n\n/**\n * The accessible name of `StarRating`. `{rating}` and `{max}` are replaced\n * with the numbers.\n */\nexport const useStarRatingLabels = createTranslation({\n label: {\n no: \"{rating} av {max} stjerner\",\n se: \"{rating} av {max} stjärnor\",\n fi: \"{rating}/{max} tähteä\",\n es: \"{rating} de {max} estrellas\",\n },\n});\n","\"use client\";\n\nimport type { ComponentProps } from \"react\";\n\nimport styles from \"./star-rating.module.css\";\nimport { useStarRatingLabels } from \"./use-star-rating-labels\";\n\nexport type StarRatingProps = Omit<ComponentProps<\"span\">, \"children\"> & {\n /**\n * How many stars, from 0 to `max`. Rounded to a whole star and kept within\n * the range.\n */\n rating: number;\n /**\n * The best rating there is, for the accessible name: \"4 av 5 stjerner\".\n * @default 5\n */\n max?: number;\n};\n\n/** The website's star (leabank.no's `star.svg`), drawn 20 × 19. */\nconst STAR_PATH =\n \"M9.07088 0.612344C9.41462 -0.204115 10.5854 -0.204114 10.9291 0.612346L12.9579 5.43123C13.1029 5.77543 13.4306 6.01061 13.8067 6.0404L19.0727 6.45748C19.9649 6.52814 20.3267 7.62813 19.6469 8.2034L15.6348 11.5987C15.3482 11.8412 15.223 12.2218 15.3106 12.5843L16.5363 17.661C16.744 18.5211 15.7969 19.201 15.033 18.7401L10.5245 16.0196C10.2025 15.8252 9.7975 15.8252 9.47548 16.0196L4.96699 18.7401C4.20311 19.201 3.25596 18.5211 3.46363 17.661L4.68942 12.5843C4.77698 12.2218 4.65182 11.8412 4.36526 11.5987L0.353062 8.2034C-0.326718 7.62813 0.0350679 6.52814 0.927291 6.45748L6.19336 6.0404C6.5695 6.01061 6.89716 5.77543 7.04207 5.43123L9.07088 0.612344Z\";\n\n/**\n * A read-only rating in stars, as on the website's testimonials. Only the\n * stars of the rating are drawn (a 4 is four stars), in the colour of the\n * text around it.\n *\n * ```tsx\n * <StarRating rating={4} />\n * ```\n *\n * It is one image to a screen reader, named \"4 av 5 stjerner\" in the\n * `LeaProvider` language; pass `aria-label` for other words.\n */\nexport function StarRating({\n rating,\n max = 5,\n className,\n ...rest\n}: StarRatingProps) {\n const labels = useStarRatingLabels();\n const best = Math.max(0, Math.round(max));\n const stars = Math.min(Math.max(0, Math.round(rating)), best);\n\n return (\n <span\n role=\"img\"\n aria-label={labels.label\n .replace(\"{rating}\", String(stars))\n .replace(\"{max}\", String(best))}\n {...rest}\n className={[styles.StarRating, className].filter(Boolean).join(\" \")}\n >\n {Array.from({ length: stars }, (_, index) => (\n <svg\n key={index}\n className={styles.Star}\n viewBox=\"0 0 20 19\"\n aria-hidden\n focusable=\"false\"\n >\n <path d={STAR_PATH} />\n </svg>\n ))}\n </span>\n );\n}\n","/*\n * The website's price table: a 1px line under every row (the website's grey,\n * `neutral-600`), no stripes, no column lines. `lg` is 16px text with 16px\n * around it, so a one-line row is 52px and its line; `sm` is 14px text with\n * 8px around it. Without `size` the table is `lg` and turns `sm` below\n * 640px, where the website's does.\n */\n/*\n * The box around the table: it scrolls sideways when the table is wider than\n * its column. While it does, it can take focus (see table.tsx).\n */\n.Scroll {\n overflow-x: auto;\n max-width: 100%;\n}\n\n.Scroll:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n.Table {\n --table-padding: var(--lea-spacing-2);\n --table-font-size: var(--lea-font-step-0);\n\n width: 100%;\n border-collapse: collapse;\n border-spacing: 0;\n color: var(--lea-palette-text-base);\n}\n\n.Table[data-size=\"sm\"] {\n --table-padding: var(--lea-spacing-1);\n --table-font-size: var(--lea-font-step-minus-1);\n}\n\n@media (max-width: 639.95px) {\n .Table:not([data-size]) {\n --table-padding: var(--lea-spacing-1);\n --table-font-size: var(--lea-font-step-minus-1);\n }\n}\n\n.Row {\n border-bottom: 1px solid var(--lea-color-neutral-600);\n}\n\n/*\n * The text of a cell: the size of the table, with the website's \"normal\"\n * line height, which the tight line height matches (16px text in a 20px\n * line). Long words break rather than push the table wider than its column.\n */\n.HeaderCell,\n.Cell {\n padding: var(--table-padding);\n font-size: var(--table-font-size);\n line-height: var(--lea-line-height-tight);\n letter-spacing: 0;\n text-align: center;\n vertical-align: middle;\n overflow-wrap: break-word;\n}\n\n.HeaderCell {\n font-weight: var(--lea-font-weight-semibold);\n}\n\n.Cell {\n font-weight: var(--lea-font-weight-regular);\n}\n\n/* The first column at the start, the last at the end, the rest centred. */\n.HeaderCell:first-child,\n.Cell:first-child {\n text-align: start;\n}\n\n.HeaderCell:last-child,\n.Cell:last-child {\n text-align: end;\n}\n\n/* `align` on a cell wins over its place in the row. */\n.HeaderCell[data-align=\"start\"],\n.Cell[data-align=\"start\"] {\n text-align: start;\n}\n\n.HeaderCell[data-align=\"center\"],\n.Cell[data-align=\"center\"] {\n text-align: center;\n}\n\n.HeaderCell[data-align=\"end\"],\n.Cell[data-align=\"end\"] {\n text-align: end;\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport {\n createContext,\n useContext,\n useEffect,\n useRef,\n useState,\n type Ref,\n} from \"react\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport styles from \"./table.module.css\";\n\nexport type TableSize = \"sm\" | \"lg\";\nexport type TableCellAlign = \"start\" | \"center\" | \"end\";\n\n/** Which part of the table a cell is in, for the `scope` of a header cell. */\nconst TableSectionContext = createContext<\"head\" | \"body\" | undefined>(\n undefined,\n);\n\nexport type TableProps = Omit<useRender.ComponentProps<\"table\">, \"ref\"> & {\n /** The rendered element: the `<table>`, or the element passed to `render`. */\n ref?: Ref<HTMLTableElement>;\n /**\n * `\"lg\"`: 16px text and 16px around it, rows 53px tall with their line.\n * `\"sm\"`: 14px text and 8px around it, for a narrow column. Left out, the\n * table is `lg` and turns `sm` below 640px, as on the website.\n */\n size?: TableSize;\n};\n\n/**\n * A plain data table, as the website's price tables: a 1px line under every\n * row, no stripes and no column lines. The first column is aligned to the\n * start, the last to the end and the ones between to the centre; text wraps\n * inside its cell.\n *\n * ```tsx\n * <Table aria-label=\"Priseksempler\">\n * <TableHead>\n * <TableRow>\n * <TableHeaderCell>Beløp</TableHeaderCell>\n * <TableHeaderCell>Pr. måned</TableHeaderCell>\n * </TableRow>\n * </TableHead>\n * <TableBody>\n * <TableRow>\n * <TableCell>50 000 kr</TableCell>\n * <TableCell>1 060 kr</TableCell>\n * </TableRow>\n * </TableBody>\n * </Table>\n * ```\n *\n * The header row is optional: the live price tables have none. The parts\n * render the native table elements, so they nest only as a table's do. Name\n * the table with `aria-label`, or `aria-labelledby` pointing at the heading\n * above it.\n *\n * The table sits in a box of its own that scrolls sideways when the table is\n * wider than its column, as on the website. While it scrolls, the box can be\n * focused, so it scrolls from the keyboard too, and carries the table's name.\n * `className`, `style` and `ref` are the table's.\n */\nexport function Table({ render, ref, className, size, ...rest }: TableProps) {\n const scrollRef = useRef<HTMLDivElement>(null);\n const [scrollable, setScrollable] = useState(false);\n\n useEffect(() => {\n const box = scrollRef.current;\n if (!box || typeof ResizeObserver === \"undefined\") return;\n\n const measure = () => setScrollable(box.scrollWidth > box.clientWidth);\n const observer = new ResizeObserver(measure);\n\n observer.observe(box);\n if (box.firstElementChild) observer.observe(box.firstElementChild);\n measure();\n\n return () => observer.disconnect();\n }, []);\n\n const table = useRender({\n render,\n ref,\n defaultTagName: \"table\",\n props: {\n ...rest,\n \"data-size\": size,\n className: mergeClassName(styles.Table, className)({}),\n },\n });\n\n return (\n <div\n ref={scrollRef}\n className={styles.Scroll}\n {...(scrollable && {\n role: \"region\",\n tabIndex: 0,\n \"aria-label\": rest[\"aria-label\"],\n \"aria-labelledby\": rest[\"aria-labelledby\"],\n })}\n >\n {table}\n </div>\n );\n}\n\nexport type TableHeadProps = Omit<\n useRender.ComponentProps<\"thead\">,\n \"ref\"\n> & {\n /** The rendered element: the `<thead>`, or the element passed to `render`. */\n ref?: Ref<HTMLTableSectionElement>;\n};\n\n/** The header row group, `<thead>`. Leave it out for a table without one. */\nexport function TableHead({ render, ref, ...rest }: TableHeadProps) {\n const element = useRender({\n render,\n ref,\n defaultTagName: \"thead\",\n props: rest,\n });\n\n return (\n <TableSectionContext.Provider value=\"head\">\n {element}\n </TableSectionContext.Provider>\n );\n}\n\nexport type TableBodyProps = Omit<\n useRender.ComponentProps<\"tbody\">,\n \"ref\"\n> & {\n /** The rendered element: the `<tbody>`, or the element passed to `render`. */\n ref?: Ref<HTMLTableSectionElement>;\n};\n\n/** The rows of data, `<tbody>`. */\nexport function TableBody({ render, ref, ...rest }: TableBodyProps) {\n const element = useRender({\n render,\n ref,\n defaultTagName: \"tbody\",\n props: rest,\n });\n\n return (\n <TableSectionContext.Provider value=\"body\">\n {element}\n </TableSectionContext.Provider>\n );\n}\n\nexport type TableRowProps = Omit<useRender.ComponentProps<\"tr\">, \"ref\"> & {\n /** The rendered element: the `<tr>`, or the element passed to `render`. */\n ref?: Ref<HTMLTableRowElement>;\n};\n\n/** A row, `<tr>`, with the 1px line under it. */\nexport function TableRow({ render, ref, className, ...rest }: TableRowProps) {\n return useRender({\n render,\n ref,\n defaultTagName: \"tr\",\n props: {\n ...rest,\n className: mergeClassName(styles.Row, className)({}),\n },\n });\n}\n\ntype CellAlignProps = {\n /**\n * Where the text sits in the cell. Left out, the first cell of a row is at\n * the start, the last at the end and the others in the centre. (It stands\n * in for HTML's obsolete `align` attribute, and is written as\n * `data-align`.)\n */\n align?: TableCellAlign;\n};\n\nexport type TableHeaderCellProps = Omit<\n useRender.ComponentProps<\"th\">,\n \"ref\" | \"align\"\n> &\n CellAlignProps & {\n /** The rendered element: the `<th>`, or the element passed to `render`. */\n ref?: Ref<HTMLTableCellElement>;\n };\n\n/**\n * A header cell, `<th>`, in semibold. In `TableHead` it heads its column\n * (`scope=\"col\"`). At the start of a row in `TableBody` it heads the row\n * (`scope=\"row\"`), so a screen reader reads the label with each value of a\n * label / value table.\n */\nexport function TableHeaderCell({\n render,\n ref,\n className,\n align,\n scope,\n ...rest\n}: TableHeaderCellProps) {\n const section = useContext(TableSectionContext);\n\n return useRender({\n render,\n ref,\n defaultTagName: \"th\",\n props: {\n scope: scope ?? (section === \"body\" ? \"row\" : \"col\"),\n ...rest,\n \"data-align\": align,\n className: mergeClassName(styles.HeaderCell, className)({}),\n },\n });\n}\n\nexport type TableCellProps = Omit<\n useRender.ComponentProps<\"td\">,\n \"ref\" | \"align\"\n> &\n CellAlignProps & {\n /** The rendered element: the `<td>`, or the element passed to `render`. */\n ref?: Ref<HTMLTableCellElement>;\n };\n\n/** A data cell, `<td>`. */\nexport function TableCell({\n render,\n ref,\n className,\n align,\n ...rest\n}: TableCellProps) {\n return useRender({\n render,\n ref,\n defaultTagName: \"td\",\n props: {\n ...rest,\n \"data-align\": align,\n className: mergeClassName(styles.Cell, className)({}),\n },\n });\n}\n","/*\n * Text from the CMS, as the website's FreeBlock styles it, on Paloma's text\n * styles: headings h1 to h4 on `display-md`, `heading-xl`, `heading-md` and\n * `heading-sm`, paragraphs and lists on `body-lg` (the website's 18px), 16px\n * after each block, more around quotes.\n *\n * Every rule is for a bare element (`:not([class])`), the way the CMS writes\n * it, so a Paloma component placed inside (a `Text`, a `Link`, an `Alert`)\n * keeps its own look.\n */\n.RichText {\n color: var(--lea-palette-text-base);\n font-size: var(--lea-text-body-lg-font-size);\n font-weight: var(--lea-text-body-lg-font-weight);\n line-height: var(--lea-text-body-lg-line-height);\n letter-spacing: var(--lea-text-body-lg-letter-spacing);\n overflow-wrap: break-word;\n}\n\n/*\n * No space above the first block or below the last, so the text sits flush\n * in a Panel. Specific enough to beat the block rules below.\n */\n.RichText > :first-child:not([class]) {\n margin-top: 0;\n}\n\n.RichText > :last-child:not([class]) {\n margin-bottom: 0;\n}\n\n/* Headings */\n.RichText :is(h1, h2, h3, h4, h5, h6):not([class]) {\n margin: 0 0 var(--lea-spacing-2);\n color: inherit;\n}\n\n.RichText h1:not([class]) {\n font-size: var(--lea-text-display-md-font-size);\n font-weight: var(--lea-text-display-md-font-weight);\n line-height: var(--lea-text-display-md-line-height);\n letter-spacing: var(--lea-text-display-md-letter-spacing);\n}\n\n.RichText h2:not([class]) {\n font-size: var(--lea-text-heading-xl-font-size);\n font-weight: var(--lea-text-heading-xl-font-weight);\n line-height: var(--lea-text-heading-xl-line-height);\n letter-spacing: var(--lea-text-heading-xl-letter-spacing);\n}\n\n.RichText h3:not([class]) {\n font-size: var(--lea-text-heading-md-font-size);\n font-weight: var(--lea-text-heading-md-font-weight);\n line-height: var(--lea-text-heading-md-line-height);\n letter-spacing: var(--lea-text-heading-md-letter-spacing);\n}\n\n.RichText h4:not([class]) {\n font-size: var(--lea-text-heading-sm-font-size);\n font-weight: var(--lea-text-heading-sm-font-weight);\n line-height: var(--lea-text-heading-sm-line-height);\n letter-spacing: var(--lea-text-heading-sm-letter-spacing);\n}\n\n.RichText :is(h5, h6):not([class]) {\n font-size: var(--lea-text-heading-xs-font-size);\n font-weight: var(--lea-text-heading-xs-font-weight);\n line-height: var(--lea-text-heading-xs-line-height);\n letter-spacing: var(--lea-text-heading-xs-letter-spacing);\n}\n\n/*\n * Smaller headings where the website's step down: h1 below 1024px, h2 to h4\n * below 768px. The website takes h1 to 28px below 1024px, under its 40px h2;\n * here h1 steps down twice, so it stays the largest heading at every width.\n */\n@media (max-width: 1023.95px) {\n .RichText h1:not([class]) {\n font-size: var(--lea-text-display-sm-font-size);\n font-weight: var(--lea-text-display-sm-font-weight);\n line-height: var(--lea-text-display-sm-line-height);\n letter-spacing: var(--lea-text-display-sm-letter-spacing);\n }\n}\n\n@media (max-width: 767.95px) {\n .RichText h1:not([class]) {\n font-size: var(--lea-text-heading-lg-font-size);\n font-weight: var(--lea-text-heading-lg-font-weight);\n line-height: var(--lea-text-heading-lg-line-height);\n letter-spacing: var(--lea-text-heading-lg-letter-spacing);\n }\n\n .RichText h2:not([class]) {\n font-size: var(--lea-text-heading-md-font-size);\n font-weight: var(--lea-text-heading-md-font-weight);\n line-height: var(--lea-text-heading-md-line-height);\n letter-spacing: var(--lea-text-heading-md-letter-spacing);\n }\n\n .RichText h3:not([class]) {\n font-size: var(--lea-text-heading-sm-font-size);\n font-weight: var(--lea-text-heading-sm-font-weight);\n line-height: var(--lea-text-heading-sm-line-height);\n letter-spacing: var(--lea-text-heading-sm-letter-spacing);\n }\n\n .RichText h4:not([class]) {\n font-size: var(--lea-text-heading-xs-font-size);\n font-weight: var(--lea-text-heading-xs-font-weight);\n line-height: var(--lea-text-heading-xs-line-height);\n letter-spacing: var(--lea-text-heading-xs-letter-spacing);\n }\n}\n\n/* Paragraphs */\n.RichText p:not([class]) {\n margin: 0 0 var(--lea-spacing-2);\n}\n\n/* Marks. The font has no italic face, so the browser slants the regular. */\n.RichText :is(strong, b):not([class]) {\n font-weight: var(--lea-font-weight-semibold);\n}\n\n.RichText :is(em, i):not([class]) {\n font-style: italic;\n}\n\n.RichText u:not([class]) {\n text-decoration: underline;\n}\n\n.RichText s:not([class]) {\n text-decoration: line-through;\n}\n\n/*\n * Links: purple and underlined, so they stand apart from the words around\n * them by more than colour. Hover thickens the line, as a plain `Link`.\n */\n.RichText a:not([class]) {\n color: var(--lea-palette-text-accent);\n text-decoration: underline;\n text-decoration-thickness: 0.0625rem;\n /* Clears the descenders of the font, as `Link`; there is no token for it. */\n text-underline-offset: 0.3rem;\n}\n\n.RichText a:not([class]):focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n@media (hover: hover) {\n .RichText a:not([class]):hover {\n text-decoration-thickness: 0.125rem;\n }\n}\n\n/* Lists: 16px above and below, 32px indent, 8px between the items. */\n.RichText :is(ul, ol):not([class]) {\n padding-inline-start: var(--lea-spacing-4);\n margin: var(--lea-spacing-2) 0;\n}\n\n.RichText ul:not([class]) {\n list-style-type: disc;\n}\n\n.RichText ol:not([class]) {\n list-style-type: decimal;\n}\n\n.RichText li:not([class]) {\n margin-bottom: var(--lea-spacing-1);\n}\n\n/* A quote: a 2px grey line at the start, 32px above and below. */\n.RichText blockquote:not([class]) {\n padding-inline-start: var(--lea-spacing-2);\n border-inline-start: 2px solid var(--lea-color-neutral-600);\n margin: var(--lea-spacing-4) 0;\n}\n\n/* Code, in the website's Courier New at 0.9em, on grey. */\n.RichText code:not([class]) {\n padding: var(--lea-spacing-0-5) var(--lea-spacing-1);\n border-radius: var(--lea-radius-sm);\n background-color: var(--lea-palette-bg-subtle);\n font-family: var(--lea-font-family-mono);\n font-size: 0.9em;\n}\n\n.RichText pre:not([class]) {\n overflow-x: auto;\n padding: var(--lea-spacing-2);\n border-radius: var(--lea-radius-xl);\n margin: var(--lea-spacing-2) 0;\n background-color: var(--lea-palette-bg-subtle);\n font-family: var(--lea-font-family-mono);\n}\n\n.RichText pre:not([class]) code:not([class]) {\n padding: 0;\n border-radius: 0;\n background-color: transparent;\n}\n\n/* Images fill the width, with the website's 64px corners. */\n.RichText img:not([class]) {\n display: block;\n width: 100%;\n height: auto;\n border-radius: var(--lea-radius-huge);\n margin: var(--lea-spacing-2) 0;\n object-fit: cover;\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport type { Ref } from \"react\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport styles from \"./rich-text.module.css\";\n\nexport type RichTextProps = Omit<useRender.ComponentProps<\"div\">, \"ref\"> & {\n /** The rendered element: the `<div>`, or the element passed to `render`. */\n ref?: Ref<HTMLElement>;\n};\n\n/**\n * Styles text from the CMS: headings, paragraphs, bold, italic, underline\n * and strikethrough, lists, links, quotes, code and images, on Paloma's text\n * styles and the website's spacing. It styles the elements inside it and\n * renders nothing of its own but a `div`.\n *\n * ```tsx\n * <RichText>{renderBlocks(blocks)}</RichText>\n * ```\n *\n * It does not sanitise. Markup from the CMS as React elements (as the\n * website's `renderBlocks` makes them) is safe as it is; an HTML string has\n * to be sanitised first (the website uses DOMPurify) and goes in through\n * `dangerouslySetInnerHTML`.\n *\n * Only bare elements are styled, the way the CMS writes them: an element with\n * a class of its own, such as a Paloma `Text` or `Link`, keeps its own look.\n * Headings step down on narrow screens as on the website; set `text-align`\n * with `className` or `style` to centre the text.\n */\nexport function RichText({ render, ref, className, ...rest }: RichTextProps) {\n return useRender({\n render,\n ref,\n defaultTagName: \"div\",\n props: {\n ...rest,\n className: mergeClassName(styles.RichText, className)({}),\n },\n });\n}\n","/*\n * The website's featured photo (HeroServiceGrid, FlexiblePromoBlock): `huge`\n * corners that crop the photo, no border, no shadow. From 1024px it fills the\n * height of its row (600px in the website's grids); below that it is\n * 1 : 0.7. The action sits 32px in from the bottom right corner.\n */\n.FeaturedImage {\n position: relative;\n overflow: hidden;\n width: 100%;\n border-radius: var(--lea-radius-huge);\n /* Behind the photo while it loads. */\n background-color: var(--lea-palette-bg-muted);\n}\n\n@media (min-width: 1024px) {\n .FeaturedImage {\n height: 100%;\n }\n}\n\n@media (max-width: 1023.95px) {\n .FeaturedImage {\n aspect-ratio: 1 / 0.7;\n }\n}\n\n.Image {\n display: block;\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n\n.Action {\n position: absolute;\n inset-block-end: var(--lea-spacing-4);\n inset-inline-end: var(--lea-spacing-4);\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport type { ComponentProps, ReactElement, ReactNode } from \"react\";\n\nimport styles from \"./featured-image.module.css\";\n\nexport type FeaturedImageProps = Omit<ComponentProps<\"div\">, \"children\"> & {\n /** The photo. */\n src: string;\n /**\n * What the photo shows, for screen readers; `\"\"` when it is decoration\n * and the text beside it says it all.\n */\n alt: string;\n /**\n * The image element to render instead of the plain `<img>`, such as\n * `next/image` with `fill`: `imageRender={<NextImage src={photo} alt=\"\"\n * fill sizes=\"(min-width: 1024px) 50vw, 100vw\" />}`, or an `<img>` with\n * attributes of its own (`loading=\"lazy\"` below the fold, `srcSet`). Its\n * own props win over `src` and `alt`.\n */\n imageRender?: ReactElement;\n /**\n * A call to action on the photo, 32px from its bottom right corner: an\n * `ActionButton`, `size=\"lg\"` on wide screens and `\"sm\"` on phones.\n */\n action?: ReactNode;\n};\n\n/**\n * A large photo with the website's 64px corners, and optionally a call to\n * action on it, as in the website's hero grid and promo block.\n *\n * ```tsx\n * <FeaturedImage\n * src=\"/bilder/familie.jpg\"\n * alt=\"En familie på kjøkkenet\"\n * action={\n * <ActionButton render={<NextLink href=\"/lan\" />}>Se våre lån</ActionButton>\n * }\n * />\n * ```\n *\n * From 1024px it fills the height its row gives it (the photo is cropped to\n * fit); below that it keeps the website's 1 : 0.7. The action comes after\n * the photo in the reading order.\n */\nexport function FeaturedImage({\n src,\n alt,\n imageRender,\n action,\n className,\n ...rest\n}: FeaturedImageProps) {\n const image = useRender({\n render: imageRender,\n defaultTagName: \"img\",\n props: { src, alt, className: styles.Image },\n });\n\n return (\n <div\n {...rest}\n className={[styles.FeaturedImage, className].filter(Boolean).join(\" \")}\n >\n {image}\n {action != null && <div className={styles.Action}>{action}</div>}\n </div>\n );\n}\n","/*\n * The website's video block: a 16 : 9 video with `huge` corners, black until\n * the first frame shows, and the elevated play / pause button centred 24px\n * under it (the website's 64px from the video's edge to the button's, less\n * the 40px button). The button takes its place in the flow, so nothing\n * after the player runs under it.\n */\n.VideoPlayer {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--lea-spacing-3);\n width: 100%;\n}\n\n.Video {\n display: block;\n width: 100%;\n aspect-ratio: 16 / 9;\n border-radius: var(--lea-radius-huge);\n background-color: var(--lea-palette-bg-contrast);\n object-fit: cover;\n}\n","import { createTranslation } from \"../../../i18n\";\n\n/** The names of `VideoPlayer`'s button, for a paused and a playing video. */\nexport const useVideoPlayerLabels = createTranslation({\n play: {\n no: \"Spill av video\",\n se: \"Spela upp video\",\n fi: \"Toista video\",\n es: \"Reproducir vídeo\",\n },\n pause: {\n no: \"Sett video på pause\",\n se: \"Pausa video\",\n fi: \"Keskeytä video\",\n es: \"Pausar vídeo\",\n },\n});\n","\"use client\";\n\nimport { Pause, Play } from \"lucide-react\";\nimport { useEffect, useRef, useState, type ComponentProps } from \"react\";\n\nimport { IconButton } from \"../../buttons/icon-button/icon-button\";\nimport styles from \"./video-player.module.css\";\nimport { useVideoPlayerLabels } from \"./use-video-player-labels\";\n\nconst REDUCED_MOTION = \"(prefers-reduced-motion: reduce)\";\n\nexport type VideoPlayerProps = Omit<ComponentProps<\"div\">, \"children\"> & {\n /** The video file. It plays without sound, so it should need none. */\n src: string;\n /** A still to show before the video starts, and while it is paused. */\n poster?: string;\n};\n\n/**\n * A silent video that plays in a loop, with the website's 64px corners and a\n * play / pause button centred under it, 16 : 9 at the width it is given.\n *\n * ```tsx\n * <VideoPlayer src=\"/video/lea-bank.mp4\" poster=\"/video/lea-bank.jpg\" />\n * ```\n *\n * It starts on its own once the page has loaded, unless the visitor has\n * asked for reduced motion: then it waits for the button. The button is named\n * \"Spill av video\" or \"Sett video på pause\" in the `LeaProvider` language.\n * The video itself is hidden from screen readers, as decoration: anything it\n * tells has to be in the text around it.\n */\nexport function VideoPlayer({\n src,\n poster,\n className,\n ...rest\n}: VideoPlayerProps) {\n const labels = useVideoPlayerLabels();\n const videoRef = useRef<HTMLVideoElement>(null);\n const [playing, setPlaying] = useState(false);\n\n useEffect(() => {\n const video = videoRef.current;\n if (!video) return;\n\n const reducedMotion = window.matchMedia(REDUCED_MOTION);\n\n // Started here rather than with `autoPlay`, so that reduced motion is\n // known before the first frame moves. A browser that blocks it leaves\n // the video paused, with the button to start it.\n if (!reducedMotion.matches) {\n video.muted = true;\n video.play().catch(() => {});\n }\n\n const stopOnReducedMotion = (event: MediaQueryListEvent) => {\n if (event.matches) video.pause();\n };\n\n reducedMotion.addEventListener(\"change\", stopOnReducedMotion);\n\n return () =>\n reducedMotion.removeEventListener(\"change\", stopOnReducedMotion);\n }, []);\n\n const toggle = () => {\n const video = videoRef.current;\n if (!video) return;\n\n if (video.paused) {\n video.play().catch(() => {});\n } else {\n video.pause();\n }\n };\n\n return (\n <div\n {...rest}\n className={[styles.VideoPlayer, className].filter(Boolean).join(\" \")}\n >\n <video\n ref={videoRef}\n className={styles.Video}\n src={src}\n poster={poster}\n muted\n loop\n playsInline\n aria-hidden\n onPlay={() => setPlaying(true)}\n onPause={() => setPlaying(false)}\n />\n <IconButton\n variant=\"elevated\"\n color=\"secondary\"\n aria-label={playing ? labels.pause : labels.play}\n onClick={toggle}\n >\n {playing ? <Pause aria-hidden /> : <Play aria-hidden />}\n </IconButton>\n </div>\n );\n}\n","/*\n * The website's round arrow (Figma `.ArrowCircle`): 48px with a 24px arrow,\n * or 32px with a 20px arrow. The outline takes the colour of the text around\n * it, so it is white on a dark card. It has no hover of its own: the card\n * moves it.\n */\n.ArrowCircle {\n --arrow-circle-size: var(--lea-spacing-6);\n --arrow-circle-arrow: var(--lea-spacing-3);\n\n display: inline-flex;\n width: var(--arrow-circle-size);\n height: var(--arrow-circle-size);\n box-sizing: border-box;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n border-radius: var(--lea-radius-full);\n}\n\n.ArrowCircle[data-size=\"sm\"] {\n --arrow-circle-size: var(--lea-spacing-4);\n --arrow-circle-arrow: var(--lea-spacing-2-5);\n}\n\n.ArrowCircle[data-variant=\"outline\"] {\n border: 2px solid currentColor;\n}\n\n.ArrowCircle[data-variant=\"filled\"] {\n background-color: var(--lea-palette-bg-contrast);\n color: var(--lea-palette-text-contrast);\n}\n\n.Arrow {\n display: block;\n width: var(--arrow-circle-arrow);\n height: var(--arrow-circle-arrow);\n}\n","import { ArrowRight } from \"lucide-react\";\n\nimport styles from \"./arrow-circle.module.css\";\n\nexport type ArrowCircleProps = {\n /**\n * `\"outline\"`: a 2px ring and the arrow in the text colour, as on a\n * `LinkCard`. `\"filled\"`: a near-black disc with a white arrow, the arrow of\n * the website's call-to-action button.\n * @default \"outline\"\n */\n variant?: \"outline\" | \"filled\";\n /**\n * `\"lg\"` is 48px with a 24px arrow, `\"sm\"` 32px with a 20px arrow.\n * @default \"lg\"\n */\n size?: \"sm\" | \"lg\";\n className?: string;\n};\n\n/**\n * The round arrow of the website's cards (Figma `.ArrowCircle`). Decoration:\n * hidden from screen readers, and the card it sits in moves it on hover.\n * Internal: not exported from the package.\n */\nexport function ArrowCircle({\n variant = \"outline\",\n size = \"lg\",\n className,\n}: ArrowCircleProps) {\n return (\n <span\n aria-hidden\n data-variant={variant}\n data-size={size}\n className={[styles.ArrowCircle, className].filter(Boolean).join(\" \")}\n >\n <ArrowRight className={styles.Arrow} />\n </span>\n );\n}\n","/*\n * The website's SimpleCard: `huge` corners, 32px inside, the title at the top\n * and the arrow at the bottom, at least 240px tall. Hover tints the card with\n * the website's own purple (`bg.hover`, or `bg.contrast-hover` on the dark\n * surfaces) and slides the arrow 8px, over `fast`, as on leabank.no.\n */\n.LinkCard {\n --link-card-bg: var(--lea-palette-bg-base);\n --link-card-hover: var(--lea-palette-bg-hover);\n --link-card-text: var(--lea-palette-text-base);\n\n display: flex;\n /* The website's min-height: 240px. */\n min-height: calc(var(--lea-spacing) * 30);\n box-sizing: border-box;\n flex-direction: column;\n justify-content: space-between;\n gap: var(--lea-spacing-4);\n padding: var(--lea-spacing-4);\n border-radius: var(--lea-radius-huge);\n background-color: var(--link-card-bg);\n color: var(--link-card-text);\n text-decoration: none;\n transition: background-color var(--lea-duration-fast)\n var(--lea-easing-standard);\n -webkit-tap-highlight-color: transparent;\n}\n\n.LinkCard[data-surface=\"subtle\"] {\n --link-card-bg: var(--lea-palette-bg-subtle);\n}\n\n.LinkCard[data-surface=\"muted\"] {\n --link-card-bg: var(--lea-palette-bg-muted);\n}\n\n.LinkCard[data-surface=\"contrast\"] {\n --link-card-bg: var(--lea-palette-bg-contrast);\n --link-card-hover: var(--lea-palette-bg-contrast-hover);\n --link-card-text: var(--lea-palette-text-contrast);\n}\n\n.LinkCard[data-surface=\"contrast-accent\"] {\n --link-card-bg: var(--lea-palette-bg-contrast-accent);\n --link-card-hover: var(--lea-palette-bg-contrast-hover);\n --link-card-text: var(--lea-palette-text-contrast);\n}\n\n/*\n * A white ring inside a near-black one, so the focus shows on whichever\n * section the card sits on, dark or light.\n */\n.LinkCard:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n box-shadow: 0 0 0 0.125rem var(--lea-palette-bg-base);\n}\n\n.Title {\n min-width: 0;\n margin: 0;\n font-size: var(--lea-text-title-md-font-size);\n font-weight: var(--lea-text-title-md-font-weight);\n line-height: var(--lea-text-title-md-line-height);\n letter-spacing: var(--lea-text-title-md-letter-spacing);\n overflow-wrap: break-word;\n}\n\n.Arrow {\n align-self: flex-start;\n transition: translate var(--lea-duration-fast) var(--lea-easing-standard);\n}\n\n@media (hover: hover) {\n .LinkCard:hover {\n background-color: var(--link-card-hover);\n }\n\n .LinkCard:hover .Arrow {\n translate: var(--lea-spacing-1) 0;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .LinkCard,\n .Arrow {\n transition-duration: 0.01ms;\n }\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport type { ReactElement, ReactNode, Ref } from \"react\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport type { SurfaceRoles } from \"../../color-roles\";\nimport { ArrowCircle } from \"./arrow-circle\";\nimport styles from \"./link-card.module.css\";\n\nexport type LinkCardSurface = SurfaceRoles<\n \"base\" | \"subtle\" | \"muted\" | \"contrast\" | \"contrast-accent\"\n>;\n\n/** A link card always leads somewhere: an `href`, or a router link. */\ntype LinkCardTarget =\n | {\n /** Where the card leads. */\n href: string;\n render?: never;\n }\n | {\n href?: never;\n /**\n * A router link to render instead of the `<a>`, carrying its own\n * destination: `render={<NextLink href=\"/lan\" />}`.\n */\n render: ReactElement;\n };\n\nexport type LinkCardProps = Omit<\n useRender.ComponentProps<\"a\">,\n \"ref\" | \"title\" | \"children\" | \"href\" | \"render\"\n> &\n LinkCardTarget & {\n /** The rendered element: the `<a>`, or the link passed to `render`. */\n ref?: Ref<HTMLElement>;\n /** The text of the card, which is also the name of the link. */\n title: ReactNode;\n /**\n * The element for the title, a `<p>` by default. Pass a heading where the\n * page outline needs one: `titleRender={<h3 />}`.\n */\n titleRender?: ReactElement;\n /**\n * The colour of the card, after its `bg` token: `\"base\"` (white),\n * `\"subtle\"` (grey), `\"muted\"` (beige), `\"contrast\"` (near-black) or\n * `\"contrast-accent\"` (deep purple). On the last two the text and the\n * arrow are white.\n * @default \"base\"\n */\n surface?: LinkCardSurface;\n };\n\n/**\n * The website's navigation card: a title and a round arrow, and the whole card\n * is one link, as in the grids of links to Lån, Sparing and Forsikring.\n *\n * ```tsx\n * <LinkCard href=\"/lan\" title=\"Lån\" surface=\"muted\" />\n * <LinkCard render={<NextLink href=\"/sparing\" />} title=\"Sparing\" surface=\"contrast\" />\n * ```\n *\n * On hover the card takes the website's purple tint (a dark one on the dark\n * surfaces) and the arrow slides to the right. It fills the cell its grid\n * gives it, at least 240px tall, with the title at the top and the arrow at\n * the bottom. The link is named by the title; the arrow is decoration.\n */\nexport function LinkCard({\n title,\n titleRender,\n surface = \"base\",\n render,\n ref,\n className,\n ...rest\n}: LinkCardProps) {\n const titleElement = useRender({\n render: titleRender,\n defaultTagName: \"p\",\n props: { className: styles.Title, children: title },\n });\n\n return useRender({\n render,\n ref,\n defaultTagName: \"a\",\n props: {\n ...rest,\n \"data-surface\": surface,\n className: mergeClassName(styles.LinkCard, className)({}),\n children: (\n <>\n {titleElement}\n <ArrowCircle className={styles.Arrow} />\n </>\n ),\n },\n });\n}\n","/*\n * The website's ui/Card. Horizontal: a coloured card with `huge` corners, two\n * equal columns from 1024px (text 64px inside, centred on a 480px minimum;\n * the photo inside 64px with `5xl` corners, or edge to edge as a background),\n * one column below with 32px inside. Vertical: no colour of its own, the\n * photo at 1 : 0.6 and centred text 32px from it. Text, the price example and\n * the photo's frame follow the surface, so they are white on the dark cards.\n */\n.MediaCard {\n --media-card-bg: var(--lea-palette-bg-base);\n --media-card-text: var(--lea-palette-text-base);\n\n display: grid;\n box-sizing: border-box;\n grid-template-columns: minmax(0, 1fr);\n overflow: hidden;\n border-radius: var(--lea-radius-huge);\n background-color: var(--media-card-bg);\n color: var(--media-card-text);\n text-decoration: none;\n -webkit-tap-highlight-color: transparent;\n}\n\n.MediaCard[data-surface=\"subtle\"] {\n --media-card-bg: var(--lea-palette-bg-subtle);\n}\n\n.MediaCard[data-surface=\"muted\"] {\n --media-card-bg: var(--lea-palette-bg-muted);\n}\n\n.MediaCard[data-surface=\"contrast\"] {\n --media-card-bg: var(--lea-palette-bg-contrast);\n --media-card-text: var(--lea-palette-text-contrast);\n}\n\n.MediaCard[data-surface=\"contrast-accent\"] {\n --media-card-bg: var(--lea-palette-bg-contrast-accent);\n --media-card-text: var(--lea-palette-text-contrast);\n}\n\n/* The whole card as a link: a white ring inside a near-black one, so the\n focus shows on a dark section as on a light one. */\n.MediaCard[data-link]:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n box-shadow: 0 0 0 0.125rem var(--lea-palette-bg-base);\n}\n\n.Content {\n display: flex;\n min-width: 0;\n box-sizing: border-box;\n flex-direction: column;\n justify-content: center;\n gap: var(--lea-spacing-4);\n padding: var(--lea-spacing-8);\n}\n\n.Text {\n display: flex;\n flex-direction: column;\n gap: var(--lea-spacing-2);\n}\n\n.Title {\n margin: 0;\n font-size: var(--lea-text-heading-md-font-size);\n font-weight: var(--lea-text-heading-md-font-weight);\n line-height: var(--lea-text-heading-md-line-height);\n letter-spacing: var(--lea-text-heading-md-letter-spacing);\n overflow-wrap: break-word;\n}\n\n.Description {\n margin: 0;\n font-size: var(--lea-text-body-lg-font-size);\n font-weight: var(--lea-text-body-lg-font-weight);\n line-height: var(--lea-text-body-lg-line-height);\n letter-spacing: var(--lea-text-body-lg-letter-spacing);\n}\n\n.Action {\n display: flex;\n flex-wrap: wrap;\n}\n\n.Price {\n margin: 0;\n font-size: var(--lea-text-body-sm-font-size);\n font-weight: var(--lea-text-body-sm-font-weight);\n line-height: var(--lea-text-body-sm-line-height);\n letter-spacing: var(--lea-text-body-sm-letter-spacing);\n}\n\n/* A flex row, so the photo stretches to the height of its column from 1024px\n (528 × 352 in a 480px card, as on leabank.no) and keeps 1 : 0.6 below. */\n.Media {\n display: flex;\n min-width: 0;\n box-sizing: border-box;\n padding: var(--lea-spacing-4);\n}\n\n.MediaCard[data-image-position=\"start\"] .Media {\n order: -1;\n}\n\n.Image {\n display: block;\n width: 100%;\n aspect-ratio: 1 / 0.6;\n border-radius: var(--lea-radius-5xl);\n object-fit: cover;\n}\n\n.MediaCard[data-image-as-background] .Media {\n padding: 0;\n}\n\n.MediaCard[data-image-as-background] .Image {\n border-radius: 0;\n}\n\n@media (min-width: 1024px) {\n .MediaCard[data-orientation=\"horizontal\"][data-has-image] {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n }\n\n .MediaCard[data-orientation=\"horizontal\"] .Content {\n /* The website's min-height: 480px. */\n min-height: calc(var(--lea-spacing) * 60);\n }\n\n .MediaCard[data-orientation=\"horizontal\"] .Title {\n font-size: var(--lea-text-heading-xl-font-size);\n font-weight: var(--lea-text-heading-xl-font-weight);\n line-height: var(--lea-text-heading-xl-line-height);\n letter-spacing: var(--lea-text-heading-xl-letter-spacing);\n }\n\n .MediaCard[data-orientation=\"horizontal\"]:not([data-image-as-background])\n .Media {\n padding: var(--lea-spacing-8);\n }\n}\n\n@media (max-width: 767.95px) {\n .MediaCard[data-orientation=\"horizontal\"] .Content {\n padding: var(--lea-spacing-4);\n }\n\n .MediaCard[data-orientation=\"horizontal\"] .Description {\n font-size: var(--lea-text-body-md-font-size);\n font-weight: var(--lea-text-body-md-font-weight);\n line-height: var(--lea-text-body-md-line-height);\n letter-spacing: var(--lea-text-body-md-letter-spacing);\n }\n}\n\n/*\n * Vertical: a photo and centred text, transparent on its section and in the\n * section's text colour. As wide as its column, at most the website's 650px.\n */\n.MediaCard[data-orientation=\"vertical\"] {\n display: flex;\n width: 100%;\n max-width: calc(var(--lea-spacing) * 81.25);\n flex-direction: column;\n gap: var(--lea-spacing-4);\n margin-inline: auto;\n overflow: visible;\n /* Not drawn, as there is no fill: it rounds the focus ring like the photo. */\n border-radius: var(--lea-radius-5xl);\n background-color: transparent;\n color: inherit;\n}\n\n.MediaCard[data-orientation=\"vertical\"] .Content {\n align-items: center;\n gap: var(--lea-spacing-2);\n padding: 0;\n text-align: center;\n}\n\n.MediaCard[data-orientation=\"vertical\"] .Action {\n justify-content: center;\n}\n\n.MediaCard[data-orientation=\"vertical\"] .Media {\n padding: 0;\n}\n\n.MediaCard[data-orientation=\"vertical\"] .Description {\n font-size: var(--lea-text-body-md-font-size);\n font-weight: var(--lea-text-body-md-font-weight);\n line-height: var(--lea-text-body-md-line-height);\n letter-spacing: var(--lea-text-body-md-letter-spacing);\n}\n\n/* A vertical link card has no fill of its own: the ring sits 8px out, in the\n section's text colour, which shows on the section by definition. */\n.MediaCard[data-orientation=\"vertical\"][data-link]:focus-visible {\n outline-color: currentColor;\n outline-offset: var(--lea-spacing-1);\n box-shadow: none;\n}\n","import { isValidElement, type ReactElement } from \"react\";\n\n/**\n * A `render` element carrying `href` (an `<a>`, Next.js `Link`) or `to`\n * (React Router `Link`) makes a website card one link, as `ButtonRoot` reads\n * a link. Internal: not exported from the package.\n */\nexport const isLinkElement = (\n render: unknown,\n): render is ReactElement<{ href?: unknown; to?: unknown }> =>\n isValidElement<{ href?: unknown; to?: unknown }>(render) &&\n (render.props.href != null || render.props.to != null);\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { useId, type ReactElement, type ReactNode, type Ref } from \"react\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport type { SurfaceRoles } from \"../../color-roles\";\nimport styles from \"./media-card.module.css\";\nimport { isLinkElement } from \"../website-card\";\n\nexport type MediaCardSurface = SurfaceRoles<\n \"base\" | \"subtle\" | \"muted\" | \"contrast\" | \"contrast-accent\"\n>;\nexport type MediaCardOrientation = \"horizontal\" | \"vertical\";\nexport type MediaCardImagePosition = \"start\" | \"end\";\n\n/** `alt` is required with a photo, `\"\"` when it is decoration. */\ntype MediaCardImage =\n | {\n /** The photo. Without one, the card is text only. */\n src: string;\n /**\n * What the photo shows, for screen readers; `\"\"` when it is decoration\n * and the text says it all.\n */\n alt: string;\n /**\n * The image element to render instead of the plain `<img>`, such as\n * `next/image` with `fill`, or an `<img loading=\"lazy\">`. Its own props\n * win over `src` and `alt`.\n */\n imageRender?: ReactElement;\n }\n | { src?: undefined; alt?: undefined; imageRender?: undefined };\n\nexport type MediaCardProps = Omit<\n useRender.ComponentProps<\"div\">,\n \"ref\" | \"title\" | \"children\"\n> &\n MediaCardImage & {\n /**\n * The rendered element: the `<div>`, or the element passed to `render`.\n * A `render` element with `href` or `to` makes the whole card one link,\n * named by its title: `render={<NextLink href=\"/lan\" />}`.\n */\n ref?: Ref<HTMLElement>;\n /** The title of the card. */\n title: ReactNode;\n /**\n * The element for the title, an `<h2>` by default:\n * `titleRender={<h3 />}`.\n */\n titleRender?: ReactElement;\n /** The text under the title. */\n text?: ReactNode;\n /**\n * `\"horizontal\"`: text and photo side by side in a coloured card, one\n * above the other below 1024px. `\"vertical\"`: no colour of its own, the\n * photo above or below centred text, for a row of cards.\n * @default \"horizontal\"\n */\n orientation?: MediaCardOrientation;\n /**\n * Where the photo sits: `\"start\"` before the text (on the left, or on\n * top), `\"end\"` after it. Left out, it is after the text on a horizontal\n * card and above it on a vertical one, as on the website.\n */\n imagePosition?: MediaCardImagePosition;\n /**\n * Horizontal cards only: the photo fills its half of the card edge to\n * edge, instead of sitting inside it with round corners.\n * @default false\n */\n imageAsBackground?: boolean;\n /**\n * Horizontal cards only: the colour of the card, after its `bg` token.\n * On `\"contrast\"` and `\"contrast-accent\"` the text is white; give a\n * `Link` in `action` `color=\"contrast\"` there.\n * @default \"base\"\n */\n surface?: MediaCardSurface;\n /**\n * The call to action under the text: an `ActionButton`, or a `Link`\n * with `arrow`. Left out when the card itself is a link, which cannot\n * hold another.\n */\n action?: ReactNode;\n /** The price example under the action, in the small print. */\n priceExample?: ReactNode;\n };\n\n/**\n * The website's picture-and-text card. Horizontal, it is a coloured card with\n * the text in one half and the photo in the other, as the product cards on\n * leabank.no's home page; vertical, it is a photo with centred text under it,\n * for a row of three.\n *\n * ```tsx\n * <MediaCard\n * surface=\"subtle\"\n * title=\"Forbrukslån\"\n * text=\"Lån fra 10 000 til 500 000 kroner uten sikkerhet.\"\n * src=\"/bilder/familie.jpg\"\n * alt=\"\"\n * action={<ActionButton render={<NextLink href=\"/lan\" />}>Søk om forbrukslån</ActionButton>}\n * priceExample=\"Eff. rente 12,5 %, 150 000 kr o/5 år …\"\n * />\n * ```\n *\n * The horizontal card is two equal columns from 1024px (the photo fills its\n * column), and one column below, where the photo is 1 : 0.6. A `render`\n * element with `href` or `to` makes the whole card one link, named by its\n * title and described by its text; it then has no `action`.\n */\nexport function MediaCard({\n title,\n titleRender,\n text,\n src,\n alt,\n imageRender,\n orientation = \"horizontal\",\n imagePosition,\n imageAsBackground = false,\n surface = \"base\",\n action,\n priceExample,\n render,\n ref,\n className,\n ...rest\n}: MediaCardProps) {\n const id = useId();\n const isLink = isLinkElement(render);\n const titleId = `${id}-title`;\n const textId = `${id}-text`;\n const horizontal = orientation === \"horizontal\";\n\n const titleElement = useRender({\n render: titleRender,\n defaultTagName: \"h2\",\n props: {\n className: styles.Title,\n id: isLink ? titleId : undefined,\n children: title,\n },\n });\n\n const image = useRender({\n enabled: src != null,\n render: imageRender,\n defaultTagName: \"img\",\n props: { src, alt, className: styles.Image },\n });\n\n return useRender({\n render,\n ref,\n defaultTagName: \"div\",\n props: {\n ...(isLink && {\n \"aria-labelledby\": titleId,\n \"aria-describedby\": text != null ? textId : undefined,\n }),\n ...rest,\n \"data-orientation\": orientation,\n \"data-image-position\": imagePosition ?? (horizontal ? \"end\" : \"start\"),\n \"data-image-as-background\":\n (horizontal && imageAsBackground) || undefined,\n \"data-surface\": horizontal ? surface : undefined,\n \"data-has-image\": image != null || undefined,\n \"data-link\": isLink || undefined,\n className: mergeClassName(styles.MediaCard, className)({}),\n children: (\n <>\n <div className={styles.Content}>\n <div className={styles.Text}>\n {titleElement}\n {text != null && (\n <p\n className={styles.Description}\n id={isLink ? textId : undefined}\n >\n {text}\n </p>\n )}\n </div>\n {!isLink && action != null && (\n <div className={styles.Action}>{action}</div>\n )}\n {priceExample != null && (\n <p className={styles.Price}>{priceExample}</p>\n )}\n </div>\n {image != null && <div className={styles.Media}>{image}</div>}\n </>\n ),\n },\n });\n}\n","/*\n * The website's ServiceCardsBlock (Display = Grid): `huge` corners, 32px\n * inside, a photo with `5xl` corners. Stacked, the photo takes the height the\n * text leaves (32px above the text); in a row, it is a 256px square 16px\n * before the text, from 1024px. Text is white on the dark surfaces.\n */\n.ServiceCard {\n --service-card-bg: var(--lea-palette-bg-base);\n --service-card-text: var(--lea-palette-text-base);\n\n display: flex;\n box-sizing: border-box;\n flex-direction: column;\n gap: var(--lea-spacing-4);\n padding: var(--lea-spacing-4);\n overflow: hidden;\n border-radius: var(--lea-radius-huge);\n background-color: var(--service-card-bg);\n color: var(--service-card-text);\n text-decoration: none;\n -webkit-tap-highlight-color: transparent;\n}\n\n.ServiceCard[data-surface=\"subtle\"] {\n --service-card-bg: var(--lea-palette-bg-subtle);\n}\n\n.ServiceCard[data-surface=\"muted\"] {\n --service-card-bg: var(--lea-palette-bg-muted);\n}\n\n.ServiceCard[data-surface=\"contrast\"] {\n --service-card-bg: var(--lea-palette-bg-contrast);\n --service-card-text: var(--lea-palette-text-contrast);\n}\n\n.ServiceCard[data-surface=\"contrast-accent\"] {\n --service-card-bg: var(--lea-palette-bg-contrast-accent);\n --service-card-text: var(--lea-palette-text-contrast);\n}\n\n/* The whole card as a link: a white ring inside a near-black one, so the\n focus shows on a dark section as on a light one. */\n.ServiceCard[data-link]:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n box-shadow: 0 0 0 0.125rem var(--lea-palette-bg-base);\n}\n\n/* Takes the room the text leaves, and shrinks to it in a fixed cell. */\n.Image {\n display: block;\n width: 100%;\n min-height: 0;\n flex: 1 1 auto;\n border-radius: var(--lea-radius-5xl);\n object-fit: cover;\n}\n\n.Content {\n display: flex;\n min-width: 0;\n flex-shrink: 0;\n flex-direction: column;\n gap: var(--lea-spacing-2);\n}\n\n.Icon {\n display: flex;\n width: var(--lea-spacing-5);\n height: var(--lea-spacing-5);\n}\n\n.Icon > * {\n display: block;\n width: 100%;\n height: 100%;\n object-fit: contain;\n}\n\n.Title {\n margin: 0;\n font-size: var(--lea-text-heading-xl-font-size);\n font-weight: var(--lea-text-heading-xl-font-weight);\n line-height: var(--lea-text-heading-xl-line-height);\n letter-spacing: var(--lea-text-heading-xl-letter-spacing);\n overflow-wrap: break-word;\n}\n\n.Text {\n margin: 0;\n font-size: var(--lea-text-body-lg-font-size);\n font-weight: var(--lea-text-body-lg-font-weight);\n line-height: var(--lea-text-body-lg-line-height);\n letter-spacing: var(--lea-text-body-lg-letter-spacing);\n}\n\n@media (min-width: 1024px) {\n .ServiceCard[data-layout=\"row\"] {\n flex-direction: row;\n align-items: center;\n gap: var(--lea-spacing-2);\n }\n\n .ServiceCard[data-layout=\"row\"] .Image {\n /* 256px: the height of the website's 320px row inside its padding. */\n width: calc(var(--lea-spacing) * 32);\n flex: 0 0 auto;\n aspect-ratio: 1;\n }\n\n .ServiceCard[data-layout=\"row\"] .Content {\n flex: 1 1 0;\n }\n}\n\n@media (max-width: 639.95px) {\n .Title {\n font-size: var(--lea-text-heading-md-font-size);\n font-weight: var(--lea-text-heading-md-font-weight);\n line-height: var(--lea-text-heading-md-line-height);\n letter-spacing: var(--lea-text-heading-md-letter-spacing);\n }\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { useId, type ReactElement, type ReactNode, type Ref } from \"react\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport type { SurfaceRoles } from \"../../color-roles\";\nimport styles from \"./service-card.module.css\";\nimport { isLinkElement } from \"../website-card\";\n\nexport type ServiceCardSurface = SurfaceRoles<\n \"base\" | \"subtle\" | \"muted\" | \"contrast\" | \"contrast-accent\"\n>;\nexport type ServiceCardLayout = \"stacked\" | \"row\";\n\n/** `alt` is required with a photo, `\"\"` when it is decoration. */\ntype ServiceCardImage =\n | {\n /** The photo. Without one, the card is text only. */\n src: string;\n /**\n * What the photo shows, for screen readers; `\"\"` when it is decoration\n * and the text says it all.\n */\n alt: string;\n /**\n * The image element to render instead of the plain `<img>`, such as\n * `next/image` with `fill`. Its own props win over `src` and `alt`.\n */\n imageRender?: ReactElement;\n }\n | { src?: undefined; alt?: undefined; imageRender?: undefined };\n\nexport type ServiceCardProps = Omit<\n useRender.ComponentProps<\"div\">,\n \"ref\" | \"title\" | \"children\"\n> &\n ServiceCardImage & {\n /**\n * The rendered element: the `<div>`, or the element passed to `render`.\n * A `render` element with `href` or `to` makes the whole card one link,\n * named by its title, as on the website: `render={<NextLink href=\"/lan\" />}`.\n */\n ref?: Ref<HTMLElement>;\n /** The title of the card. */\n title: ReactNode;\n /**\n * The element for the title, an `<h2>` by default:\n * `titleRender={<h3 />}`.\n */\n titleRender?: ReactElement;\n /** The text under the title. */\n text?: ReactNode;\n /**\n * A 40px icon above the title, such as a Lucide icon or an `<img>`.\n * Mark it `aria-hidden`, or give an `<img>` `alt=\"\"`.\n */\n icon?: ReactNode;\n /**\n * `\"stacked\"`: the photo fills the card above the text, for the large\n * and small cells of the website's grid. `\"row\"`: a square photo before\n * the text, for the wide cells (320px tall); stacked below 1024px.\n * @default \"stacked\"\n */\n layout?: ServiceCardLayout;\n /**\n * The colour of the card, after its `bg` token. On `\"contrast\"` and\n * `\"contrast-accent\"` the text is white.\n * @default \"base\"\n */\n surface?: ServiceCardSurface;\n };\n\n/**\n * A card of the website's service grid: a photo, an optional icon, a title and\n * a line of text, in one of the section colours. It fills the grid cell it is\n * given, and the photo takes the room the text leaves.\n *\n * ```tsx\n * <ServiceCard\n * render={<NextLink href=\"/lan/forbrukslan\" />}\n * title=\"Forbrukslån\"\n * text=\"Lån fra 10 000 til 500 000 kroner uten sikkerhet.\"\n * src=\"/bilder/familie.jpg\"\n * alt=\"\"\n * surface=\"muted\"\n * />\n * ```\n *\n * On the website the whole card is a link: pass it in `render`, and the link\n * is named by the title and described by the text.\n */\nexport function ServiceCard({\n title,\n titleRender,\n text,\n icon,\n src,\n alt,\n imageRender,\n layout = \"stacked\",\n surface = \"base\",\n render,\n ref,\n className,\n ...rest\n}: ServiceCardProps) {\n const id = useId();\n const isLink = isLinkElement(render);\n const titleId = `${id}-title`;\n const textId = `${id}-text`;\n\n const titleElement = useRender({\n render: titleRender,\n defaultTagName: \"h2\",\n props: {\n className: styles.Title,\n id: isLink ? titleId : undefined,\n children: title,\n },\n });\n\n const image = useRender({\n enabled: src != null,\n render: imageRender,\n defaultTagName: \"img\",\n props: { src, alt, className: styles.Image },\n });\n\n return useRender({\n render,\n ref,\n defaultTagName: \"div\",\n props: {\n ...(isLink && {\n \"aria-labelledby\": titleId,\n \"aria-describedby\": text != null ? textId : undefined,\n }),\n ...rest,\n \"data-layout\": layout,\n \"data-surface\": surface,\n \"data-link\": isLink || undefined,\n className: mergeClassName(styles.ServiceCard, className)({}),\n children: (\n <>\n {image}\n <div className={styles.Content}>\n {icon != null && <span className={styles.Icon}>{icon}</span>}\n {titleElement}\n {text != null && (\n <p className={styles.Text} id={isLink ? textId : undefined}>\n {text}\n </p>\n )}\n </div>\n </>\n ),\n },\n });\n}\n","/*\n * The website's testimonial card: white, `5xl` corners, 64px inside (32px\n * below 768px). Stars 16px above the quote, the person 32px under it: a 48px\n * avatar, the name in semibold and a line in `text.subtle`, which reads on the\n * card's white (4.6 : 1).\n */\n.TestimonialCard {\n display: flex;\n box-sizing: border-box;\n flex-direction: column;\n gap: var(--lea-spacing-4);\n margin: 0;\n padding: var(--lea-spacing-8);\n border-radius: var(--lea-radius-5xl);\n background-color: var(--lea-palette-bg-base);\n color: var(--lea-palette-text-base);\n}\n\n@media (max-width: 767.95px) {\n .TestimonialCard {\n padding: var(--lea-spacing-4);\n }\n}\n\n.Top {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--lea-spacing-2);\n}\n\n.Quote {\n display: flex;\n margin: 0;\n flex-direction: column;\n gap: var(--lea-spacing-2);\n font-size: var(--lea-text-body-lg-font-size);\n font-weight: var(--lea-text-body-lg-font-weight);\n line-height: var(--lea-text-body-lg-line-height);\n letter-spacing: var(--lea-text-body-lg-letter-spacing);\n}\n\n.Quote > * {\n margin: 0;\n}\n\n/* The website adds the quotation marks around the quote; the copy has none. */\n.Quote > :first-child::before {\n content: \"\\201C\";\n}\n\n.Quote > :last-child::after {\n content: \"\\201D\";\n}\n\n.Author {\n display: flex;\n align-items: center;\n gap: var(--lea-spacing-2);\n}\n\n.Avatar {\n flex-shrink: 0;\n}\n\n.Person {\n display: flex;\n min-width: 0;\n flex-direction: column;\n}\n\n.Name {\n font-size: var(--lea-text-heading-xs-font-size);\n font-weight: var(--lea-text-heading-xs-font-weight);\n line-height: var(--lea-text-heading-xs-line-height);\n letter-spacing: var(--lea-text-heading-xs-letter-spacing);\n}\n\n.Meta {\n color: var(--lea-palette-text-subtle);\n font-size: var(--lea-text-body-sm-font-size);\n font-weight: var(--lea-text-body-sm-font-weight);\n line-height: var(--lea-text-body-sm-line-height);\n letter-spacing: var(--lea-text-body-sm-letter-spacing);\n}\n","\"use client\";\n\nimport type { ComponentProps, ReactElement, ReactNode } from \"react\";\n\nimport { Avatar } from \"../avatar/avatar\";\nimport { StarRating } from \"../star-rating/star-rating\";\nimport styles from \"./testimonial-card.module.css\";\n\nexport type TestimonialCardProps = Omit<\n ComponentProps<\"figure\">,\n \"children\"\n> & {\n /**\n * What the customer said, without quotation marks: the card adds them. A\n * string becomes one paragraph; pass `<p>` elements for several.\n */\n quote: ReactNode;\n /** Who said it. */\n name: ReactNode;\n /** A line under the name, such as the position and the company. */\n meta?: ReactNode;\n /** Stars above the quote, from 0 to 5. Left out, no stars are shown. */\n rating?: number;\n /**\n * A photo of the person. Without one, the avatar is a grey disc. It is\n * decoration: the name is beside it.\n */\n avatarSrc?: string;\n /**\n * The image element for the photo instead of an `<img>`, such as\n * `next/image`, as `Avatar`'s `imageRender`. Pass `avatarSrc` as well.\n */\n avatarImageRender?: ReactElement;\n};\n\n/**\n * A customer review, as in the website's carousel of reviews: stars, the\n * quote and who said it, on a white card with 32px corners.\n *\n * ```tsx\n * <TestimonialCard\n * rating={5}\n * quote=\"Rask og enkel prosess. Jeg fikk svar samme dag.\"\n * name=\"Kari Nordmann\"\n * meta=\"Kunde, Oslo\"\n * avatarSrc=\"/kunder/kari.jpg\"\n * />\n * ```\n *\n * It is a `figure`: the quote is a `blockquote`, and the person a\n * `figcaption`. 64px inside, 32px below 768px. It takes the width it is\n * given and grows with the quote.\n */\nexport function TestimonialCard({\n quote,\n name,\n meta,\n rating,\n avatarSrc,\n avatarImageRender,\n className,\n ...rest\n}: TestimonialCardProps) {\n return (\n <figure\n {...rest}\n className={[styles.TestimonialCard, className].filter(Boolean).join(\" \")}\n >\n <div className={styles.Top}>\n {rating != null && <StarRating rating={rating} />}\n <blockquote className={styles.Quote}>\n {typeof quote === \"string\" || typeof quote === \"number\" ? (\n <p>{quote}</p>\n ) : (\n quote\n )}\n </blockquote>\n </div>\n <figcaption className={styles.Author}>\n <Avatar\n src={avatarSrc}\n alt=\"\"\n imageRender={avatarImageRender}\n className={styles.Avatar}\n />\n <span className={styles.Person}>\n <span className={styles.Name}>{name}</span>\n {meta != null && <span className={styles.Meta}>{meta}</span>}\n </span>\n </figcaption>\n </figure>\n );\n}\n","/*\n * The website's blog list item and career item: a white card that is one\n * link, 32px inside. A post has `huge` corners and its 320 × 240 photo\n * (`5xl`) 32px before the text, stacked below 768px with 16px around the\n * text; a job has `5xl` corners and no photo. The excerpt is `text.subtle`\n * on the white (4.6 : 1), cut at two lines; the facts are `Chip`s, `muted`\n * and `lg`, 8px apart.\n */\n.ArticleCard {\n display: flex;\n box-sizing: border-box;\n align-items: center;\n gap: var(--lea-spacing-4);\n padding: var(--lea-spacing-4);\n overflow: hidden;\n border-radius: var(--lea-radius-huge);\n background-color: var(--lea-palette-bg-base);\n color: var(--lea-palette-text-base);\n text-decoration: none;\n -webkit-tap-highlight-color: transparent;\n}\n\n.ArticleCard[data-variant=\"job\"] {\n flex-direction: column;\n align-items: stretch;\n gap: var(--lea-spacing-2);\n border-radius: var(--lea-radius-5xl);\n}\n\n/* A white ring inside a near-black one, so the focus shows on whichever\n section the card sits on. */\n.ArticleCard:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n box-shadow: 0 0 0 0.125rem var(--lea-palette-bg-base);\n}\n\n.Image {\n display: block;\n /* The website's 320 × 240 photo. */\n width: calc(var(--lea-spacing) * 40);\n height: calc(var(--lea-spacing) * 30);\n flex-shrink: 0;\n border-radius: var(--lea-radius-5xl);\n object-fit: cover;\n}\n\n.Content {\n display: flex;\n min-width: 0;\n flex: 1 1 0;\n flex-direction: column;\n gap: var(--lea-spacing-2);\n}\n\n.Title {\n margin: 0;\n font-size: var(--lea-text-heading-md-font-size);\n font-weight: var(--lea-text-heading-md-font-weight);\n line-height: var(--lea-text-heading-md-line-height);\n letter-spacing: var(--lea-text-heading-md-letter-spacing);\n overflow-wrap: break-word;\n}\n\n.Excerpt {\n display: -webkit-box;\n margin: 0;\n overflow: hidden;\n color: var(--lea-palette-text-subtle);\n font-size: var(--lea-text-body-md-font-size);\n font-weight: var(--lea-text-body-md-font-weight);\n line-height: var(--lea-text-body-md-line-height);\n letter-spacing: var(--lea-text-body-md-letter-spacing);\n -webkit-box-orient: vertical;\n -webkit-line-clamp: 2;\n line-clamp: 2;\n}\n\n.Meta {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--lea-spacing-1);\n}\n\n@media (hover: hover) {\n .ArticleCard:hover .Title {\n text-decoration: underline;\n }\n}\n\n@media (max-width: 767.95px) {\n .ArticleCard[data-variant=\"post\"] {\n flex-direction: column;\n align-items: stretch;\n }\n\n .ArticleCard[data-variant=\"post\"] .Image {\n width: 100%;\n height: auto;\n aspect-ratio: 1 / 0.6;\n }\n\n .ArticleCard[data-variant=\"post\"] .Content {\n padding: var(--lea-spacing-2);\n }\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { useId, type ReactElement, type ReactNode, type Ref } from \"react\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport styles from \"./article-card.module.css\";\nimport { Chip } from \"../chip/chip\";\n\nexport type ArticleCardVariant = \"post\" | \"job\";\n\n/** An article card always leads somewhere: an `href`, or a router link. */\ntype ArticleCardTarget =\n | {\n /** Where the card leads: the article or the job opening. */\n href: string;\n render?: never;\n }\n | {\n href?: never;\n /**\n * A router link to render instead of the `<a>`, carrying its own\n * destination: `render={<NextLink href=\"/okonomiguiden/smalan\" />}`.\n */\n render: ReactElement;\n };\n\n/** `alt` is required with a photo, `\"\"` when it is decoration. */\ntype ArticleCardImage =\n | {\n /** The article's photo. Job openings have none. */\n src: string;\n /**\n * What the photo shows, for screen readers; `\"\"` when it is decoration\n * and the title says it all.\n */\n alt: string;\n /**\n * The image element to render instead of the plain `<img>`, such as\n * `next/image` with `fill`, or an `<img loading=\"lazy\">`. Its own props\n * win over `src` and `alt`.\n */\n imageRender?: ReactElement;\n }\n | { src?: undefined; alt?: undefined; imageRender?: undefined };\n\nexport type ArticleCardProps = Omit<\n useRender.ComponentProps<\"a\">,\n \"ref\" | \"title\" | \"children\" | \"href\" | \"render\"\n> &\n ArticleCardTarget &\n ArticleCardImage & {\n /** The rendered element: the `<a>`, or the link passed to `render`. */\n ref?: Ref<HTMLElement>;\n /** The title of the article, which also names the link. */\n title: ReactNode;\n /**\n * The element for the title, an `<h3>` by default, under the list's own\n * heading: `titleRender={<h2 />}`.\n */\n titleRender?: ReactElement;\n /** A short summary under the title, cut off after two lines. */\n excerpt?: ReactNode;\n /**\n * The facts under the excerpt, each shown as a beige pill: the category\n * and the date of a post, or the place, the employment type and the\n * closing date of a job. Pass them formatted.\n */\n meta?: ReactNode[];\n /**\n * `\"post\"`: an article, with its photo beside the text (above it below\n * 768px) and 64px corners. `\"job\"`: a job opening, text only, with 32px\n * corners.\n * @default \"post\"\n */\n variant?: ArticleCardVariant;\n };\n\n/**\n * An item in the website's list of articles or of job openings: a white card\n * that is one link, with the title, a two-line excerpt and the facts as\n * pills. On hover the title is underlined.\n *\n * ```tsx\n * <ul>\n * <li>\n * <ArticleCard\n * render={<NextLink href=\"/okonomiguiden/smalan\" />}\n * title=\"Slik refinansierer du dyre smålån\"\n * excerpt=\"Har du flere smålån og kredittkort? …\"\n * meta={[\"Lån\", \"12. mars 2026\"]}\n * src=\"/bilder/smalan.jpg\"\n * alt=\"\"\n * />\n * </li>\n * </ul>\n * ```\n *\n * The link is named by the title and described by the excerpt and the\n * facts. Put the cards in a list of the page's own; they take the width they\n * are given.\n */\nexport function ArticleCard({\n title,\n titleRender,\n excerpt,\n meta,\n src,\n alt,\n imageRender,\n variant = \"post\",\n render,\n ref,\n className,\n ...rest\n}: ArticleCardProps) {\n const id = useId();\n const titleId = `${id}-title`;\n const excerptId = `${id}-excerpt`;\n const metaId = `${id}-meta`;\n const hasMeta = meta != null && meta.length > 0;\n const describedBy =\n [excerpt != null && excerptId, hasMeta && metaId]\n .filter(Boolean)\n .join(\" \") || undefined;\n\n const titleElement = useRender({\n render: titleRender,\n defaultTagName: \"h3\",\n props: { className: styles.Title, id: titleId, children: title },\n });\n\n const image = useRender({\n enabled: src != null,\n render: imageRender,\n defaultTagName: \"img\",\n props: { src, alt, className: styles.Image },\n });\n\n return useRender({\n render,\n ref,\n defaultTagName: \"a\",\n props: {\n \"aria-labelledby\": titleId,\n \"aria-describedby\": describedBy,\n ...rest,\n \"data-variant\": variant,\n className: mergeClassName(styles.ArticleCard, className)({}),\n children: (\n <>\n {image}\n <div className={styles.Content}>\n {titleElement}\n {excerpt != null && (\n <p className={styles.Excerpt} id={excerptId}>\n {excerpt}\n </p>\n )}\n {hasMeta && (\n <span className={styles.Meta} id={metaId}>\n {meta.map((item, index) => (\n <Chip key={index} label={item} color=\"muted\" size=\"lg\" />\n ))}\n </span>\n )}\n </div>\n </>\n ),\n },\n });\n}\n","/*\n * The website's RowWithDivider: the text and a white logo tile, 64px apart\n * and 64px above and below from 1024px, stacked 16px apart below; a 1px\n * `border.base` line underneath. The tile floats over the section with the\n * website's `shadow-soft`, `huge` corners, 220px tall and at most 360px wide.\n */\n.PartnerRow {\n display: grid;\n box-sizing: border-box;\n grid-template-columns: minmax(0, 1fr);\n align-items: center;\n gap: var(--lea-spacing-2);\n padding-block: var(--lea-spacing-2);\n border-bottom: 1px solid var(--lea-palette-border-base);\n}\n\n@media (min-width: 1024px) {\n .PartnerRow {\n gap: var(--lea-spacing-8);\n padding-block: var(--lea-spacing-8);\n }\n\n .PartnerRow[data-has-logo] {\n /* The website's minmax(240px, 360px) logo column. */\n grid-template-columns:\n minmax(0, 1fr)\n minmax(calc(var(--lea-spacing) * 30), calc(var(--lea-spacing) * 45));\n }\n}\n\n.Content {\n min-width: 0;\n}\n\n.Tile {\n display: flex;\n width: 100%;\n max-width: calc(var(--lea-spacing) * 45);\n /* The website's 220px tile. */\n height: calc(var(--lea-spacing) * 27.5);\n box-sizing: border-box;\n align-items: center;\n justify-content: center;\n overflow: hidden;\n border-radius: var(--lea-radius-huge);\n background-color: var(--lea-palette-bg-base);\n box-shadow: var(--lea-shadow-soft);\n}\n\n.Logo {\n display: block;\n max-width: 100%;\n height: 100%;\n object-fit: contain;\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport type { ReactElement, Ref } from \"react\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport styles from \"./partner-row.module.css\";\n\n/** `alt` is required with a logo. */\ntype PartnerRowLogo =\n | {\n /** The partner's logo, shown on a white tile. */\n src: string;\n /**\n * The partner's name, for screen readers; `\"\"` when the text beside\n * the logo already names the partner.\n */\n alt: string;\n /**\n * The image element to render instead of the plain `<img>`, such as\n * `next/image`, or an `<img loading=\"lazy\">`. Its own props win over\n * `src` and `alt`.\n */\n imageRender?: ReactElement;\n }\n | { src?: undefined; alt?: undefined; imageRender?: undefined };\n\nexport type PartnerRowProps = Omit<useRender.ComponentProps<\"div\">, \"ref\"> &\n PartnerRowLogo & {\n /** The rendered element: the `<div>`, or the element passed to `render`. */\n ref?: Ref<HTMLElement>;\n };\n\n/**\n * One partner in the website's list of partners: text about the partner, and\n * the logo on a white tile beside it, over a 1px line.\n *\n * ```tsx\n * <PartnerRow src=\"/logoer/partner.svg\" alt=\"\">\n * <RichText>\n * <h3>Partner AS</h3>\n * <p>Vi samarbeider med …</p>\n * </RichText>\n * </PartnerRow>\n * ```\n *\n * The text is the children: usually `RichText` with the CMS content. Two\n * columns 64px apart from 1024px (the tile is 240 to 360px wide and 220px\n * tall), one column below. It takes the width it is given; the list\n * (`PartnerList`) sets it. Pass `render={<li />}` inside a list.\n */\nexport function PartnerRow({\n src,\n alt,\n imageRender,\n render,\n ref,\n className,\n children,\n ...rest\n}: PartnerRowProps) {\n const logo = useRender({\n enabled: src != null,\n render: imageRender,\n defaultTagName: \"img\",\n props: { src, alt, className: styles.Logo },\n });\n\n return useRender({\n render,\n ref,\n defaultTagName: \"div\",\n props: {\n ...rest,\n \"data-has-logo\": logo != null || undefined,\n className: mergeClassName(styles.PartnerRow, className)({}),\n children: (\n <>\n <div className={styles.Content}>{children}</div>\n {logo != null && <div className={styles.Tile}>{logo}</div>}\n </>\n ),\n },\n });\n}\n","/*\n * The four brand colours. Blue and orange have no semantic token of their\n * own, so they are steps of their scales: a brand stripe is not a status, and\n * must not follow `status.info` or `status.warning` when those change.\n */\n.BrandDivider {\n display: flex;\n width: 100%;\n height: var(--lea-spacing);\n flex-shrink: 0;\n background: linear-gradient(to right,\n var(--lea-color-blue-400) 25%,\n var(--lea-palette-tertiary-base) 25%,\n var(--lea-palette-tertiary-base) 50%,\n var(--lea-color-orange-400) 50%,\n var(--lea-color-orange-400) 75%,\n var(--lea-palette-primary-base) 75%\n );\n}\n\n.BrandDivider[data-size=\"sm\"] {\n height: calc(var(--lea-spacing) * 0.5);\n}\n\n.BrandDivider[data-size=\"md\"] {\n height: calc(var(--lea-spacing) * 1);\n}\n\n.BrandDivider[data-size=\"lg\"] {\n height: calc(var(--lea-spacing) * 1.5);\n}\n\n","import styles from \"./brand-divider.module.css\";\n\n/**\n *\n * Brand Divider in Lea colors\n *\n */\n\nexport type BrandDividerProps = {\n /**\n * The size of the divider.\n */\n size?: \"sm\" | \"md\" | \"lg\";\n};\n\nexport const BrandDivider = ({ size = \"sm\" }: BrandDividerProps) => {\n return (\n <div aria-hidden=\"true\" className={styles.BrandDivider} data-size={size} />\n );\n};\n","/*\n * Shared by Alert and GlobalAlert. Design tokens come from src/theme/tokens.css,\n * so these render styled without a provider.\n *\n * Each colour sets local properties that the variants read, which keeps the\n * variant x colour matrix to one rule per cell.\n */\n\n.Alert {\n --alert-base: var(--lea-palette-status-success-base);\n --alert-subtle: var(--lea-palette-status-success-subtle);\n /* Text and icons on the solid fill of the `filled` variant. */\n --alert-on-base: var(--lea-palette-text-contrast);\n\n /* The height of one line of text, so the icon centres on the first line. */\n --alert-line: calc(\n var(--lea-text-body-md-font-size) * var(--lea-text-body-md-line-height)\n );\n --alert-message-padding: calc(var(--lea-spacing) * 0.7);\n\n box-sizing: border-box;\n padding: var(--lea-spacing) calc(var(--lea-spacing) * 2);\n border-radius: var(--lea-radius-xl);\n color: var(--lea-palette-text-base);\n font-size: var(--lea-text-body-md-font-size);\n font-weight: var(--lea-text-body-md-font-weight);\n line-height: var(--lea-text-body-md-line-height);\n letter-spacing: var(--lea-text-body-md-letter-spacing);\n}\n\n.Layout {\n display: flex;\n align-items: flex-start;\n gap: calc(var(--lea-spacing) * 1.5);\n}\n\n.Icon {\n box-sizing: content-box;\n display: flex;\n height: var(--alert-line);\n flex-shrink: 0;\n align-items: center;\n padding-block: var(--alert-message-padding);\n color: var(--alert-base);\n /* The icon's size: it is drawn at 1em. */\n font-size: var(--lea-font-step-2);\n}\n\n.Message {\n display: flex;\n min-width: 0;\n flex: 1;\n flex-direction: column;\n padding-block: var(--alert-message-padding);\n overflow-wrap: anywhere;\n}\n\n.Title {\n font-weight: var(--lea-font-weight-medium);\n}\n\n.Action {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n margin-inline-start: auto;\n}\n\n/* Colours */\n\n.Alert[data-color=\"info\"] {\n --alert-base: var(--lea-palette-status-info-base);\n --alert-subtle: var(--lea-palette-status-info-subtle);\n}\n\n.Alert[data-color=\"warning\"] {\n --alert-base: var(--lea-palette-status-warning-base);\n --alert-subtle: var(--lea-palette-status-warning-subtle);\n /* White does not read on amber. */\n --alert-on-base: var(--lea-palette-text-base);\n}\n\n.Alert[data-color=\"error\"] {\n --alert-base: var(--lea-palette-status-error-base);\n --alert-subtle: var(--lea-palette-status-error-subtle);\n}\n\n.Alert[data-color=\"primary\"] {\n --alert-base: var(--lea-palette-primary-base);\n --alert-subtle: var(--lea-palette-primary-subtle);\n}\n\n.Alert[data-color=\"neutral\"] {\n --alert-base: var(--lea-color-neutral-700);\n --alert-subtle: var(--lea-color-neutral-100);\n}\n\n/* Variants */\n\n.Alert[data-variant=\"standard\"] {\n background-color: var(--alert-subtle);\n}\n\n.Alert[data-variant=\"filled\"] {\n background-color: var(--alert-base);\n color: var(--alert-on-base);\n}\n\n.Alert[data-variant=\"filled\"] .Icon,\n.Alert[data-variant=\"filled\"] .Close {\n color: var(--alert-on-base);\n}\n\n.Alert[data-variant=\"outlined\"] {\n border: 1px solid var(--alert-base);\n background-color: transparent;\n}\n\n/* Sizes */\n\n.Alert[data-size=\"sm\"] {\n --alert-line: calc(\n var(--lea-text-body-sm-font-size) * var(--lea-text-body-sm-line-height)\n );\n\n padding: calc(var(--lea-spacing) * 0.5) calc(var(--lea-spacing) * 1.5);\n font-size: var(--lea-text-body-sm-font-size);\n font-weight: var(--lea-text-body-sm-font-weight);\n line-height: var(--lea-text-body-sm-line-height);\n letter-spacing: var(--lea-text-body-sm-letter-spacing);\n}\n\n.Alert[data-size=\"sm\"] .Layout,\n.Alert[data-size=\"sm\"].Layout {\n gap: var(--lea-spacing);\n}\n\n.Alert[data-size=\"sm\"] .Action {\n margin-block: calc(var(--lea-spacing) * -0.25);\n}\n\n/*\n * GlobalAlert: edge to edge, with the side padding coming from the inner\n * Container so the content lines up with the page.\n */\n\n.Alert[data-global] {\n width: 100%;\n padding-inline: 0;\n border-radius: 0;\n}\n\n.Alert[data-global][data-variant=\"outlined\"] {\n border-inline: 0;\n}\n\n.Layout[data-fluid] {\n max-width: none;\n}\n","\"use client\";\n\nimport { CircleAlert, CircleCheck, Info, TriangleAlert } from \"lucide-react\";\nimport type {\n ComponentPropsWithRef,\n ReactNode,\n SyntheticEvent,\n} from \"react\";\n\nimport { CloseButton } from \"../buttons/close-button/close-button\";\nimport type { ColorRoles } from \"../color-roles\";\nimport styles from \"./alert.module.css\";\n\nexport type AlertSeverity = ColorRoles<\n \"success\" | \"info\" | \"warning\" | \"error\"\n>;\nexport type AlertColor = AlertSeverity | ColorRoles<\"primary\" | \"neutral\">;\nexport type AlertVariant = \"standard\" | \"filled\" | \"outlined\";\nexport type AlertSize = \"sm\" | \"lg\";\n\nexport type AlertProps = Omit<ComponentPropsWithRef<\"div\">, \"color\"> & {\n /**\n * Semantic meaning — picks the icon and how screen readers announce it.\n * @default \"success\", or \"info\" with a brand `color`\n */\n severity?: AlertSeverity;\n /**\n * Colour scheme. Defaults to the colour of `severity`. `\"primary\"` (the\n * brand purple) and `\"neutral\"` (grey) carry no status meaning.\n */\n color?: AlertColor;\n /**\n * `\"standard\"` is a tinted fill, `\"filled\"` a solid one, `\"outlined\"` a\n * border only.\n * @default \"standard\"\n */\n variant?: AlertVariant;\n /**\n * `\"sm\"` for tighter padding and smaller text.\n * @default \"lg\"\n */\n size?: AlertSize;\n /** Replaces the severity icon. Pass `false` to hide it. */\n icon?: ReactNode | false;\n /** Content for the end of the alert, e.g. a button. Replaces the close button. */\n action?: ReactNode;\n /** Renders a close button that calls this when pressed. */\n onClose?: (event: SyntheticEvent) => void;\n /** Accessible name of the close button. Defaults to a translated \"Close\". */\n closeLabel?: string;\n};\n\nconst brandColours: AlertColor[] = [\"primary\", \"neutral\"];\n\n/** The icon of each severity. Shared with `Toast`. Internal. */\nexport const alertIcons: Record<AlertSeverity, ReactNode> = {\n success: <CircleCheck size=\"1em\" aria-hidden />,\n info: <Info size=\"1em\" aria-hidden />,\n warning: <TriangleAlert size=\"1em\" aria-hidden />,\n error: <CircleAlert size=\"1em\" aria-hidden />,\n};\n\n/**\n * The severity when none is given: `info` with a brand `color`, otherwise\n * `success`. Shared with `Toast`, so the two never disagree. Internal.\n */\nexport const resolveSeverity = (\n severity: AlertSeverity | undefined,\n color: AlertColor | undefined,\n): AlertSeverity =>\n severity ?? (color && brandColours.includes(color) ? \"info\" : \"success\");\n\n/**\n * Resolves the props shared by `Alert` and `GlobalAlert` into the root's\n * attributes and its inner markup. Internal — not exported from the package.\n */\nexport function resolveAlert({\n severity,\n color,\n variant = \"standard\",\n size = \"lg\",\n icon,\n action,\n onClose,\n closeLabel,\n role,\n children,\n ...rest\n}: AlertProps) {\n const resolvedSeverity = resolveSeverity(severity, color);\n\n const rootProps = {\n ...rest,\n role:\n role ??\n (resolvedSeverity === \"error\" || resolvedSeverity === \"warning\"\n ? \"alert\"\n : \"status\"),\n \"data-color\": color ?? resolvedSeverity,\n \"data-variant\": variant,\n \"data-size\": size,\n };\n\n const resolvedIcon = icon === undefined ? alertIcons[resolvedSeverity] : icon;\n const resolvedAction =\n action ??\n (onClose && (\n <CloseButton\n size=\"sm\"\n label={closeLabel}\n onClick={onClose}\n className={styles.Close}\n />\n ));\n\n const content = (\n <>\n {resolvedIcon !== false && resolvedIcon != null && (\n <div className={styles.Icon}>{resolvedIcon}</div>\n )}\n <div className={styles.Message}>{children}</div>\n {resolvedAction && <div className={styles.Action}>{resolvedAction}</div>}\n </>\n );\n\n return { rootProps, content };\n}\n","\"use client\";\n\nimport type { ComponentPropsWithRef } from \"react\";\n\nimport { type AlertProps, resolveAlert } from \"../alert-parts\";\nimport styles from \"../alert.module.css\";\n\nexport type {\n AlertColor,\n AlertProps,\n AlertSeverity,\n AlertSize,\n AlertVariant,\n} from \"../alert-parts\";\n\n/**\n * A message with Lea Bank's status colours. The ARIA `role` is derived from\n * `severity`: `error` / `warning` announce assertively (`role=\"alert\"`) while\n * `info` / `success` announce politely (`role=\"status\"`). Pass an explicit\n * `role` to override.\n *\n * With `onClose`, a close button is rendered with a translated accessible\n * name — override it per-instance with `closeLabel`.\n *\n * ```tsx\n * <Alert color=\"primary\" size=\"sm\">\n * Lånet forutsetter at du løser skulder for totalt 80 000 kr.\n * </Alert>\n * ```\n */\nexport function Alert({ className, ...props }: AlertProps) {\n const { rootProps, content } = resolveAlert(props);\n\n return (\n <div\n {...rootProps}\n className={[styles.Alert, styles.Layout, className]\n .filter(Boolean)\n .join(\" \")}\n >\n {content}\n </div>\n );\n}\n\nexport type AlertTitleProps = ComponentPropsWithRef<\"div\">;\n\n/** A heading above the alert's message. */\nexport function AlertTitle({ className, ...rest }: AlertTitleProps) {\n return (\n <div\n {...rest}\n className={[styles.Title, className].filter(Boolean).join(\" \")}\n />\n );\n}\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n * `--lea-spacing` is the 0.5rem base unit, multiplied to match `spacing(n)`.\n */\n\n.Backdrop {\n position: fixed;\n z-index: 1300;\n background-color: var(--lea-palette-bg-overlay);\n inset: 0;\n opacity: 1;\n transition: opacity var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n.Backdrop[data-starting-style],\n.Backdrop[data-ending-style] {\n opacity: 0;\n}\n\n.Viewport {\n position: fixed;\n z-index: 1300;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: calc(var(--lea-spacing) * 2);\n inset: 0;\n /*\n * The viewport covers the window so the popup can be centred, but only the\n * popup should swallow clicks — presses that land here are outside presses\n * and must dismiss the dialog.\n */\n pointer-events: none;\n}\n\n.Popup {\n display: flex;\n overflow: hidden;\n width: 100%;\n max-height: 100%;\n flex-direction: column;\n border-radius: var(--lea-radius-4xl);\n background-color: var(--lea-palette-bg-base);\n box-shadow: var(--lea-shadow-xl);\n color: var(--lea-palette-text-base);\n opacity: 1;\n pointer-events: auto;\n transform: scale(1);\n transition:\n opacity var(--lea-duration-normal) var(--lea-easing-standard),\n transform var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n.Popup[data-starting-style],\n.Popup[data-ending-style] {\n opacity: 0;\n transform: scale(0.95);\n}\n\n.Popup[data-size=\"xs\"] {\n max-width: 27.75rem;\n}\n\n.Popup[data-size=\"sm\"] {\n max-width: 37.5rem;\n}\n\n.Popup[data-size=\"md\"] {\n max-width: 56.25rem;\n}\n\n.Popup[data-size=\"lg\"] {\n max-width: 75rem;\n}\n\n.Header {\n display: flex;\n flex-shrink: 0;\n align-items: flex-start;\n justify-content: space-between;\n gap: var(--lea-spacing);\n padding: calc(var(--lea-spacing) * 3) calc(var(--lea-spacing) * 3)\n calc(var(--lea-spacing) * 2);\n}\n\n.Title {\n margin: 0;\n color: var(--lea-palette-text-base);\n font-size: var(--lea-text-heading-sm-font-size);\n font-weight: var(--lea-text-heading-sm-font-weight);\n line-height: var(--lea-text-heading-sm-line-height);\n letter-spacing: var(--lea-text-heading-sm-letter-spacing);\n}\n\n.Description {\n margin: 0 0 calc(var(--lea-spacing) * 2);\n color: inherit;\n}\n\n/*\n * Set when the description is the last thing in the body. A `:last-child`\n * selector would not do: body content is often a bare string, and text nodes\n * don't count as children.\n */\n.Description[data-last] {\n margin-bottom: 0;\n}\n\n.Content {\n overflow-y: auto;\n flex: 1;\n padding: 0 calc(var(--lea-spacing) * 3) calc(var(--lea-spacing) * 3);\n color: var(--lea-palette-text-base);\n}\n\n/* The footer's top padding takes over the gap, mirroring the header. */\n.Content:has(+ .Footer) {\n padding-bottom: 0;\n}\n\n.Footer {\n display: flex;\n flex-shrink: 0;\n flex-wrap: wrap;\n align-items: center;\n justify-content: flex-end;\n gap: var(--lea-spacing);\n padding: calc(var(--lea-spacing) * 2) calc(var(--lea-spacing) * 3)\n calc(var(--lea-spacing) * 2);\n background-color: var(--lea-palette-bg-base);\n}\n\n.CloseButton {\n display: inline-flex;\n width: 2.5rem;\n height: 2.5rem;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n padding: 0;\n border: 2px solid var(--lea-palette-border-base);\n border-radius: var(--lea-radius-full);\n margin-inline-end: calc(var(--lea-spacing) * -0.5);\n background-color: transparent;\n color: var(--lea-palette-text-base);\n cursor: pointer;\n transition:\n border-color var(--lea-duration-emphasis) var(--lea-easing-spring),\n background-color var(--lea-duration-emphasis) var(--lea-easing-spring),\n color var(--lea-duration-emphasis) var(--lea-easing-spring);\n}\n\n@media (hover: hover) {\n .CloseButton:hover {\n border-color: var(--lea-palette-primary-base);\n background-color: var(--lea-palette-primary-base);\n color: var(--lea-palette-primary-contrast);\n }\n}\n\n.CloseButton:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n/*\n * Fills the screen rather than floating as a card. `100dvh` follows the mobile\n * browser chrome as it collapses, and the safe-area inset keeps the content\n * clear of the home indicator.\n */\n@media (max-width: 599.95px) {\n .Viewport:has(.Popup[data-full-screen-on-mobile]) {\n padding: 0;\n }\n\n .Popup[data-full-screen-on-mobile] {\n width: 100vw;\n max-width: none;\n height: 100dvh;\n max-height: none;\n border-radius: 0;\n }\n\n .Popup[data-full-screen-on-mobile] .Content {\n padding-bottom: calc(\n var(--lea-spacing) * 3 + env(safe-area-inset-bottom)\n );\n }\n\n .Popup[data-full-screen-on-mobile] .Content:has(+ .Footer) {\n padding-bottom: 0;\n }\n\n .Popup[data-full-screen-on-mobile] .Footer {\n padding-bottom: calc(\n var(--lea-spacing) * 3 + env(safe-area-inset-bottom)\n );\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Backdrop,\n .Popup,\n .CloseButton {\n transition-duration: 0.01ms;\n }\n}\n","\"use client\";\n\nimport { Dialog as BaseDialog } from \"@base-ui/react/dialog\";\nimport { X } from \"lucide-react\";\nimport type { ComponentPropsWithRef } from \"react\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport styles from \"./dialog.module.css\";\nimport { useCloseLabel } from \"../use-close-label\";\n\n/**\n * Maximum width of the popup. The names match the breakpoint scale the library\n * used before, so `size=\"xs\"` is still the compact confirm dialog.\n */\nexport type Size = \"xs\" | \"sm\" | \"md\" | \"lg\";\n\n/**\n * Groups all parts of the dialog and owns the open state. Renders no element\n * of its own. Control it with `open` + `onOpenChange`, or leave both off and\n * let a `Dialog.Trigger` drive it.\n */\nexport const Root = BaseDialog.Root;\nexport type RootProps<Payload = unknown> = BaseDialog.Root.Props<Payload>;\n\n/**\n * Opens the dialog. Renders a bare `<button>`, so pass a styled control\n * through `render` — `<Dialog.Trigger render={<Button />}>Open</Dialog.Trigger>`.\n */\nexport const Trigger = BaseDialog.Trigger;\nexport type TriggerProps<Payload = unknown> = BaseDialog.Trigger.Props<Payload>;\n\n/** Moves the backdrop and popup to the end of `<body>`. */\nexport const Portal = BaseDialog.Portal;\nexport type PortalProps = BaseDialog.Portal.Props;\n\nexport type BackdropProps = BaseDialog.Backdrop.Props;\n\n/** The dimmed overlay behind the popup. */\nexport function Backdrop({ className, ...rest }: BackdropProps) {\n return (\n <BaseDialog.Backdrop\n {...rest}\n className={mergeClassName(styles.Backdrop, className)}\n />\n );\n}\n\nexport type ViewportProps = BaseDialog.Viewport.Props;\n\n/** Centres the popup in the window and keeps a gutter at the screen edges. */\nexport function Viewport({ className, ...rest }: ViewportProps) {\n return (\n <BaseDialog.Viewport\n {...rest}\n className={mergeClassName(styles.Viewport, className)}\n />\n );\n}\n\nexport type PopupProps = BaseDialog.Popup.Props & {\n /**\n * Maximum width of the surface.\n * @default \"sm\"\n */\n size?: Size;\n /**\n * Fills the screen on narrow viewports (below 600px) instead of floating as\n * a card. Use it for flows that need the whole screen, such as a multi-step\n * form or a long consent list.\n */\n fullScreenOnMobile?: boolean;\n};\n\n/**\n * The dialog surface. Lays its children out in a column so a `Dialog.Content`\n * can scroll while the header stays put.\n */\nexport function Popup({\n size = \"sm\",\n fullScreenOnMobile,\n className,\n ...rest\n}: PopupProps) {\n return (\n <BaseDialog.Popup\n {...rest}\n data-size={size}\n data-full-screen-on-mobile={fullScreenOnMobile || undefined}\n className={mergeClassName(styles.Popup, className)}\n />\n );\n}\n\nexport type HeaderProps = ComponentPropsWithRef<\"div\">;\n\n/**\n * The title row. Anything after the `Dialog.Title` sits on the same line,\n * which is where `Dialog.CloseButton` belongs. Stays fixed while\n * `Dialog.Content` scrolls.\n */\nexport function Header({ className, ...rest }: HeaderProps) {\n return (\n <div\n {...rest}\n className={[styles.Header, className].filter(Boolean).join(\" \")}\n />\n );\n}\n\nexport type TitleProps = BaseDialog.Title.Props;\n\n/**\n * The heading. Base UI links it to the popup as the accessible name, so no id\n * wiring is needed.\n */\nexport function Title({ className, ...rest }: TitleProps) {\n return (\n <BaseDialog.Title\n {...rest}\n className={mergeClassName(styles.Title, className)}\n />\n );\n}\n\nexport type DescriptionProps = BaseDialog.Description.Props;\n\n/** Supporting text, linked to the popup as its accessible description. */\nexport function Description({ className, ...rest }: DescriptionProps) {\n return (\n <BaseDialog.Description\n {...rest}\n className={mergeClassName(styles.Description, className)}\n />\n );\n}\n\nexport type ContentProps = ComponentPropsWithRef<\"div\">;\n\n/** The body. Scrolls on its own once the popup runs out of height. */\nexport function Content({ className, ...rest }: ContentProps) {\n return (\n <div\n {...rest}\n className={[styles.Content, className].filter(Boolean).join(\" \")}\n />\n );\n}\n\nexport type FooterProps = ComponentPropsWithRef<\"div\">;\n\n/**\n * The action row, placed after `Dialog.Content`. Stays pinned to the bottom of\n * the popup while `Dialog.Content` scrolls, and lines its children up at the\n * end.\n */\nexport function Footer({ className, ...rest }: FooterProps) {\n return (\n <div\n {...rest}\n className={[styles.Footer, className].filter(Boolean).join(\" \")}\n />\n );\n}\n\n/**\n * Closes the dialog. Renders a bare `<button>` for text actions — use\n * `render` for a styled one: `<Dialog.Close render={<Button />}>Cancel</Dialog.Close>`.\n * For the X in the header corner, use `Dialog.CloseButton`.\n */\nexport const Close = BaseDialog.Close;\nexport type CloseProps = BaseDialog.Close.Props;\n\nexport type CloseButtonProps = Omit<\n BaseDialog.Close.Props,\n \"children\" | \"aria-label\"\n> & {\n /** Accessible name. Defaults to a translated \"Close\". */\n label?: string;\n};\n\n/**\n * The round X in the header corner. It renders its own icon and always has an\n * accessible name, so it cannot be built without one.\n */\nexport function CloseButton({ label, className, ...rest }: CloseButtonProps) {\n const t = useCloseLabel();\n\n return (\n <BaseDialog.Close\n {...rest}\n aria-label={label ?? t.close}\n className={mergeClassName(styles.CloseButton, className)}\n >\n <X size={20} aria-hidden />\n </BaseDialog.Close>\n );\n}\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n * `--lea-spacing` is the 0.5rem base unit, multiplied to match `spacing(n)`.\n *\n * Positioning keys off `data-swipe-direction`, which Base UI sets on the popup\n * from the root's `swipeDirection` — the same value `Drawer.Root` derives from\n * `side`, so the panel can never sit on one edge and swipe towards another.\n */\n\n.Backdrop {\n position: fixed;\n z-index: 1300;\n background-color: var(--lea-palette-bg-overlay);\n inset: 0;\n /* Fades in step with the swipe, so the panel feels attached to the finger. */\n opacity: calc(1 - var(--drawer-swipe-progress, 0));\n transition: opacity var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n.Backdrop[data-starting-style],\n.Backdrop[data-ending-style] {\n opacity: 0;\n}\n\n.Viewport {\n position: fixed;\n z-index: 1300;\n inset: 0;\n /* Only the panel should swallow clicks; presses elsewhere dismiss it. */\n pointer-events: none;\n}\n\n.Popup {\n position: absolute;\n display: flex;\n flex-direction: column;\n background-color: var(--lea-palette-bg-base);\n box-shadow: var(--lea-shadow-xl);\n color: var(--lea-palette-text-base);\n pointer-events: auto;\n transition: transform var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n.Popup[data-swipe-direction=\"left\"],\n.Popup[data-swipe-direction=\"right\"] {\n top: 0;\n width: min(var(--drawer-size, 22.5rem), 100vw);\n height: 100%;\n transform: translateX(var(--drawer-swipe-movement-x, 0px));\n}\n\n.Popup[data-swipe-direction=\"left\"] {\n left: 0;\n}\n\n.Popup[data-swipe-direction=\"right\"] {\n right: 0;\n}\n\n.Popup[data-swipe-direction=\"up\"],\n.Popup[data-swipe-direction=\"down\"] {\n left: 0;\n width: 100%;\n /* Sheets size to their content; `--drawer-size` caps them lower if needed. */\n max-height: var(--drawer-size, 90dvh);\n transform: translateY(\n calc(\n var(--drawer-snap-point-offset, 0px) + var(--drawer-swipe-movement-y, 0px)\n )\n );\n}\n\n/* Sheets round the edge that faces the content, the way a card would. */\n.Popup[data-swipe-direction=\"up\"] {\n top: 0;\n border-radius: 0 0 var(--drawer-radius, var(--lea-radius-4xl))\n var(--drawer-radius, var(--lea-radius-4xl));\n}\n\n.Popup[data-swipe-direction=\"down\"] {\n bottom: 0;\n border-radius: var(--drawer-radius, var(--lea-radius-4xl))\n var(--drawer-radius, var(--lea-radius-4xl)) 0 0;\n padding-bottom: env(safe-area-inset-bottom);\n}\n\n.Popup[data-starting-style][data-swipe-direction=\"left\"],\n.Popup[data-ending-style][data-swipe-direction=\"left\"] {\n transform: translateX(-100%);\n}\n\n.Popup[data-starting-style][data-swipe-direction=\"right\"],\n.Popup[data-ending-style][data-swipe-direction=\"right\"] {\n transform: translateX(100%);\n}\n\n.Popup[data-starting-style][data-swipe-direction=\"up\"],\n.Popup[data-ending-style][data-swipe-direction=\"up\"] {\n transform: translateY(-100%);\n}\n\n.Popup[data-starting-style][data-swipe-direction=\"down\"],\n.Popup[data-ending-style][data-swipe-direction=\"down\"] {\n transform: translateY(100%);\n}\n\n/* A flick should settle faster than a slow drag; a live drag must not lag. */\n.Popup[data-ending-style],\n.Backdrop[data-ending-style] {\n transition-duration: calc(\n var(--drawer-swipe-strength, 1) * var(--lea-duration-normal)\n );\n}\n\n.Popup[data-swiping],\n.Backdrop[data-swiping] {\n transition-duration: 0ms;\n}\n\n.Handle {\n width: calc(var(--lea-spacing) * 5);\n height: calc(var(--lea-spacing) * 0.5);\n flex-shrink: 0;\n border-radius: var(--lea-radius-sm);\n margin: calc(var(--lea-spacing) * 1.5) auto calc(var(--lea-spacing) * 0.5);\n background-color: var(--lea-palette-border-base);\n}\n\n/* A top sheet is dragged upwards, so its handle belongs on the bottom edge. */\n.Popup[data-swipe-direction=\"up\"] .Handle {\n margin: calc(var(--lea-spacing) * 0.5) auto calc(var(--lea-spacing) * 1.5);\n}\n\n.Header {\n display: flex;\n flex-shrink: 0;\n align-items: flex-start;\n justify-content: space-between;\n gap: var(--lea-spacing);\n padding: calc(var(--lea-spacing) * 3) calc(var(--lea-spacing) * 3)\n calc(var(--lea-spacing) * 2);\n}\n\n.Title {\n margin: 0;\n color: var(--lea-palette-text-base);\n font-size: var(--lea-text-heading-sm-font-size);\n font-weight: var(--lea-text-heading-sm-font-weight);\n line-height: var(--lea-text-heading-sm-line-height);\n letter-spacing: var(--lea-text-heading-sm-letter-spacing);\n}\n\n.Description {\n margin: 0 0 calc(var(--lea-spacing) * 2);\n color: inherit;\n}\n\n/*\n * Set when the description is the last thing in the body. A `:last-child`\n * selector would not do: body content is often a bare string, and text nodes\n * don't count as children.\n */\n.Description[data-last] {\n margin-bottom: 0;\n}\n\n.Content {\n overflow-y: auto;\n flex: 1;\n padding: 0 calc(var(--lea-spacing) * 3) calc(var(--lea-spacing) * 3);\n color: var(--lea-palette-text-base);\n}\n\n.CloseButton {\n display: inline-flex;\n width: 2.5rem;\n height: 2.5rem;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n padding: 0;\n border: 2px solid var(--lea-palette-border-base);\n border-radius: var(--lea-radius-full);\n margin-inline-end: calc(var(--lea-spacing) * -0.5);\n background-color: transparent;\n color: var(--lea-palette-text-base);\n cursor: pointer;\n transition: border-color var(--lea-duration-emphasis) var(--lea-easing-spring),\n background-color var(--lea-duration-emphasis) var(--lea-easing-spring),\n color var(--lea-duration-emphasis) var(--lea-easing-spring);\n}\n\n@media (hover: hover) {\n .CloseButton:hover {\n border-color: var(--lea-palette-primary-base);\n background-color: var(--lea-palette-primary-base);\n color: var(--lea-palette-primary-contrast);\n }\n}\n\n.CloseButton:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Backdrop,\n .Popup,\n .CloseButton {\n transition-duration: 0.01ms;\n }\n}\n","\"use client\";\n\nimport { Drawer as BaseDrawer } from \"@base-ui/react/drawer\";\nimport { X } from \"lucide-react\";\nimport type { ComponentPropsWithRef } from \"react\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport styles from \"./drawer.module.css\";\nimport { useCloseLabel } from \"../use-close-label\";\n\n/** Screen edge the panel slides in from. */\nexport type Side = \"left\" | \"right\" | \"top\" | \"bottom\";\n\n/**\n * The panel is dismissed by swiping it back towards the edge it came from, so\n * the gesture direction follows from the side.\n */\nconst SWIPE_DIRECTION = {\n left: \"left\",\n right: \"right\",\n top: \"up\",\n bottom: \"down\",\n} as const;\n\nexport type RootProps<Payload = unknown> = Omit<\n BaseDrawer.Root.Props<Payload>,\n \"swipeDirection\"\n> & {\n /**\n * Screen edge the panel slides in from. Also sets the swipe-to-dismiss\n * direction, so the two can never disagree.\n * @default \"right\"\n */\n side?: Side;\n};\n\n/**\n * Groups all parts of the drawer and owns the open state. Renders no element\n * of its own. Control it with `open` + `onOpenChange`, or leave both off and\n * let a `Drawer.Trigger` drive it.\n */\nexport function Root<Payload = unknown>({\n side = \"right\",\n ...rest\n}: RootProps<Payload>) {\n return <BaseDrawer.Root {...rest} swipeDirection={SWIPE_DIRECTION[side]} />;\n}\n\n/**\n * Opens the drawer. Renders a bare `<button>`, so pass a styled control\n * through `render` — `<Drawer.Trigger render={<Button />}>Open</Drawer.Trigger>`.\n */\nexport const Trigger = BaseDrawer.Trigger;\nexport type TriggerProps<Payload = unknown> = BaseDrawer.Trigger.Props<Payload>;\n\n/** Moves the backdrop and popup to the end of `<body>`. */\nexport const Portal = BaseDrawer.Portal;\nexport type PortalProps = BaseDrawer.Portal.Props;\n\nexport type BackdropProps = BaseDrawer.Backdrop.Props;\n\n/** The dimmed overlay behind the panel. Fades out as the panel is swiped away. */\nexport function Backdrop({ className, ...rest }: BackdropProps) {\n return (\n <BaseDrawer.Backdrop\n {...rest}\n className={mergeClassName(styles.Backdrop, className)}\n />\n );\n}\n\nexport type ViewportProps = BaseDrawer.Viewport.Props;\n\n/**\n * Positioning container for the panel. Also the element Base UI offsets when\n * the on-screen keyboard appears.\n */\nexport function Viewport({ className, ...rest }: ViewportProps) {\n return (\n <BaseDrawer.Viewport\n {...rest}\n className={mergeClassName(styles.Viewport, className)}\n />\n );\n}\n\nexport type PopupProps = BaseDrawer.Popup.Props;\n\n/**\n * The panel surface, pinned to the edge given by `side` on `Drawer.Root`.\n * Side panels are `min(22.5rem, 100vw)` wide and sheets are at most 90% of the\n * screen height; override either with the `--drawer-size` custom property.\n */\nexport function Popup({ className, ...rest }: PopupProps) {\n return (\n <BaseDrawer.Popup\n {...rest}\n className={mergeClassName(styles.Popup, className)}\n />\n );\n}\n\nexport type HandleProps = ComponentPropsWithRef<\"div\">;\n\n/**\n * The grab bar at the top of a bottom sheet. Decorative — the panel is\n * swipe-dismissable anywhere, so this is a hint rather than a control.\n */\nexport function Handle({ className, ...rest }: HandleProps) {\n return (\n <div\n {...rest}\n aria-hidden\n className={[styles.Handle, className].filter(Boolean).join(\" \")}\n />\n );\n}\n\nexport type HeaderProps = ComponentPropsWithRef<\"div\">;\n\n/**\n * The title row. Anything after the `Drawer.Title` sits on the same line,\n * which is where `Drawer.CloseButton` belongs. Stays fixed while\n * `Drawer.Content` scrolls.\n */\nexport function Header({ className, ...rest }: HeaderProps) {\n return (\n <div\n {...rest}\n className={[styles.Header, className].filter(Boolean).join(\" \")}\n />\n );\n}\n\nexport type TitleProps = BaseDrawer.Title.Props;\n\n/**\n * The heading. Base UI links it to the panel as the accessible name, so no id\n * wiring is needed.\n */\nexport function Title({ className, ...rest }: TitleProps) {\n return (\n <BaseDrawer.Title\n {...rest}\n className={mergeClassName(styles.Title, className)}\n />\n );\n}\n\nexport type DescriptionProps = BaseDrawer.Description.Props;\n\n/** Supporting text, linked to the panel as its accessible description. */\nexport function Description({ className, ...rest }: DescriptionProps) {\n return (\n <BaseDrawer.Description\n {...rest}\n className={mergeClassName(styles.Description, className)}\n />\n );\n}\n\nexport type ContentProps = BaseDrawer.Content.Props;\n\n/**\n * The body. Scrolls on its own once the panel runs out of height, and lets a\n * mouse select text inside it without starting a swipe.\n */\nexport function Content({ className, ...rest }: ContentProps) {\n return (\n <BaseDrawer.Content\n {...rest}\n className={mergeClassName(styles.Content, className)}\n />\n );\n}\n\n/**\n * Closes the drawer. Renders a bare `<button>` for text actions — use\n * `render` for a styled one: `<Drawer.Close render={<Button />}>Cancel</Drawer.Close>`.\n * For the X in the header corner, use `Drawer.CloseButton`.\n */\nexport const Close = BaseDrawer.Close;\nexport type CloseProps = BaseDrawer.Close.Props;\n\nexport type CloseButtonProps = Omit<\n BaseDrawer.Close.Props,\n \"children\" | \"aria-label\"\n> & {\n /** Accessible name. Defaults to a translated \"Close\". */\n label?: string;\n};\n\n/**\n * The round X in the header corner. It renders its own icon and always has an\n * accessible name, so it cannot be built without one.\n */\nexport function CloseButton({ label, className, ...rest }: CloseButtonProps) {\n const t = useCloseLabel();\n\n return (\n <BaseDrawer.Close\n {...rest}\n aria-label={label ?? t.close}\n className={mergeClassName(styles.CloseButton, className)}\n >\n <X size={20} aria-hidden />\n </BaseDrawer.Close>\n );\n}\n",".Container {\n box-sizing: border-box;\n width: 100%;\n max-width: var(--lea-breakpoint-lg);\n padding-inline: calc(var(--lea-spacing) * 2);\n margin-inline: auto;\n}\n\n.Container[data-size=\"sm\"] {\n max-width: var(--lea-breakpoint-sm);\n}\n\n.Container[data-size=\"md\"] {\n max-width: var(--lea-breakpoint-md);\n}\n\n.Container[data-size=\"xl\"] {\n max-width: var(--lea-breakpoint-xl);\n}\n\n@media (min-width: 600px) {\n .Container {\n padding-inline: calc(var(--lea-spacing) * 3);\n }\n}\n\n@media (min-width: 900px) {\n .Container {\n padding-inline: calc(var(--lea-spacing) * 4);\n }\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\n\nimport type { Breakpoint } from \"../../../theme/tokens/breakpoints\";\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport styles from \"./container.module.css\";\n\nexport type ContainerSize = Exclude<Breakpoint, \"xs\">;\n\nexport type ContainerProps = Omit<useRender.ComponentProps<\"div\">, \"ref\"> & {\n /**\n * Maximum width, named after the breakpoint it matches: `\"md\"` stops\n * growing at the `md` breakpoint width.\n * @default \"lg\"\n */\n size?: ContainerSize;\n};\n\n/**\n * Centres page content, caps its width and keeps it clear of the screen edge.\n * The side padding grows with the viewport: `spacing(2)` on phones,\n * `spacing(3)` from `sm` and `spacing(4)` from `md`.\n *\n * ```tsx\n * <Container render={<main />}>\n * <Text variant=\"h1\">Mine lån</Text>\n * </Container>\n * ```\n */\nexport function Container({\n size = \"lg\",\n render,\n className,\n ...rest\n}: ContainerProps) {\n return useRender({\n render,\n props: {\n ...rest,\n \"data-size\": size,\n className: mergeClassName(styles.Container, className)({}),\n },\n });\n}\n","\"use client\";\n\nimport { Container } from \"../../layout/container/container\";\nimport { type AlertProps, resolveAlert } from \"../alert-parts\";\nimport styles from \"../alert.module.css\";\n\nexport type GlobalAlertProps = AlertProps & {\n /**\n * Lets the content span the full width instead of stopping at the `lg`\n * breakpoint. The side padding stays.\n * @default false\n */\n fluid?: boolean;\n innerClassName?: string;\n};\n\n/**\n * An `Alert` that spans the whole page width, for messages about the whole\n * page or session. Place it directly above or below `AppHeader`. It takes the\n * same props and styling as `Alert`, without the rounded corners, and its\n * content follows the `Container` breakpoints and side padding.\n *\n * ```tsx\n * <AppHeader />\n * <GlobalAlert severity=\"warning\" onClose={dismiss}>\n * <AlertTitle>Planned maintenance</AlertTitle>\n * Online banking is unavailable tonight from 22:00 to 23:00.\n * </GlobalAlert>\n * ```\n */\nexport function GlobalAlert({\n fluid = false,\n className,\n innerClassName,\n ...props\n}: GlobalAlertProps) {\n const { rootProps, content } = resolveAlert(props);\n\n return (\n <div\n {...rootProps}\n data-global=\"\"\n className={[styles.Alert, className].filter(Boolean).join(\" \")}\n >\n <Container\n className={[styles.Layout, innerClassName].filter(Boolean).join(\" \")}\n data-fluid={fluid || undefined}\n >\n {content}\n </Container>\n </div>\n );\n}\n","\"use client\";\n\nimport type { ReactNode } from \"react\";\n\nimport * as Dialog from \"./dialog\";\n\nexport type SimpleDialogProps = Omit<Dialog.RootProps, \"children\"> & {\n /** Heading. Becomes the dialog's accessible name. */\n title: ReactNode;\n /** Optional supporting text. Becomes the accessible description. */\n description?: ReactNode;\n /** Body content rendered below the title / description. */\n children?: ReactNode;\n /** Accessible name for the close button. Defaults to a translated \"Close\". */\n closeLabel?: string;\n /**\n * Maximum width of the surface.\n * @default \"sm\"\n */\n size?: Dialog.Size;\n /** Fills the screen below 600px instead of floating as a card. */\n fullScreenOnMobile?: boolean;\n /** Hide close button. */\n hideCloseButton?: boolean;\n};\n\n/**\n * The ready-made modal: pass `title`, an optional `description` and\n * `children`, and it renders the Lea chrome — a white, large-radius surface\n * over a dark overlay, a title row with a close button, and a scrollable body.\n *\n * Reach for `Dialog` (the parts) when you need a different layout.\n *\n * ```tsx\n * <SimpleDialog\n * open={open}\n * onOpenChange={setOpen}\n * title=\"Update your details\"\n * description=\"Keep your contact information up to date.\"\n * >\n * <Input label=\"Phone\" />\n * </SimpleDialog>\n * ```\n */\nexport function SimpleDialog({\n title,\n description,\n children,\n closeLabel,\n size,\n fullScreenOnMobile,\n hideCloseButton,\n ...rest\n}: SimpleDialogProps) {\n return (\n <Dialog.Root {...rest}>\n <Dialog.Portal>\n <Dialog.Backdrop />\n <Dialog.Viewport>\n <Dialog.Popup size={size} fullScreenOnMobile={fullScreenOnMobile}>\n <Dialog.Header>\n <Dialog.Title>{title}</Dialog.Title>\n {!hideCloseButton && <Dialog.CloseButton label={closeLabel} />}\n </Dialog.Header>\n <Dialog.Content>\n {description != null && (\n <Dialog.Description data-last={children == null || undefined}>\n {description}\n </Dialog.Description>\n )}\n {children}\n </Dialog.Content>\n </Dialog.Popup>\n </Dialog.Viewport>\n </Dialog.Portal>\n </Dialog.Root>\n );\n}\n","\"use client\";\n\nimport type { ReactNode } from \"react\";\n\nimport * as Drawer from \"./drawer\";\n\nexport type SimpleDrawerProps = Omit<Drawer.RootProps, \"children\"> & {\n /** Heading. Becomes the drawer's accessible name. */\n title: ReactNode;\n /** Optional supporting text. Becomes the accessible description. */\n description?: ReactNode;\n /** Body content rendered below the title / description. */\n children?: ReactNode;\n /** Accessible name for the close button. Defaults to a translated \"Close\". */\n closeLabel?: string;\n};\n\n/**\n * The ready-made side panel: pass `title`, an optional `description` and\n * `children`, and it renders the Lea chrome — a white surface that slides in\n * from the edge over a dark overlay, a header row with a close button, and a\n * scrollable body. Swipe-to-dismiss comes for free, towards `side`.\n *\n * Bottom and top sheets (`side=\"bottom\"` / `\"top\"`) get a grab handle and\n * round the edge that faces the content.\n *\n * Reach for `Drawer` (the parts) when you need a different layout.\n *\n * ```tsx\n * <SimpleDrawer open={open} onOpenChange={setOpen} title=\"Filters\">\n * <FilterForm />\n * </SimpleDrawer>\n * ```\n */\nexport function SimpleDrawer({\n title,\n description,\n children,\n closeLabel,\n side = \"right\",\n ...rest\n}: SimpleDrawerProps) {\n return (\n <Drawer.Root {...rest} side={side}>\n <Drawer.Portal>\n <Drawer.Backdrop />\n <Drawer.Viewport>\n <Drawer.Popup>\n {/* The handle sits on the edge the sheet is dragged towards. */}\n {side === \"bottom\" && <Drawer.Handle />}\n <Drawer.Header>\n <Drawer.Title>{title}</Drawer.Title>\n <Drawer.CloseButton label={closeLabel} />\n </Drawer.Header>\n <Drawer.Content>\n {description != null && (\n <Drawer.Description data-last={children == null || undefined}>\n {description}\n </Drawer.Description>\n )}\n {children}\n </Drawer.Content>\n {side === \"top\" && <Drawer.Handle />}\n </Drawer.Popup>\n </Drawer.Viewport>\n </Drawer.Portal>\n </Drawer.Root>\n );\n}\n","/*\n * A toast is an Alert that floats: its colours, padding, icon and text come\n * from alert.module.css, which sits on the same elements. These rules only\n * place and stack it, and set no property that alert.module.css also sets.\n *\n * The stacking follows Base UI's own example. Base UI writes the index of each\n * toast and the measured heights to the custom properties `--toast-*`.\n */\n\n.Viewport {\n position: fixed;\n /* Above Dialog and Drawer (1300), below Tooltip (1500). */\n z-index: 1400;\n inset-block-end: calc(var(--lea-spacing-2) + env(safe-area-inset-bottom));\n inset-inline: var(--lea-spacing-2);\n}\n\n/* The gutters follow Container's. */\n@media (min-width: 600px) {\n .Viewport {\n width: 22.5rem;\n inset-block-end: calc(var(--lea-spacing-3) + env(safe-area-inset-bottom));\n inset-inline: auto var(--lea-spacing-3);\n }\n}\n\n@media (min-width: 900px) {\n .Viewport {\n inset-block-end: calc(var(--lea-spacing-4) + env(safe-area-inset-bottom));\n inset-inline-end: var(--lea-spacing-4);\n }\n}\n\n.Toast {\n /* How far each toast behind the front one peeks out above it. */\n --toast-peek: var(--lea-spacing);\n /* The space between toasts once the stack spreads out. */\n --toast-gap: var(--lea-spacing);\n --toast-scale: calc(max(0, 1 - (var(--toast-index) * 0.1)));\n --toast-shrink: calc(1 - var(--toast-scale));\n /* Collapsed, every toast takes the height of the front one. */\n --toast-stack-height: var(--toast-frontmost-height, var(--toast-height));\n --toast-spread-y: calc(\n var(--toast-offset-y) * -1 + (var(--toast-index) * var(--toast-gap) * -1) +\n var(--toast-swipe-movement-y)\n );\n\n position: absolute;\n z-index: calc(1000 - var(--toast-index));\n width: 100%;\n height: var(--toast-stack-height);\n box-shadow: var(--lea-shadow-lg);\n cursor: default;\n inset-block-end: 0;\n inset-inline-end: 0;\n transform: translateX(var(--toast-swipe-movement-x))\n translateY(\n calc(\n var(--toast-swipe-movement-y) - (var(--toast-index) * var(--toast-peek)) -\n (var(--toast-shrink) * var(--toast-stack-height))\n )\n )\n scale(var(--toast-scale));\n transform-origin: bottom center;\n transition:\n transform var(--lea-duration-emphasis) var(--lea-easing-standard),\n height var(--lea-duration-emphasis) var(--lea-easing-standard),\n opacity var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n/* Fills the gap between spread-out toasts, so the pointer stays on the stack. */\n.Toast::after {\n position: absolute;\n width: 100%;\n height: calc(var(--toast-gap) + 1px);\n content: \"\";\n inset-block-start: 100%;\n inset-inline-start: 0;\n}\n\n.Toast[data-expanded] {\n height: var(--toast-height);\n transform: translateX(var(--toast-swipe-movement-x))\n translateY(var(--toast-spread-y));\n}\n\n.Toast[data-starting-style],\n.Toast[data-ending-style] {\n transform: translateY(150%);\n}\n\n.Toast[data-limited],\n.Toast[data-ending-style] {\n opacity: 0;\n}\n\n/* A swiped toast leaves in the direction it was swiped. */\n\n.Toast[data-ending-style][data-swipe-direction=\"up\"] {\n transform: translateY(calc(var(--toast-swipe-movement-y) - 150%));\n}\n\n.Toast[data-ending-style][data-swipe-direction=\"down\"] {\n transform: translateY(calc(var(--toast-swipe-movement-y) + 150%));\n}\n\n.Toast[data-ending-style][data-swipe-direction=\"left\"] {\n transform: translateX(calc(var(--toast-swipe-movement-x) - 150%))\n translateY(var(--toast-spread-y));\n}\n\n.Toast[data-ending-style][data-swipe-direction=\"right\"] {\n transform: translateX(calc(var(--toast-swipe-movement-x) + 150%))\n translateY(var(--toast-spread-y));\n}\n\n.Toast:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n/* The toasts behind the front one show only their edge until the stack spreads. */\n.Content {\n overflow: hidden;\n /*\n * Room for the focus ring of the buttons (2px wide, 2px off), which the\n * overflow would otherwise cut off. The margin takes the room back, so the\n * layout is Alert's.\n */\n padding: var(--lea-spacing-0-5);\n margin: calc(var(--lea-spacing-0-5) * -1);\n transition: opacity var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n.Content[data-behind] {\n opacity: 0;\n}\n\n.Content[data-expanded] {\n opacity: 1;\n}\n\n.Description {\n margin: 0;\n}\n\n/* Under the message, so it never crowds the close button on a phone. */\n.ToastAction {\n align-self: flex-start;\n margin-block-start: var(--lea-spacing);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Toast,\n .Content {\n transition-duration: 0.01ms;\n }\n}\n","\"use client\";\n\nimport {\n Toast as BaseToast,\n type ToastManager as BaseToastManager,\n type ToastManagerAddOptions,\n} from \"@base-ui/react/toast\";\nimport {\n createContext,\n type MouseEvent,\n type ReactNode,\n useContext,\n useState,\n} from \"react\";\n\nimport { createTranslation } from \"../../../i18n\";\nimport { Button } from \"../../buttons/button/button\";\nimport { CloseButton } from \"../../buttons/close-button/close-button\";\nimport {\n type AlertColor,\n type AlertSeverity,\n alertIcons,\n resolveSeverity,\n} from \"../alert-parts\";\nimport alertStyles from \"../alert.module.css\";\nimport styles from \"./toast.module.css\";\n\nexport type ToastSeverity = AlertSeverity;\nexport type ToastColor = AlertColor;\n\nexport type ToastOptions = {\n /**\n * The heading. Required: the toast is announced as a dialog named by its\n * title, and a dialog without a name is not accessible.\n */\n title: ReactNode;\n /** The message under the title. */\n description?: ReactNode;\n /**\n * Semantic meaning — picks the icon and how urgently screen readers\n * announce it. The same values as `Alert`.\n * @default \"success\", or \"info\" with a brand `color`\n */\n severity?: ToastSeverity;\n /**\n * Colour scheme. Defaults to the colour of `severity`. `\"primary\"` (the\n * brand purple) and `\"neutral\"` (grey) carry no status meaning.\n */\n color?: ToastColor;\n /**\n * Milliseconds before the toast closes by itself. `0` keeps it until it is\n * closed. The timer pauses while the pointer is over the toasts, while they\n * have focus and while the window is hidden.\n * @default the provider's `timeout`, 5000\n */\n timeout?: number;\n /**\n * How screen readers announce it: `\"high\"` interrupts, `\"low\"` waits.\n * @default \"high\" for `error` and `warning`, otherwise \"low\"\n */\n priority?: \"low\" | \"high\";\n /**\n * A button under the message, e.g. \"Angre\". The toast may close before\n * someone reaches it, so it must not be the only way to do the thing.\n */\n action?: {\n label: string;\n onClick: (event: MouseEvent<HTMLButtonElement>) => void;\n };\n /** Called when the toast starts to close, by the timer or by a person. */\n onClose?: () => void;\n /** Called once the toast is gone, after its exit animation. */\n onRemove?: () => void;\n /**\n * Identifies the toast. Adding a toast with the id of one on screen\n * replaces it and restarts its timer.\n */\n id?: string;\n};\n\nexport type ToastManager = {\n /** Shows a toast and returns its id. */\n add: (options: ToastOptions) => string;\n /** Closes the toast with this id, or every toast when called without one. */\n close: (id?: string) => void;\n};\n\ntype ToastData = { severity: ToastSeverity; color: ToastColor };\n\nconst toBaseOptions = ({\n severity,\n color,\n priority,\n action,\n ...rest\n}: ToastOptions): ToastManagerAddOptions<ToastData> => {\n const resolvedSeverity = resolveSeverity(severity, color);\n\n return {\n ...rest,\n type: resolvedSeverity,\n priority:\n priority ??\n (resolvedSeverity === \"error\" || resolvedSeverity === \"warning\"\n ? \"high\"\n : \"low\"),\n actionProps: action && {\n children: action.label,\n onClick: action.onClick,\n },\n data: { severity: resolvedSeverity, color: color ?? resolvedSeverity },\n };\n};\n\n/** The Base UI manager behind each of ours, for `ToastProvider`. */\nconst baseManagers = new WeakMap<ToastManager, BaseToastManager<ToastData>>();\n\n/**\n * Creates a toast manager to show toasts from outside React, such as an API\n * client. Pass it to `ToastProvider` as `toastManager`, and call `add` on it\n * anywhere. Create it once, in a module of its own:\n *\n * ```ts\n * export const toastManager = createToastManager();\n *\n * toastManager.add({ title: \"Sesjonen er utløpt\", severity: \"warning\" });\n * ```\n */\nexport function createToastManager(): ToastManager {\n const base = BaseToast.createToastManager<ToastData>();\n const manager: ToastManager = {\n add: (options) => base.add(toBaseOptions(options)),\n close: (id) => base.close(id),\n };\n baseManagers.set(manager, base);\n return manager;\n}\n\nconst ToastManagerContext = createContext<ToastManager | null>(null);\n\n/**\n * Returns the manager of the nearest `ToastProvider`, to show and close\n * toasts. The component calling it does not re-render when toasts change.\n *\n * ```tsx\n * const toast = useToastManager();\n * toast.add({ title: \"Endringene er lagret\" });\n * ```\n */\nexport function useToastManager(): ToastManager {\n const manager = useContext(ToastManagerContext);\n if (!manager) {\n throw new Error(\"useToastManager must be used inside a ToastProvider.\");\n }\n return manager;\n}\n\nconst useToastLabels = createTranslation({\n notifications: {\n no: \"Varsler\",\n se: \"Aviseringar\",\n fi: \"Ilmoitukset\",\n es: \"Notificaciones\",\n },\n});\n\nexport type ToastProviderProps = {\n children?: ReactNode;\n /**\n * Milliseconds before a toast closes by itself, unless the toast sets its\n * own. `0` keeps toasts until they are closed.\n * @default 5000\n */\n timeout?: number;\n /**\n * How many toasts are shown at once. Older ones are hidden beyond it.\n * @default 3\n */\n limit?: number;\n /**\n * A manager from `createToastManager`, to show toasts from outside React.\n * Without one, the provider makes its own.\n */\n toastManager?: ToastManager;\n};\n\n/**\n * Shows toasts: short messages that float at the bottom of the screen and\n * close by themselves. They have the statuses and colours of `Alert`. Render\n * it once, around the app, and show toasts with `useToastManager`, or with a\n * manager from `createToastManager` outside React.\n *\n * Toasts stack, newest in front, and spread out on hover. Keyboard users\n * reach them with F6. Every toast has a close button, and errors and\n * warnings are announced as soon as they appear.\n *\n * ```tsx\n * <ToastProvider>\n * <App />\n * </ToastProvider>\n * ```\n */\nexport function ToastProvider({\n children,\n timeout,\n limit,\n toastManager,\n}: ToastProviderProps) {\n const [ownManager] = useState(createToastManager);\n const manager = toastManager ?? ownManager;\n const t = useToastLabels();\n\n return (\n <ToastManagerContext.Provider value={manager}>\n <BaseToast.Provider\n timeout={timeout}\n limit={limit}\n toastManager={baseManagers.get(manager)}\n >\n {children}\n <BaseToast.Portal>\n <BaseToast.Viewport\n aria-label={t.notifications}\n className={styles.Viewport}\n >\n <ToastList />\n </BaseToast.Viewport>\n </BaseToast.Portal>\n </BaseToast.Provider>\n </ToastManagerContext.Provider>\n );\n}\n\nfunction ToastList() {\n const { toasts } = BaseToast.useToastManager<ToastData>();\n\n return toasts.map((toast) => <ToastItem key={toast.id} toast={toast} />);\n}\n\nfunction ToastItem({\n toast,\n}: {\n toast: BaseToast.Root.ToastObject<ToastData>;\n}) {\n const severity = toast.data?.severity ?? \"success\";\n\n return (\n <BaseToast.Root\n toast={toast}\n data-color={toast.data?.color ?? severity}\n data-variant=\"standard\"\n data-size=\"lg\"\n className={`${alertStyles.Alert} ${styles.Toast}`}\n >\n <BaseToast.Content className={`${alertStyles.Layout} ${styles.Content}`}>\n <div className={alertStyles.Icon}>{alertIcons[severity]}</div>\n <div className={alertStyles.Message}>\n {/* A `div`, as `AlertTitle` is: the toast sits outside the page's\n heading outline. */}\n <BaseToast.Title render={<div />} className={alertStyles.Title} />\n <BaseToast.Description className={styles.Description} />\n <BaseToast.Action\n render={<Button size=\"sm\" variant=\"outline\" color=\"secondary\" />}\n className={styles.ToastAction}\n />\n </div>\n <div className={alertStyles.Action}>\n <BaseToast.Close render={<CloseButton size=\"sm\" />} />\n </div>\n </BaseToast.Content>\n </BaseToast.Root>\n );\n}\n","/*\n * The website's loading blocks: `neutral-100`, 16px corners, fading between\n * 40 % and full opacity. 1.5s is the website's pulse: a loop has no duration\n * token (the button spinner turns in 0.8s).\n */\n.Skeleton {\n display: block;\n width: 100%;\n height: var(--lea-spacing-3);\n border-radius: var(--lea-radius-4xl);\n background-color: var(--lea-color-neutral-100);\n animation: skeleton-pulse 1.5s var(--lea-easing-standard) infinite;\n}\n\n@keyframes skeleton-pulse {\n 0%,\n 100% {\n opacity: 0.4;\n }\n\n 50% {\n opacity: 1;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Skeleton {\n animation: none;\n }\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport type { Ref } from \"react\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport styles from \"./skeleton.module.css\";\n\nexport type SkeletonProps = Omit<\n useRender.ComponentProps<\"div\">,\n \"ref\" | \"children\"\n> & {\n /** The rendered element: the `<div>`, or the element passed to `render`. */\n ref?: Ref<HTMLElement>;\n};\n\n/**\n * A grey block that pulses where content is on its way, as the website's\n * loading page. Size it to the content it stands for, with `style` or a class:\n *\n * ```tsx\n * <div role=\"status\" aria-label=\"Laster innhold\">\n * <Skeleton style={{ width: \"40%\", height: 80 }} />\n * <Skeleton style={{ height: 480 }} />\n * </div>\n * ```\n *\n * It is hidden from screen readers. Put the blocks in an element with\n * `role=\"status\"` and a name, so a screen reader hears that the page is\n * loading. Under reduced motion it stays still.\n */\nexport function Skeleton({ render, ref, className, ...rest }: SkeletonProps) {\n return useRender({\n render,\n ref,\n defaultTagName: \"div\",\n props: {\n \"aria-hidden\": true,\n ...rest,\n className: mergeClassName(styles.Skeleton, className)({}),\n },\n });\n}\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n *\n * The track, the min / max row and the helper text are styled in\n * slider-parts.module.css, which `Slider` shares.\n */\n\n.AmountSlider {\n display: flex;\n width: 100%;\n flex-direction: column;\n color: var(--lea-palette-text-base);\n font-size: var(--lea-text-body-md-font-size);\n font-weight: var(--lea-text-body-md-font-weight);\n line-height: var(--lea-text-body-md-line-height);\n letter-spacing: var(--lea-text-body-md-letter-spacing);\n}\n\n.Label {\n margin-block-end: calc(var(--lea-spacing) * 1.5);\n font-size: var(--lea-text-heading-md-font-size);\n font-weight: var(--lea-text-heading-md-font-weight);\n line-height: var(--lea-text-heading-md-line-height);\n letter-spacing: var(--lea-text-heading-md-letter-spacing);\n text-align: center;\n}\n\n/* The same box as the library's other fields: their height, radius and border. */\n.Input {\n width: 100%;\n /* 54px, the height of every other single-line field. */\n height: calc(var(--lea-spacing) * 7);\n box-sizing: border-box;\n padding-block: 0;\n padding-inline: calc(var(--lea-spacing) * 2);\n border: 1px solid transparent;\n border-radius: var(--lea-radius-xl);\n margin: 0;\n background-color: transparent;\n color: var(--lea-palette-primary-base);\n font-family: inherit;\n /* No text style has this size at medium weight, so the size alone. */\n font-size: var(--lea-font-step-8);\n font-variant-numeric: tabular-nums;\n font-weight: var(--lea-font-weight-medium);\n text-align: center;\n}\n\n/*\n * The 2px border of a state is drawn as an inset outline over the 1px one, so\n * the field never changes size between states.\n */\n@media (hover: hover) {\n .Input:hover:not(:focus, [data-disabled], [data-invalid]) {\n outline: 2px solid var(--lea-palette-border-strong);\n outline-offset: -2px;\n }\n}\n\n.Input:focus {\n outline: 2px solid var(--lea-palette-border-accent);\n outline-offset: -2px;\n}\n\n.Input:active {\n outline: 2px solid var(--lea-palette-border-accent);\n outline-offset: -2px;\n background-color: var(--lea-color-purple-50);\n}\n\n.Input[data-invalid] {\n outline: 2px solid var(--lea-palette-status-error-base);\n outline-offset: -2px;\n}\n\n.Input[data-disabled] {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n.Slider {\n display: flex;\n flex-direction: column;\n margin-block-start: var(--lea-spacing);\n}\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so these parts render correctly without a provider.\n *\n * `Slider`'s `color=\"contrast\"` sets `--slider-rail`, `--slider-fill`,\n * `--slider-thumb-ring`, `--slider-focus` and `--slider-secondary` on the\n * slider; left unset (`AmountSlider`, `color=\"primary\"`), the fallbacks are\n * the purple look.\n */\n\n.Control {\n --slider-thumb-size: calc(var(--lea-spacing) * 3);\n /* The same thickness as the LinearFormStepper bar. */\n --slider-track-size: calc(var(--lea-spacing) * 0.75);\n /*\n * Base UI centres the thumb on the value, so at min and max half of it\n * hangs past the control. The control is inset by that half to keep the\n * thumb inside the slider's own box.\n */\n --slider-inset: calc(var(--slider-thumb-size) / 2);\n --slider-thumb-hover: color-mix(\n in srgb,\n var(--lea-palette-primary-base) calc(var(--lea-opacity-hover) * 100%),\n var(--lea-palette-bg-base)\n );\n --slider-thumb-pressed: color-mix(\n in srgb,\n var(--lea-palette-primary-base) calc(var(--lea-opacity-pressed) * 100%),\n var(--lea-palette-bg-base)\n );\n\n position: relative;\n display: flex;\n /* Taller than the thumb, so the touch target is 44px. */\n height: calc(var(--lea-spacing) * 5.5);\n align-items: center;\n margin-inline: var(--slider-inset);\n cursor: pointer;\n touch-action: none;\n}\n\n/* Keeps the rounded ends of the track, which sit in the inset, clickable. */\n.Control::before {\n position: absolute;\n content: \"\";\n inset-block: 0;\n inset-inline: calc(var(--slider-inset) * -1);\n}\n\n.Control[data-disabled] {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n.Track {\n position: relative;\n width: 100%;\n height: var(--slider-track-size);\n}\n\n/*\n * The visible rail runs the full width of the slider, past the control.\n * purple-200 rather than purple-subtle, which is the same colour as the\n * bg-accent surface and would make the rail disappear on it.\n */\n.Track::before {\n position: absolute;\n border-radius: var(--lea-radius-full);\n background-color: var(--slider-rail, var(--lea-color-purple-200));\n content: \"\";\n inset-block: 0;\n inset-inline: calc(var(--slider-inset) * -1);\n}\n\n.Indicator {\n background-color: var(--slider-fill, var(--lea-palette-primary-base));\n}\n\n/* Fills the rounded start of the rail, which lies before the control. */\n.Indicator::before {\n position: absolute;\n width: var(--slider-inset);\n background-color: inherit;\n border-end-start-radius: var(--lea-radius-full);\n border-start-start-radius: var(--lea-radius-full);\n content: \"\";\n inset-block: 0;\n inset-inline-end: 100%;\n}\n\n.Thumb {\n width: var(--slider-thumb-size);\n height: var(--slider-thumb-size);\n box-sizing: border-box;\n border: 2px solid var(--slider-thumb-ring, var(--lea-palette-primary-base));\n border-radius: var(--lea-radius-full);\n background-color: var(--lea-palette-bg-base);\n transition: background-color var(--lea-duration-normal)\n var(--lea-easing-standard);\n}\n\n@media (hover: hover) {\n .Thumb:not([data-disabled]):hover {\n background-color: var(--slider-thumb-hover);\n }\n}\n\n.Thumb[data-dragging]:not([data-disabled]) {\n background-color: var(--slider-thumb-pressed);\n}\n\n/* Focus lands on the visually hidden range input inside the thumb. */\n.Thumb:has(:focus-visible) {\n outline: 2px solid var(--slider-focus, var(--lea-palette-text-base));\n outline-offset: 0.125rem;\n}\n\n/*\n * neutral-600 rather than text-subtle for the secondary text: a slider has no\n * background of its own, and text-subtle falls below 4.5:1 on the tinted\n * surfaces it can sit on.\n */\n.Range {\n display: flex;\n justify-content: space-between;\n gap: calc(var(--lea-spacing) * 2);\n color: var(--slider-secondary, var(--lea-color-neutral-600));\n font-size: var(--lea-text-body-sm-font-size);\n font-weight: var(--lea-text-body-sm-font-weight);\n line-height: var(--lea-text-body-sm-line-height);\n letter-spacing: var(--lea-text-body-sm-letter-spacing);\n font-variant-numeric: tabular-nums;\n}\n\n.HelperText {\n margin: 0;\n margin-block-start: var(--lea-spacing);\n color: var(--slider-secondary, var(--lea-color-neutral-600));\n font-size: var(--lea-text-body-sm-font-size);\n font-weight: var(--lea-text-body-sm-font-weight);\n line-height: var(--lea-text-body-sm-line-height);\n letter-spacing: var(--lea-text-body-sm-letter-spacing);\n}\n\n.HelperText[data-invalid] {\n color: var(--lea-palette-status-error-base);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Thumb {\n transition-duration: 0.01ms;\n }\n}\n","\"use client\";\n\nimport { Field } from \"@base-ui/react/field\";\nimport { Slider as BaseSlider } from \"@base-ui/react/slider\";\nimport { useMemo, type ReactNode } from \"react\";\n\nimport styles from \"./slider-parts.module.css\";\n\n/*\n * Internal parts shared by `Slider` and `AmountSlider`, so the track, the\n * min / max row and the helper text look and behave the same in both. Not\n * part of the public API, so this file isn't re-exported from `index.ts`.\n */\n\n/**\n * `render` for a `Field.Root` that is only there to hand a control its\n * validity state, which is how Base UI puts `aria-invalid` on the slider's\n * nested range input. It makes the field render no element of its own.\n */\nexport const renderWithoutElement: Field.Root.Props[\"render\"] = (props) => (\n <>{props.children}</>\n);\n\ntype SliderTrackProps = {\n \"aria-label\"?: string;\n \"aria-describedby\"?: string;\n};\n\n/**\n * The clickable control with its track, fill and thumb. The thumb is rendered\n * here so it can never be left out. Must sit inside a Base UI `Slider.Root`.\n */\nexport function SliderTrack({\n \"aria-label\": ariaLabel,\n \"aria-describedby\": ariaDescribedBy,\n}: SliderTrackProps) {\n return (\n <BaseSlider.Control className={styles.Control}>\n <BaseSlider.Track className={styles.Track}>\n <BaseSlider.Indicator className={styles.Indicator} />\n <BaseSlider.Thumb\n className={styles.Thumb}\n aria-label={ariaLabel}\n aria-describedby={ariaDescribedBy}\n />\n </BaseSlider.Track>\n </BaseSlider.Control>\n );\n}\n\ntype SliderRangeProps = {\n min: number;\n max: number;\n format?: Intl.NumberFormatOptions;\n locale?: Intl.LocalesArgument;\n};\n\n/** `min` and `max` under each end of the track, written like the value. */\nexport function SliderRange({ min, max, format, locale }: SliderRangeProps) {\n const formatter = useMemo(\n () => new Intl.NumberFormat(locale, format),\n [locale, format],\n );\n\n return (\n // The range input already exposes its bounds, so these are visual only.\n <div className={styles.Range} aria-hidden>\n <span>{formatter.format(min)}</span>\n <span>{formatter.format(max)}</span>\n </div>\n );\n}\n\ntype SliderHelperTextProps = {\n /** Id the controls point at with `aria-describedby`. */\n id: string;\n error: boolean;\n children: ReactNode;\n};\n\n/** Helper text below the slider. Announced as an alert while in error. */\nexport function SliderHelperText({\n id,\n error,\n children,\n}: SliderHelperTextProps) {\n return (\n <p\n id={id}\n className={styles.HelperText}\n data-invalid={error ? \"\" : undefined}\n role={error ? \"alert\" : undefined}\n >\n {children}\n </p>\n );\n}\n","\"use client\";\n\nimport { Field } from \"@base-ui/react/field\";\nimport { NumberField } from \"@base-ui/react/number-field\";\nimport { Slider as BaseSlider } from \"@base-ui/react/slider\";\nimport {\n useId,\n useState,\n type ComponentPropsWithRef,\n type ReactNode,\n} from \"react\";\n\nimport styles from \"./amount-slider.module.css\";\nimport {\n renderWithoutElement,\n SliderHelperText,\n SliderRange,\n SliderTrack,\n} from \"../slider-parts\";\n\nexport type AmountSliderProps = Omit<\n ComponentPropsWithRef<\"div\">,\n \"children\" | \"defaultValue\" | \"onChange\"\n> & {\n /**\n * The question above the amount, e.g. \"Hur mycket vill du låna?\". It labels\n * the amount field and names the slider, so it is required.\n */\n label: ReactNode;\n /** The lowest amount that can be chosen. */\n min: number;\n /** The highest amount that can be chosen. */\n max: number;\n /**\n * How far the slider, and the arrow keys in the amount field, move the\n * amount. A typed amount is kept as typed, also between two steps.\n * @default 1\n */\n step?: number;\n /** The amount. Use with `onValueChange` to control the component. */\n value?: number;\n /**\n * The amount when first rendered, for uncontrolled use.\n * @default min\n */\n defaultValue?: number;\n /**\n * Called with the new amount when the slider moves, and when an amount\n * within `min` and `max` is typed.\n */\n onValueChange?: (value: number) => void;\n /** How the amount, `min` and `max` are written, e.g. as a currency. */\n format?: Intl.NumberFormatOptions;\n /** The locale the amount is written and typed in. Defaults to the runtime locale. */\n locale?: Intl.LocalesArgument;\n /** Submits the amount under this name when inside a form. */\n name?: string;\n /** Disables both the amount field and the slider. */\n disabled?: boolean;\n /** Helper / error text shown below the slider and linked via `aria-describedby`. */\n helperText?: ReactNode;\n /** Puts the amount field, the slider and the helper text into the error state. */\n error?: boolean;\n};\n\n/**\n * An amount chosen by dragging a slider or by typing it, for questions like\n * \"how much do you want to borrow?\". The amount field and the slider share one\n * value: moving the slider rewrites the amount, and typing moves the thumb.\n *\n * The `label` is a real `<label>` for the amount field and also names the\n * slider, and the helper text describes both. A typed amount outside `min`\n * and `max` is brought back into range when the field loses focus, and an\n * emptied field goes back to the last amount.\n *\n * Use `Slider` instead when the value does not need to be typed.\n *\n * ```tsx\n * <AmountSlider\n * label=\"Hur mycket vill du låna?\"\n * min={20000}\n * max={600000}\n * step={5000}\n * defaultValue={200000}\n * locale=\"sv-SE\"\n * format={{ style: \"currency\", currency: \"SEK\", maximumFractionDigits: 0 }}\n * onValueChange={(amount) => setAmount(amount)}\n * />\n * ```\n */\nexport function AmountSlider({\n label,\n min,\n max,\n step = 1,\n value: valueProp,\n defaultValue,\n onValueChange,\n format,\n locale,\n name,\n disabled = false,\n helperText,\n error = false,\n className,\n ...rest\n}: AmountSliderProps) {\n const labelId = useId();\n const inputId = useId();\n const helperTextId = useId();\n const describedBy = helperText != null ? helperTextId : undefined;\n\n const [uncontrolledValue, setUncontrolledValue] = useState(\n defaultValue ?? min,\n );\n const value = valueProp ?? uncontrolledValue;\n\n // What the amount field holds while it differs from the amount: nothing, or\n // a number outside the range that hasn't been brought back in yet.\n // `undefined` means the field simply shows the amount.\n const [draft, setDraft] = useState<number | null>();\n\n const commit = (next: number) => {\n setDraft(undefined);\n if (valueProp === undefined) setUncontrolledValue(next);\n if (next !== value) onValueChange?.(next);\n };\n\n return (\n <div\n {...rest}\n className={[styles.AmountSlider, className].filter(Boolean).join(\" \")}\n >\n <label id={labelId} htmlFor={inputId} className={styles.Label}>\n {label}\n </label>\n {/*\n * The number field stays outside the slider's `Field.Root`: inside one,\n * Base UI gives the input the field's own id until after mount, which\n * leaves the label pointing at nothing on the server.\n */}\n <NumberField.Root\n id={inputId}\n name={name}\n value={draft !== undefined ? draft : value}\n // Base UI would otherwise clamp on every keystroke, sending the\n // slider to `min` on the first digit of a longer amount. The amount\n // is clamped here instead, once the field loses focus.\n allowOutOfRange\n onValueChange={(next) => {\n if (next != null && next >= min && next <= max) commit(next);\n else setDraft(next);\n }}\n onValueCommitted={(next) => {\n if (next == null) setDraft(undefined);\n else commit(Math.min(Math.max(next, min), max));\n }}\n min={min}\n max={max}\n step={step}\n format={format}\n locale={locale}\n disabled={disabled}\n >\n <NumberField.Input\n className={styles.Input}\n aria-describedby={describedBy}\n aria-invalid={error || undefined}\n data-invalid={error ? \"\" : undefined}\n />\n </NumberField.Root>\n <Field.Root invalid={error} render={renderWithoutElement}>\n <BaseSlider.Root\n value={value}\n onValueChange={commit}\n min={min}\n max={max}\n step={step}\n format={format}\n locale={locale}\n disabled={disabled}\n aria-labelledby={labelId}\n className={styles.Slider}\n >\n <SliderTrack aria-describedby={describedBy} />\n <SliderRange min={min} max={max} format={format} locale={locale} />\n </BaseSlider.Root>\n </Field.Root>\n {helperText != null && (\n <SliderHelperText id={helperTextId} error={error}>\n {helperText}\n </SliderHelperText>\n )}\n </div>\n );\n}\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n *\n * Shared by Autocomplete and EmailInput. The box, its border states, the\n * label and the text field come from input.module.css, and the list from\n * option-list.module.css; this file adds the buttons at the end of the field\n * and the chips of a multiple control.\n */\n\n/*\n * `.Field` in front of a selector lifts it over the rules of input.module.css\n * for the same element, whichever of the two stylesheets loads last.\n */\n.Field .Box {\n /* White, where Input shows the surface it sits on. As it always was. */\n background-color: var(--lea-palette-bg-base);\n}\n\n/* The field and its open list read as one shape: square where they meet. */\n.Box[data-popup-side=\"bottom\"] {\n border-end-end-radius: 0;\n border-end-start-radius: 0;\n}\n\n.Box[data-popup-side=\"top\"] {\n border-start-end-radius: 0;\n border-start-start-radius: 0;\n}\n\n/* The buttons follow the text, so it runs up to them. */\n.Field .Inner:not(:last-child) .Control {\n padding-inline-end: var(--lea-spacing-0-5);\n}\n\n/* A multiple control has a value when it has chips, whatever the input holds. */\n.Field .Box[data-filled] .Label {\n max-width: calc(\n (100% - var(--input-inset-start) - var(--input-padding-inline)) / 0.75\n );\n scale: 0.75;\n translate: 0 calc(var(--lea-spacing) * 0.75);\n}\n\n.Buttons {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n align-self: center;\n /* The arrow itself ends 14px from the edge, where the text of a field starts. */\n margin-inline-end: calc(var(--lea-spacing) * 1.25);\n}\n\n.Button {\n display: inline-flex;\n width: 1.75rem;\n height: 1.75rem;\n align-items: center;\n justify-content: center;\n padding: 0;\n border: 0;\n border-radius: var(--lea-radius-full);\n background: none;\n color: var(--lea-palette-text-base);\n cursor: pointer;\n font: inherit;\n}\n\n.Button:disabled,\n.Button[data-disabled] {\n color: var(--lea-palette-text-muted);\n cursor: not-allowed;\n}\n\n/* Out of the tab order, so only reachable by a script; shown all the same. */\n.Button:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n.Trigger svg {\n width: 1.25rem;\n height: 1.25rem;\n transition: rotate var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n.Trigger[data-popup-open] svg {\n rotate: 180deg;\n}\n\n.Clear svg {\n width: 1.125rem;\n height: 1.125rem;\n}\n\n/*\n * With a mouse, the clear button waits for the pointer or for focus, so a\n * form at rest is not a column of crosses. A touch screen has neither, and\n * shows it whenever there is something to clear.\n */\n@media (pointer: fine) {\n .Box:not(:hover, :focus-within) .Clear {\n visibility: hidden;\n }\n}\n\n.Box[data-disabled] .Clear {\n display: none;\n}\n\n/* The chips of a multiple control, and its input after them. */\n.Chips {\n display: flex;\n min-width: 0;\n flex: 1;\n flex-wrap: wrap;\n align-items: center;\n padding-inline: var(--input-inset-start) var(--lea-spacing-0-5);\n padding-block: var(--input-padding-top) calc(var(--lea-spacing) * 0.75);\n gap: var(--lea-spacing-0-5);\n}\n\n.ChipInput {\n /* Shares the last line of chips while 4rem is left on it, then takes a line of its own. */\n min-width: 4rem;\n flex: 1;\n padding: 0;\n padding-inline-start: var(--lea-spacing-0-5);\n border: 0;\n margin: 0;\n background: none;\n color: inherit;\n font: inherit;\n letter-spacing: inherit;\n outline: none;\n}\n\n.ChipInput:disabled {\n cursor: not-allowed;\n opacity: 1;\n /* Safari dims disabled text on top of the colour set here. */\n -webkit-text-fill-color: currentcolor;\n}\n\n.ChipInput::placeholder {\n color: var(--lea-palette-text-muted);\n opacity: 1;\n}\n\n/* While the label rests over the field the placeholder is hidden, so the two never overlap. */\n.Box[data-has-label]:not(:focus-within, [data-filled]) .ChipInput::placeholder {\n color: transparent;\n}\n\n/*\n * The list fades in, and goes at once. While a list is on its way out, Base UI\n * keeps showing the options of the text it was closed with; a key pressed in\n * that time brought the list back with those, not with what was typed.\n */\n.Popup[data-ending-style] {\n transition: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Trigger svg {\n transition-duration: 0.01ms;\n }\n}\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so these parts render correctly without a provider.\n *\n * The sizes and distances match the label and helper text of the fields that\n * are still built on MUI, so the two sit side by side in a form without a\n * visible seam.\n */\n\n.GroupLabel {\n display: inline-flex;\n width: fit-content;\n align-items: center;\n padding-block-end: var(--lea-spacing-1-5);\n margin: 0;\n color: var(--lea-palette-text-base);\n font-size: var(--lea-text-label-lg-font-size);\n font-weight: var(--lea-text-label-lg-font-weight);\n gap: var(--lea-spacing-1);\n letter-spacing: var(--lea-text-label-lg-letter-spacing);\n line-height: var(--lea-text-label-lg-line-height);\n}\n\n.GroupLabel[data-invalid] {\n color: var(--lea-palette-status-error-base);\n}\n\n.GroupLabel[data-disabled] {\n color: var(--lea-palette-text-muted);\n}\n\n.HelperText {\n /* 14px, where the text inside a field starts. */\n margin-inline: calc(var(--lea-spacing) * 1.75);\n margin-block: var(--lea-spacing-0-5) 0;\n /*\n * neutral-600 rather than text-subtle: these controls have no background of\n * their own, and text-subtle falls below 4.5:1 on the tinted surfaces they\n * can sit on.\n */\n color: var(--lea-color-neutral-600);\n font-size: var(--lea-text-body-sm-font-size);\n font-weight: var(--lea-text-body-sm-font-weight);\n letter-spacing: var(--lea-text-body-sm-letter-spacing);\n line-height: var(--lea-text-body-sm-line-height);\n}\n\n.HelperText[data-invalid] {\n color: var(--lea-palette-status-error-base);\n}\n\n/*\n * A value that is allowed but unusual. amber-700, not the warning base, which\n * is 2:1 on white: amber-700 is 5.9:1. It colours the text only; the field\n * keeps its border, because the value is valid.\n */\n.HelperText[data-warning] {\n color: var(--lea-color-amber-700);\n}\n","import { createTranslation } from \"../../i18n\";\n\n/**\n * Translated accessible name for the info trigger a control renders next to\n * its label when given a `tooltip`.\n */\nexport const useTooltipTriggerLabels = createTranslation({\n moreInformation: {\n no: \"Mer informasjon\",\n se: \"Mer information\",\n fi: \"Lisätietoja\",\n es: \"Más información\",\n },\n});\n","\"use client\";\n\nimport { InfoIcon } from \"lucide-react\";\nimport type { ReactNode } from \"react\";\n\nimport { Tooltip } from \"../data-display\";\nimport styles from \"./field-parts.module.css\";\nimport { useTooltipTriggerLabels } from \"./use-tooltip-trigger-labels\";\n\n/*\n * Internal parts shared by the form controls that no longer wrap MUI\n * (`Input`, `Checkbox`, the radio groups), so their label, asterisk and helper\n * text look and behave the same. Not part of the public API, so this file\n * isn't re-exported from `index.ts`.\n *\n * Each control generates its own ids with `useId` and hands them to these\n * parts, so every link (`aria-labelledby`, `aria-describedby`) is already in\n * the markup the server sends.\n */\n\n/** The ` *` after the label of a required control. Decorative: `required` is on the control. */\nexport const RequiredAsterisk = ({ className }: { className?: string }) => (\n <span aria-hidden className={className}>\n {\"\\u2009*\"}\n </span>\n);\n\ntype FieldHelperTextProps = {\n /** Id the control points at with `aria-describedby`. */\n id: string;\n error: boolean;\n /** The text is a warning: amber, and announced politely. */\n warning?: boolean;\n children: ReactNode;\n};\n\n/**\n * Helper text below a control. Announced as an alert while in error, and as a\n * status while it is a warning.\n */\nexport function FieldHelperText({\n id,\n error,\n warning = false,\n children,\n}: FieldHelperTextProps) {\n return (\n <p\n id={id}\n className={styles.HelperText}\n data-invalid={error ? \"\" : undefined}\n data-warning={warning && !error ? \"\" : undefined}\n role={error ? \"alert\" : warning ? \"status\" : undefined}\n >\n {children}\n </p>\n );\n}\n\n/**\n * What the line below a control shows: the warning while there is one,\n * otherwise the helper text. An error wins over a warning, so with `error`\n * the line is the helper text (where a form puts its error message).\n */\nexport function fieldMessage(\n helperText: ReactNode,\n warning: ReactNode,\n error: boolean,\n): { content: ReactNode; warning: boolean } {\n const showWarning =\n !error && warning != null && warning !== false && warning !== \"\";\n return { content: showWarning ? warning : helperText, warning: showWarning };\n}\n\ntype FieldGroupLabelProps = {\n /** Id the group points at with `aria-labelledby`. It sits on the text alone. */\n id: string;\n /**\n * Id of a single control this labels. The text is then a real `<label>`, so\n * a click on it moves focus to the control.\n */\n htmlFor?: string;\n /** Help text behind an info button next to the label. */\n tooltip?: ReactNode;\n required?: boolean;\n error?: boolean;\n disabled?: boolean;\n children: ReactNode;\n};\n\n/**\n * The label above a group of choices, or above a field whose label sits\n * outside it. The element is not a `<label>`: it names its control through\n * `aria-labelledby`, and a `<label>` around the info button would pass clicks\n * on the text to that button. With `htmlFor`, the text alone is a `<label>`.\n */\nexport function FieldGroupLabel({\n id,\n htmlFor,\n tooltip,\n required,\n error,\n disabled,\n children,\n}: FieldGroupLabelProps) {\n const labels = useTooltipTriggerLabels();\n const hasTooltip = tooltip != null && tooltip !== false;\n\n return (\n <div\n className={styles.GroupLabel}\n data-invalid={error ? \"\" : undefined}\n data-disabled={disabled ? \"\" : undefined}\n >\n {/* One part of the row, so the asterisk follows the text by its own\n thin space and stays on the last line when the text wraps. */}\n <span>\n {htmlFor != null ? (\n <label id={id} htmlFor={htmlFor}>\n {children}\n </label>\n ) : (\n <span id={id}>{children}</span>\n )}\n {required && <RequiredAsterisk />}\n </span>\n {hasTooltip && (\n <Tooltip\n title={tooltip}\n ariaLabel={labels.moreInformation}\n triggerIcon={<InfoIcon size={16} aria-hidden />}\n // The colour of the label, in every state of the button.\n style={{ color: \"inherit\" }}\n />\n )}\n </div>\n );\n}\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n *\n * The box, the floating label and the distances match the fields that are\n * still built on MUI (`Select`, `Autocomplete`, the date picker), so an\n * `Input` next to one of them lines up.\n */\n\n.Field {\n position: relative;\n display: flex;\n width: 100%;\n min-width: 0;\n flex-direction: column;\n}\n\n.Box {\n /* 54px, the height of every single-line field. */\n --input-height: calc(var(--lea-spacing) * 6.75);\n /* 14px, where the other fields start their text. */\n --input-padding-inline: calc(var(--lea-spacing) * 1.75);\n /* Room above the value for the floated label. */\n --input-padding-top: var(--lea-spacing-3);\n --input-border-color: var(--lea-color-neutral-300);\n --input-border-width: 1px;\n\n position: relative;\n display: flex;\n min-height: var(--input-height);\n box-sizing: border-box;\n border-radius: var(--lea-radius-xl);\n color: var(--lea-palette-text-base);\n cursor: text;\n font-size: var(--lea-text-body-md-font-size);\n font-weight: var(--lea-text-body-md-font-weight);\n letter-spacing: var(--lea-text-body-md-letter-spacing);\n line-height: var(--lea-text-body-md-line-height);\n}\n\n/*\n * The border is drawn over the box rather than on it, so going from 1px to\n * 2px in a state never moves what is inside.\n */\n.Box::after {\n position: absolute;\n border: var(--input-border-width) solid var(--input-border-color);\n border-radius: inherit;\n content: \"\";\n inset: 0;\n pointer-events: none;\n}\n\n@media (hover: hover) {\n .Box:hover:not(:focus-within, [data-disabled], [data-invalid]) {\n --input-border-color: var(--lea-palette-border-strong);\n --input-border-width: 2px;\n }\n}\n\n/* The border is the focus indicator: the input's own outline is taken off below. */\n.Box:focus-within {\n --input-border-color: var(--lea-palette-border-accent);\n --input-border-width: 2px;\n}\n\n.Box:active:not([data-disabled], [data-invalid]) {\n --input-border-color: var(--lea-palette-border-accent);\n --input-border-width: 2px;\n\n background-color: var(--lea-palette-bg-subtle);\n}\n\n/* After focus and active, so an invalid field stays red in every state. */\n.Box[data-invalid] {\n --input-border-color: var(--lea-palette-status-error-base);\n --input-border-width: 2px;\n}\n\n.Box[data-disabled] {\n color: var(--lea-palette-text-muted);\n cursor: not-allowed;\n}\n\n/* The label and the control share this cell, so the label follows an adornment of any width. */\n.Inner {\n --input-inset-start: var(--input-padding-inline);\n\n position: relative;\n display: flex;\n min-width: 0;\n flex: 1;\n}\n\n.Adornment[data-position=\"start\"] + .Inner {\n --input-inset-start: 0px;\n}\n\n.Control {\n width: 100%;\n min-width: 0;\n box-sizing: border-box;\n padding-block: var(--input-padding-top) calc(var(--lea-spacing) * 0.75);\n padding-inline: var(--input-inset-start) var(--input-padding-inline);\n border: 0;\n border-radius: inherit;\n margin: 0;\n background: none;\n color: inherit;\n font: inherit;\n letter-spacing: inherit;\n outline: none;\n}\n\n.Control:disabled {\n cursor: not-allowed;\n opacity: 1;\n /* Safari dims disabled text on top of the colour set here. */\n -webkit-text-fill-color: currentcolor;\n}\n\ntextarea.Control {\n display: block;\n resize: none;\n}\n\n/* With the label above the field, nothing floats, so the value is centred. */\n.Box[data-label-outside] .Control {\n padding-block: calc(\n (var(--input-height) - var(--lea-text-body-md-line-height) * 1em) / 2\n );\n}\n\n.Control::placeholder {\n color: var(--lea-palette-text-muted);\n opacity: 1;\n}\n\n/* While the label rests over the field the placeholder is hidden, so the two never overlap. */\n.Box[data-has-label]:not(:focus-within) .Control::placeholder {\n color: transparent;\n}\n\n/* Keeps the browser's autofill tint off the field. */\n.Control:-webkit-autofill {\n box-shadow: 0 0 0 100px var(--lea-palette-bg-base) inset;\n caret-color: var(--lea-palette-text-base);\n -webkit-text-fill-color: var(--lea-palette-text-base);\n}\n\n.Label {\n position: absolute;\n z-index: 1;\n overflow: hidden;\n max-width: calc(\n 100% - var(--input-inset-start) - var(--input-padding-inline)\n );\n /*\n * neutral-600 rather than text-subtle: a field has no background of its\n * own, and text-subtle falls below 4.5:1 on the tinted surfaces it can sit on.\n */\n color: var(--lea-color-neutral-600);\n inset-block-start: 0;\n inset-inline-start: var(--input-inset-start);\n pointer-events: none;\n scale: 1;\n text-overflow: ellipsis;\n transform-origin: top left;\n /* Resting, the label is centred in a single-line field, where the value will be. */\n translate: 0\n calc((var(--input-height) - var(--lea-text-body-md-line-height) * 1em) / 2);\n transition: color var(--lea-duration-normal) var(--lea-easing-standard),\n scale var(--lea-duration-normal) var(--lea-easing-standard),\n translate var(--lea-duration-normal) var(--lea-easing-standard);\n white-space: nowrap;\n}\n\n/*\n * The label floats to the top of the field once there is something to make\n * room for: focus, or a value. `:placeholder-shown` is how CSS sees an empty\n * control, which is why the control always has a placeholder, if only a space.\n */\n.Box:focus-within .Label,\n.Inner:has(.Control:not(:placeholder-shown)) .Label {\n max-width: calc(\n (100% - var(--input-inset-start) - var(--input-padding-inline)) / 0.75\n );\n scale: 0.75;\n translate: 0 calc(var(--lea-spacing) * 0.75);\n}\n\n/* Separate rules: a browser that does not know a selector drops the whole list. */\n.Inner:has(.Control:-webkit-autofill) .Label {\n max-width: calc(\n (100% - var(--input-inset-start) - var(--input-padding-inline)) / 0.75\n );\n scale: 0.75;\n translate: 0 calc(var(--lea-spacing) * 0.75);\n}\n\n.Inner:has(.Control:autofill) .Label {\n max-width: calc(\n (100% - var(--input-inset-start) - var(--input-padding-inline)) / 0.75\n );\n scale: 0.75;\n translate: 0 calc(var(--lea-spacing) * 0.75);\n}\n\n.Box:focus-within .Label {\n color: var(--lea-palette-text-accent);\n}\n\n.Box[data-invalid] .Label {\n color: var(--lea-palette-status-error-base);\n}\n\n.Box[data-disabled] .Label {\n color: var(--lea-palette-text-muted);\n}\n\n.Adornment {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n align-self: center;\n color: var(--lea-palette-text-subtle);\n white-space: nowrap;\n}\n\n.Adornment[data-position=\"start\"] {\n /*\n * The value sits low in the field to clear the floated label, so a centred\n * icon reads as too high next to it. The margin moves it down by half its\n * size, onto the text line.\n */\n margin-block-start: var(--lea-spacing-0-5);\n margin-inline: var(--lea-spacing-1-5) var(--lea-spacing-1);\n}\n\n/* With the label above the field the value is centred, so the icon is too. */\n.Box[data-label-outside] .Adornment[data-position=\"start\"] {\n margin-block-start: 0;\n}\n\n.Adornment[data-position=\"end\"] {\n margin-inline: var(--lea-spacing-1) var(--input-padding-inline);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Label {\n transition-duration: 0.01ms;\n }\n}\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n *\n * The list of options in a popup, shared by Select, MultiSelect, Autocomplete,\n * EmailInput and the country list of PhoneNumberInput.\n */\n\n/* Level with Dialog and Drawer (1300), and later in the page, so a list\n inside one of them opens above it. */\n.Positioner {\n z-index: 1300;\n outline: none;\n}\n\n/* As wide as the field: a long option wraps rather than widening the list. */\n.Popup {\n overflow: hidden auto;\n width: var(--anchor-width);\n max-width: var(--available-width);\n /*\n * 200px: five options, then it scrolls. Less when the window is short.\n * Base UI sets --available-height once it has placed the popup, and places\n * it by its size before that; the fallback keeps that first size at 200px,\n * so a long list is not sent above the field for want of room it has.\n */\n max-height: min(12.5rem, var(--available-height, 100vh));\n box-sizing: border-box;\n border: 1px solid var(--lea-palette-border-accent);\n border-radius: 0 0 var(--lea-radius-xl) var(--lea-radius-xl);\n background-color: var(--lea-palette-bg-base);\n box-shadow: var(--lea-shadow-lg);\n color: var(--lea-palette-text-base);\n outline: none;\n transition: opacity var(--lea-duration-fast) var(--lea-easing-standard);\n}\n\n.Popup[data-side=\"top\"] {\n border-radius: var(--lea-radius-xl) var(--lea-radius-xl) 0 0;\n}\n\n.Popup[data-starting-style],\n.Popup[data-ending-style] {\n opacity: 0;\n}\n\n.List {\n display: flex;\n flex-direction: column;\n padding: var(--lea-spacing-0-5);\n /* A hair between options, so two marked neighbours do not merge. */\n gap: calc(var(--lea-spacing) * 0.25);\n outline: none;\n}\n\n/* A list to search in can come up empty; `.Empty` then stands in for it. */\n.List[data-empty] {\n display: none;\n}\n\n.Item {\n --option-bg: transparent;\n\n display: flex;\n align-items: center;\n padding-block: var(--lea-spacing-1);\n padding-inline: var(--lea-spacing-2);\n border-radius: var(--lea-radius-md);\n background-color: var(--option-bg);\n cursor: pointer;\n font-size: var(--lea-text-body-md-font-size);\n font-weight: var(--lea-text-body-md-font-weight);\n gap: var(--lea-spacing-1);\n letter-spacing: var(--lea-text-body-md-letter-spacing);\n line-height: var(--lea-text-body-md-line-height);\n outline: none;\n user-select: none;\n}\n\n.ItemText {\n min-width: 0;\n flex: 1;\n /* A word longer than the list breaks instead of being cut off. */\n overflow-wrap: break-word;\n}\n\n@media (hover: hover) {\n .Item:hover:not([data-disabled]) {\n --option-bg: var(--lea-palette-bg-subtle);\n }\n}\n\n/* After hover, so the chosen option keeps its fill under the pointer. */\n.Item[data-selected] {\n --option-bg: var(--lea-palette-bg-accent);\n}\n\n/*\n * The option the arrow keys are on: a shade over whatever fill it has. In a\n * select Base UI moves focus to that option, so `:focus-visible` shows it for\n * the keyboard and leaves the mouse its hover. In a combobox focus stays in\n * the text field and Base UI marks the option `data-highlighted` instead; the\n * popup carries `data-keyboard` while the keyboard is what is in use.\n */\n.Item:focus-visible,\n[data-keyboard] .Item[data-highlighted] {\n background-color: color-mix(\n in srgb,\n var(--lea-palette-text-base) calc(var(--lea-opacity-pressed) * 100%),\n var(--option-bg)\n );\n}\n\n.Item[data-disabled] {\n cursor: not-allowed;\n opacity: 0.38;\n}\n\n/* \"No options\" and \"Loading…\", where the options would be. */\n.Empty:not(:empty) {\n padding-block: calc(var(--lea-spacing) * 1.75);\n padding-inline: var(--lea-spacing-2);\n color: var(--lea-palette-text-subtle);\n font-size: var(--lea-text-body-md-font-size);\n font-weight: var(--lea-text-body-md-font-weight);\n letter-spacing: var(--lea-text-body-md-letter-spacing);\n line-height: var(--lea-text-body-md-line-height);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Popup {\n transition-duration: 0.01ms;\n }\n}\n","import { createTranslation } from \"../../i18n\";\n\n/**\n * Translated texts for the controls with a list to search in (`Autocomplete`\n * and `EmailInput`): what the list says when it has nothing to show, and the\n * names of the open and clear buttons.\n */\nexport const useAutocompleteLabels = createTranslation({\n noOptions: {\n no: \"Ingen alternativer\",\n se: \"Inga alternativ\",\n fi: \"Ei vaihtoehtoja\",\n es: \"Sin opciones\",\n },\n loading: {\n no: \"Laster…\",\n se: \"Laddar…\",\n fi: \"Ladataan…\",\n es: \"Cargando…\",\n },\n open: { no: \"Åpne\", se: \"Öppna\", fi: \"Avaa\", es: \"Abrir\" },\n close: { no: \"Lukk\", se: \"Stäng\", fi: \"Sulje\", es: \"Cerrar\" },\n clear: { no: \"Tøm\", se: \"Rensa\", fi: \"Tyhjennä\", es: \"Borrar\" },\n});\n","import type { FocusEvent, RefObject } from \"react\";\n\n/**\n * `onBlur` for a field that is made of several elements, some of them in a\n * popup: it is called once focus has left them all, not when it moves from\n * one to another. Put the returned handler on each element in `refs`.\n */\nexport function useBlurOutside(\n onBlur: ((event: FocusEvent<HTMLElement>) => void) | undefined,\n refs: readonly RefObject<HTMLElement | null>[],\n) {\n return (event: FocusEvent<HTMLElement>) => {\n if (!onBlur) return;\n // Focus is on its way somewhere; decide when it has arrived.\n window.setTimeout(() => {\n const active = document.activeElement;\n if (refs.some((ref) => ref.current?.contains(active))) return;\n onBlur(event);\n });\n };\n}\n","\"use client\";\n\nimport { Combobox } from \"@base-ui/react/combobox\";\nimport { ChevronDown, X } from \"lucide-react\";\nimport {\n useId,\n useRef,\n useState,\n type CSSProperties,\n type FocusEvent,\n type InputHTMLAttributes,\n type ReactNode,\n type Ref,\n} from \"react\";\n\nimport styles from \"./combo-field.module.css\";\nimport { FieldHelperText, fieldMessage, RequiredAsterisk } from \"./field-parts\";\nimport inputStyles from \"./input.module.css\";\nimport listStyles from \"./option-list.module.css\";\nimport { useAutocompleteLabels } from \"./use-autocomplete-labels\";\nimport { useBlurOutside } from \"./use-blur-outside\";\n\n/*\n * Internal. The field that `Autocomplete` and `EmailInput` share: the box and\n * the floating label of `Input` around a text field with the `combobox` role,\n * the clear and open buttons, and the list in a popup. Not part of the public\n * API, so this file isn't re-exported from `index.ts`.\n *\n * The parts are Base UI's combobox parts. Which root they sit in is up to the\n * caller (`Combobox.Root` to choose among options, `Autocomplete.Root` for\n * free text with suggestions), so the caller renders it through `renderRoot`.\n */\n\n/** The props `Autocomplete` and `EmailInput` have in common. */\nexport type ComboFieldBaseProps = {\n /** Floating label, linked to the input for you. */\n label?: ReactNode;\n /** Helper / error text shown below the control and linked via `aria-describedby`. */\n helperText?: ReactNode;\n /** Puts the control, its label and its helper text into the error state. */\n error?: boolean;\n /**\n * A message for a value that is allowed but unusual, such as an unusually\n * high amount. Shown in amber in place of the helper text, without the\n * error state: the field is not marked invalid. `error` wins when both are set.\n */\n warning?: ReactNode;\n /** Marks the control as required, with an asterisk after the label. */\n required?: boolean;\n disabled?: boolean;\n /**\n * Text shown while the input is empty. With a `label` it appears once the\n * label has floated up, on focus.\n */\n placeholder?: string;\n /** Called when focus leaves the control, its list included. */\n onBlur?: (event: FocusEvent<HTMLElement>) => void;\n /** Submits the value under this name in a native form. */\n name?: string;\n /** Id of the text `<input>`. */\n id?: string;\n /** Names the control when there is no visible `label`. */\n \"aria-label\"?: string;\n \"aria-labelledby\"?: string;\n \"aria-describedby\"?: string;\n /** Class on the outermost element, for layout. */\n className?: string;\n /** Inline style on the outermost element, for layout. */\n style?: CSSProperties;\n /** The text `<input>`. */\n ref?: Ref<HTMLInputElement>;\n};\n\ntype ComboFieldProps = Omit<ComboFieldBaseProps, \"name\"> & {\n /** Wraps the field and its popup in the Base UI root that drives them. */\n renderRoot: (children: ReactNode) => ReactNode;\n /** More attributes for the `<input>`: `type`, `name`, `autoComplete`, … */\n inputProps?: InputHTMLAttributes<HTMLInputElement>;\n /**\n * The chosen values of a multiple control, as `Combobox.Chip`s. They share\n * the field with the input and wrap onto new lines.\n */\n chips?: ReactNode;\n /** Whether a multiple control has values, which is what floats its label. */\n filled?: boolean;\n /** Shows the button that empties the control, while there is something to empty. */\n clearable?: boolean;\n /** Shows the arrow button that opens and closes the list. */\n trigger?: boolean;\n /**\n * Selects the text on the first click into the input, so that typing\n * replaces it. For a control whose text is the label of the chosen option.\n */\n selectTextOnClick?: boolean;\n /** The content of the popup: the list, and what stands in for it when empty. */\n children: ReactNode;\n /** More below the helper text, inside the outermost element. */\n afterHelperText?: ReactNode;\n};\n\nconst join = (...parts: (string | false | null | undefined)[]) =>\n parts.filter(Boolean).join(\" \") || undefined;\n\nexport function ComboField({\n label,\n helperText,\n error = false,\n warning,\n required = false,\n disabled = false,\n placeholder,\n onBlur,\n id,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n \"aria-describedby\": ariaDescribedBy,\n className,\n style,\n ref,\n renderRoot,\n inputProps,\n chips,\n filled = false,\n clearable = false,\n trigger = false,\n selectTextOnClick = false,\n children,\n afterHelperText,\n}: ComboFieldProps) {\n const labels = useAutocompleteLabels();\n const generatedId = useId();\n const inputId = id ?? generatedId;\n const labelId = useId();\n const helperTextId = useId();\n const hasLabel = label != null;\n const message = fieldMessage(helperText, warning, error);\n const hasHelperText = message.content != null;\n const multiple = chips !== undefined;\n\n const boxRef = useRef<HTMLDivElement>(null);\n const popupRef = useRef<HTMLDivElement>(null);\n const handleBlur = useBlurOutside(onBlur, [boxRef, popupRef]);\n\n // Focus stays in the input while the arrow keys move through the list, so\n // `:focus-visible` cannot tell which of the two is in use. This does: the\n // option the keys are on is only marked once a key has been pressed.\n const [keyboard, setKeyboard] = useState(false);\n\n // True from the moment the input takes focus until the click that gave it\n // focus has been handled.\n const firstClick = useRef(false);\n\n const input = (\n <Combobox.Input\n {...inputProps}\n ref={ref}\n id={inputId}\n disabled={disabled}\n required={required}\n // Never empty: `:placeholder-shown` is how the stylesheet tells an\n // empty input from a filled one, and it needs a placeholder to work.\n placeholder={placeholder || \" \"}\n aria-label={ariaLabel}\n // Base UI hides everything but the input and the list from assistive\n // tech while the list is open, the label included. A label that is\n // referred to by id still names the input then.\n aria-labelledby={ariaLabelledBy ?? (hasLabel ? labelId : undefined)}\n aria-invalid={error || undefined}\n aria-describedby={join(hasHelperText && helperTextId, ariaDescribedBy)}\n className={\n multiple ? styles.ChipInput : join(inputStyles.Control, styles.Control)\n }\n onFocus={() => {\n firstClick.current = true;\n }}\n onClick={(event) => {\n const element = event.currentTarget;\n if (\n selectTextOnClick &&\n firstClick.current &&\n element.selectionStart === element.selectionEnd\n )\n element.select();\n firstClick.current = false;\n }}\n onKeyDown={(event) => {\n // With the list closed, Base UI empties the control on Escape. Left\n // alone, the key keeps its meaning for what is around the control,\n // such as closing a dialog.\n if (\n event.key === \"Escape\" &&\n event.currentTarget.getAttribute(\"aria-expanded\") !== \"true\"\n )\n event.preventBaseUIHandler();\n }}\n />\n );\n\n return (\n <div\n className={join(inputStyles.Field, styles.Field, className)}\n style={style}\n >\n {renderRoot(\n <>\n <Combobox.InputGroup\n ref={boxRef}\n className={join(inputStyles.Box, styles.Box)}\n data-invalid={error ? \"\" : undefined}\n data-has-label={hasLabel ? \"\" : undefined}\n data-filled={filled ? \"\" : undefined}\n onBlur={handleBlur}\n onKeyDownCapture={() => setKeyboard(true)}\n onPointerDownCapture={() => setKeyboard(false)}\n >\n <div className={join(inputStyles.Inner, styles.Inner)}>\n {hasLabel && (\n <label\n id={labelId}\n htmlFor={inputId}\n className={join(inputStyles.Label, styles.Label)}\n >\n {label}\n {required && <RequiredAsterisk />}\n </label>\n )}\n {multiple ? (\n <Combobox.Chips className={styles.Chips}>\n {chips}\n {input}\n </Combobox.Chips>\n ) : (\n input\n )}\n </div>\n {(clearable || trigger) && (\n <div className={styles.Buttons}>\n {clearable && (\n <Combobox.Clear\n className={join(styles.Button, styles.Clear)}\n aria-label={labels.clear}\n // Out of the tab order, as the arrow is: the keyboard\n // empties the input by deleting its text.\n tabIndex={-1}\n >\n <X aria-hidden />\n </Combobox.Clear>\n )}\n {trigger && (\n <Combobox.Trigger\n className={join(styles.Button, styles.Trigger)}\n tabIndex={-1}\n // Until the input has mounted, Base UI takes this button\n // for the combobox itself and says so in the markup the\n // server sends. It is the arrow next to one.\n render={(props, state) => (\n <button\n {...props}\n role={undefined}\n aria-haspopup=\"listbox\"\n aria-required={undefined}\n aria-label={state.open ? labels.close : labels.open}\n />\n )}\n >\n <ChevronDown aria-hidden />\n </Combobox.Trigger>\n )}\n </div>\n )}\n </Combobox.InputGroup>\n <Combobox.Portal>\n <Combobox.Positioner\n anchor={boxRef}\n side=\"bottom\"\n align=\"start\"\n sideOffset={0}\n className={listStyles.Positioner}\n >\n <Combobox.Popup\n ref={popupRef}\n className={join(listStyles.Popup, styles.Popup)}\n data-keyboard={keyboard ? \"\" : undefined}\n onBlur={handleBlur}\n onPointerDownCapture={() => setKeyboard(false)}\n >\n {children}\n </Combobox.Popup>\n </Combobox.Positioner>\n </Combobox.Portal>\n </>,\n )}\n {hasHelperText && (\n <FieldHelperText\n id={helperTextId}\n error={error}\n warning={message.warning}\n >\n {message.content}\n </FieldHelperText>\n )}\n {afterHelperText}\n </div>\n );\n}\n","\"use client\";\n\nimport { Combobox } from \"@base-ui/react/combobox\";\nimport { CircleX } from \"lucide-react\";\nimport { useState, type ReactElement, type ReactNode } from \"react\";\n\nimport chipStyles from \"../../data-display/chip/chip.module.css\";\nimport { useChipLabels } from \"../../data-display/chip/use-chip-labels\";\nimport { ComboField, type ComboFieldBaseProps } from \"../combo-field\";\nimport listStyles from \"../option-list.module.css\";\nimport type { SelectOption } from \"../select-field\";\nimport { useAutocompleteLabels } from \"../use-autocomplete-labels\";\n\ntype AutocompleteBaseProps<Value extends string> = ComboFieldBaseProps & {\n /** The choices. Typing filters them by their label. */\n options: readonly SelectOption<Value>[];\n /** Shown in the list when nothing matches. Translated by default. */\n noOptionsText?: ReactNode;\n /**\n * Whether the options are on their way. While there are none to show, the\n * list says so instead of \"no options\".\n */\n loading?: boolean;\n};\n\ntype SingleValueProps<Value extends string> = {\n /** Choose several values, shown as chips. */\n multiple?: false;\n /** The chosen value (controlled). `\"\"` means nothing is chosen. */\n value?: Value | \"\";\n /** The chosen value when first rendered, for uncontrolled use. */\n defaultValue?: Value | \"\";\n /** Called with the chosen value, or `\"\"` when the control is emptied. */\n onValueChange?: (value: Value | \"\") => void;\n};\n\ntype MultipleValueProps<Value extends string> = {\n /** Choose several values, shown as chips. */\n multiple: true;\n /** The chosen values (controlled). */\n value?: Value[];\n /** The chosen values when first rendered, for uncontrolled use. */\n defaultValue?: Value[];\n /** Called with the next array of chosen values. */\n onValueChange?: (value: Value[]) => void;\n /** Fewest values that must stay chosen. @default 1 */\n min?: number;\n /** Most values that can be chosen. Unlimited when omitted. */\n max?: number;\n};\n\ntype AutocompleteSingleProps<Value extends string = string> =\n AutocompleteBaseProps<Value> & SingleValueProps<Value>;\n\ntype AutocompleteMultipleProps<Value extends string = string> =\n AutocompleteBaseProps<Value> & MultipleValueProps<Value>;\n\nexport type AutocompleteProps<Value extends string = string> =\n | AutocompleteSingleProps<Value>\n | AutocompleteMultipleProps<Value>;\n\nconst sameOption = (a: SelectOption, b: SelectOption) => a.value === b.value;\n\ntype OptionListProps = {\n noOptionsText?: ReactNode;\n loading?: boolean;\n /** Options that cannot be picked right now, on top of their own `disabled`. */\n isDisabled?: (option: SelectOption) => boolean;\n};\n\n/** The content of the popup: the options that match, or a line of text when none do. */\nfunction OptionList({ noOptionsText, loading, isDisabled }: OptionListProps) {\n const labels = useAutocompleteLabels();\n\n return (\n <>\n <Combobox.Empty className={listStyles.Empty}>\n {loading ? labels.loading : (noOptionsText ?? labels.noOptions)}\n </Combobox.Empty>\n <Combobox.List className={listStyles.List}>\n {(option: SelectOption) => (\n <Combobox.Item\n key={option.value}\n value={option}\n disabled={option.disabled || isDisabled?.(option)}\n className={listStyles.Item}\n >\n <span className={listStyles.ItemText}>{option.label}</span>\n </Combobox.Item>\n )}\n </Combobox.List>\n </>\n );\n}\n\nfunction SingleAutocomplete<Value extends string>({\n options,\n value,\n defaultValue = \"\",\n onValueChange,\n noOptionsText,\n loading,\n name,\n ...field\n}: AutocompleteSingleProps<Value>) {\n const isControlled = value !== undefined;\n const [internal, setInternal] = useState<Value | \"\">(defaultValue);\n const current = isControlled ? value : internal;\n\n const chosen = options.find((option) => option.value === current) ?? null;\n\n return (\n <ComboField\n {...field}\n clearable\n trigger\n selectTextOnClick\n renderRoot={(children) => (\n <Combobox.Root<SelectOption<Value>>\n items={options}\n value={chosen}\n onValueChange={(next) => {\n const nextValue = next?.value ?? \"\";\n if (nextValue === current) return;\n if (!isControlled) setInternal(nextValue);\n onValueChange?.(nextValue);\n }}\n isItemEqualToValue={sameOption}\n name={name}\n disabled={field.disabled}\n required={field.required}\n // The keyboard and the mouse each mark their own option, as in\n // `Select`: hovering does not move the option the arrow keys are on.\n highlightItemOnHover={false}\n >\n {children}\n </Combobox.Root>\n )}\n >\n <OptionList noOptionsText={noOptionsText} loading={loading} />\n </ComboField>\n );\n}\n\nfunction MultipleAutocomplete<Value extends string>({\n options,\n value,\n defaultValue = [],\n onValueChange,\n min = 1,\n max,\n noOptionsText,\n loading,\n name,\n multiple: _multiple,\n ...field\n}: AutocompleteMultipleProps<Value>) {\n const chipLabels = useChipLabels();\n const isControlled = value !== undefined;\n const [internal, setInternal] = useState<Value[]>(defaultValue);\n const selected = isControlled ? value : internal;\n // What is typed to filter the options. Held here to be emptied after a choice.\n const [query, setQuery] = useState(\"\");\n\n // In the order chosen, which is the order of the chips.\n const selectedOptions = selected\n .map((item) => options.find((option) => option.value === item))\n .filter((option) => option !== undefined);\n\n const atMax = max !== undefined && selected.length >= max;\n const atMin = selected.length <= min;\n const canRemove = !field.disabled && !atMin;\n\n return (\n <ComboField\n {...field}\n // Emptying the control would go below `min`, so the button is only\n // there when nothing has to stay chosen.\n clearable={min <= 0}\n trigger\n filled={selectedOptions.length > 0}\n chips={selectedOptions.map((option) => (\n <Combobox.Chip\n key={option.value}\n className={chipStyles.Chip}\n data-color=\"primary\"\n data-variant=\"filled\"\n data-size=\"sm\"\n >\n <span className={chipStyles.Label}>{option.label}</span>\n {canRemove && (\n <Combobox.ChipRemove\n className={chipStyles.Delete}\n aria-label={`${chipLabels.remove} ${option.label}`}\n >\n <CircleX aria-hidden />\n </Combobox.ChipRemove>\n )}\n </Combobox.Chip>\n ))}\n renderRoot={(children) => (\n <Combobox.Root<SelectOption<Value>, true>\n multiple\n items={options}\n value={selectedOptions}\n inputValue={query}\n onInputValueChange={setQuery}\n // A choice leaves the list open, to pick several in a row, and\n // empties the text that was typed to find it. Base UI closes the\n // list instead when there was such text, and a key pressed while it\n // closes brings it back with the options of the old text.\n onOpenChange={(open, details) => {\n if (!open && details.reason === \"item-press\") details.cancel();\n }}\n onValueChange={(next, details) => {\n if (details.reason === \"item-press\") setQuery(\"\");\n const nextValues = next.map((option) => option.value);\n // The options that would break a bound are disabled; this also\n // covers Backspace in the input, which removes the last chip.\n if (nextValues.length < min && nextValues.length < selected.length)\n return;\n if (\n max !== undefined &&\n nextValues.length > max &&\n nextValues.length > selected.length\n )\n return;\n if (!isControlled) setInternal(nextValues);\n onValueChange?.(nextValues);\n }}\n isItemEqualToValue={sameOption}\n name={name}\n disabled={field.disabled}\n required={field.required}\n highlightItemOnHover={false}\n >\n {children}\n </Combobox.Root>\n )}\n >\n <OptionList\n noOptionsText={noOptionsText}\n loading={loading}\n isDisabled={(option) => {\n const isSelected = selected.includes(option.value as Value);\n return (atMax && !isSelected) || (atMin && isSelected);\n }}\n />\n </ComboField>\n );\n}\n\n/**\n * A select to search in, on Base UI's `Combobox`, in the same box as `Input`.\n * Typing filters the options. The label, the helper text and the ids that\n * link them to the input are all rendered and wired for you.\n *\n * ```tsx\n * <Autocomplete\n * label=\"Land\"\n * options={[\n * { value: \"no\", label: \"Norge\" },\n * { value: \"se\", label: \"Sverige\" },\n * ]}\n * value={country}\n * onValueChange={setCountry}\n * />\n * ```\n *\n * Values are strings, typed from the options as in `Select`, and `\"\"` means\n * nothing is chosen. Pass `multiple` to choose several: they show as chips\n * inside the field, bounded by `min` (default 1) and `max`.\n *\n * For an email address with suggested domains, use `EmailInput`.\n */\n// A signature for each mode ahead of the one that takes either: TypeScript\n// then reads the type of the values from `options`, `value` and\n// `onValueChange` together, as it does for `Select` and `MultiSelect`. The\n// last one is for a wrapper that passes on props typed `AutocompleteProps`.\nexport function Autocomplete<Value extends string = string>(\n props: AutocompleteMultipleProps<Value>,\n): ReactElement;\nexport function Autocomplete<Value extends string = string>(\n props: AutocompleteSingleProps<Value>,\n): ReactElement;\nexport function Autocomplete<Value extends string = string>(\n props: AutocompleteProps<Value>,\n): ReactElement;\nexport function Autocomplete<Value extends string = string>(\n props: AutocompleteProps<Value>,\n) {\n return props.multiple ? (\n <MultipleAutocomplete {...props} />\n ) : (\n <SingleAutocomplete {...props} />\n );\n}\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n */\n\n.Checkbox {\n display: flex;\n width: 100%;\n flex-direction: column;\n}\n\n.Box {\n display: inline-flex;\n width: 1.3rem;\n height: 1.3rem;\n box-sizing: border-box;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n padding: 0;\n border: 2px solid var(--lea-palette-border-base);\n margin: 0;\n border-radius: var(--lea-radius-sm);\n background-color: var(--lea-palette-bg-base);\n /* The colour of the tick. */\n color: var(--lea-palette-primary-contrast);\n cursor: pointer;\n transition: background-color var(--lea-duration-normal)\n var(--lea-easing-standard),\n border-color var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n@media (hover: hover) {\n .Box:hover:not([data-checked], [data-disabled]) {\n border-color: var(--lea-palette-border-strong);\n }\n}\n\n.Box[data-checked] {\n border-color: var(--lea-palette-primary-base);\n background-color: var(--lea-palette-primary-base);\n}\n\n.Box:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n.Box[data-disabled] {\n cursor: not-allowed;\n opacity: 0.7;\n}\n\n.Tick {\n display: block;\n width: 100%;\n height: 100%;\n}\n\n/* The tick starts undrawn: the gap in the dash covers the whole path. */\n.Tick path {\n stroke-dasharray: 1;\n stroke-dashoffset: 1;\n transition: stroke-dashoffset var(--lea-duration-emphasis)\n var(--lea-easing-spring);\n}\n\n.Box[data-checked] .Tick path {\n stroke-dashoffset: 0;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Box,\n .Tick path {\n transition-duration: 0.01ms;\n }\n}\n","import styles from \"./checkbox.module.css\";\n\n/** The tick. It is drawn in by a transition when its box becomes checked. */\nexport const checkboxTick = (\n <svg className={styles.Tick} viewBox=\"0 0 24 24\" aria-hidden>\n <path\n d=\"M5 12.5l4.5 4.5L19 7\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={3}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n pathLength={1}\n />\n </svg>\n);\n\n/**\n * Internal. The checkbox box as a picture only, for places where something\n * else is the control: `MultiSelect` shows it inside each option, and the\n * option is what gets picked. It looks exactly like `Checkbox`, which uses\n * the same class on the real control.\n */\nexport const CheckboxBox = ({ checked }: { checked: boolean }) => (\n <span\n className={styles.Box}\n data-checked={checked ? \"\" : undefined}\n aria-hidden\n >\n {checkboxTick}\n </span>\n);\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so these parts render correctly without a provider.\n *\n * The row a `Checkbox` and a `RadioGroup.Item` share: the control, then its\n * label text.\n */\n\n.Row {\n display: flex;\n align-items: center;\n padding-block-end: var(--lea-spacing-1);\n margin: 0;\n color: var(--lea-palette-text-base);\n cursor: pointer;\n font-size: var(--lea-text-body-md-font-size);\n font-weight: var(--lea-text-body-md-font-weight);\n gap: var(--lea-spacing-1-5);\n letter-spacing: var(--lea-text-body-md-letter-spacing);\n line-height: var(--lea-text-body-md-line-height);\n}\n\n/* Base UI marks the control, whether it or its whole group is disabled. */\n.Row:has([data-disabled]) {\n color: var(--lea-palette-text-muted);\n cursor: not-allowed;\n}\n\n/* The asterisk keeps its error colour while the label text stays readable. */\n.Row[data-invalid] .Asterisk {\n color: var(--lea-palette-status-error-base);\n}\n\n.Text {\n min-width: 0;\n}\n","\"use client\";\n\nimport { Checkbox as BaseCheckbox } from \"@base-ui/react/checkbox\";\nimport {\n useId,\n type CSSProperties,\n type FocusEventHandler,\n type ReactNode,\n type Ref,\n} from \"react\";\n\nimport { checkboxTick } from \"../checkbox-box\";\nimport styles from \"../checkbox.module.css\";\nimport rowStyles from \"../choice-row.module.css\";\nimport { FieldHelperText, RequiredAsterisk } from \"../field-parts\";\n\n/**\n * Requires an accessible name so the checkbox is never nameless. Provide a\n * visible `label`, or an `aria-label` / `aria-labelledby` when the design has\n * none (a row in a table, say).\n */\ntype CheckboxAccessibleName =\n | { label: ReactNode; \"aria-label\"?: never; \"aria-labelledby\"?: never }\n | { label?: never; \"aria-label\": string; \"aria-labelledby\"?: never }\n | { label?: never; \"aria-label\"?: never; \"aria-labelledby\": string };\n\nexport type CheckboxProps = CheckboxAccessibleName & {\n /** Whether the box is ticked (controlled). Pair with `onCheckedChange`. */\n checked?: boolean;\n /** Whether the box is ticked when first rendered, for uncontrolled use. */\n defaultChecked?: boolean;\n /** Called with the new state when the box is ticked or cleared. */\n onCheckedChange?: (checked: boolean) => void;\n /** Helper / error text shown below the checkbox and linked via `aria-describedby`. */\n helperText?: ReactNode;\n /** Puts the checkbox and its helper text into the error state. */\n error?: boolean;\n /** Submits the checkbox under this name when inside a form. */\n name?: string;\n /** The value submitted when ticked. The browser default is `\"on\"`. */\n value?: string;\n /** Disables the checkbox. */\n disabled?: boolean;\n /** Marks the checkbox as required and adds the asterisk to the label. */\n required?: boolean;\n /** Id of the checkbox itself, the element with the `checkbox` role. */\n id?: string;\n onBlur?: FocusEventHandler<HTMLElement>;\n onFocus?: FocusEventHandler<HTMLElement>;\n /** Extra ids describing the checkbox, alongside the helper text. */\n \"aria-describedby\"?: string;\n /** Class on the outermost element, for layout. */\n className?: string;\n /** Inline style on the outermost element, for layout. */\n style?: CSSProperties;\n /**\n * The `<input type=\"checkbox\">` that holds the value. It is hidden, but\n * `ref.current.focus()` moves focus to the visible box.\n */\n ref?: Ref<HTMLInputElement>;\n};\n\nconst join = (...parts: (string | false | null | undefined)[]) =>\n parts.filter(Boolean).join(\" \") || undefined;\n\n/**\n * Checkbox with its label, helper text and error state built in, on Base UI's\n * accessible `Checkbox` primitive.\n *\n * Pass `label` for the text next to the box; it is a real `<label>`, so\n * clicking it ticks the box. Without a visible label, pass `aria-label`.\n * `className` and `style` go on the outermost element.\n *\n * ```tsx\n * <Checkbox\n * label=\"Jeg godtar vilkårene\"\n * checked={accepted}\n * onCheckedChange={setAccepted}\n * />\n * ```\n */\nexport function Checkbox({\n label,\n helperText,\n error = false,\n checked,\n defaultChecked,\n onCheckedChange,\n name,\n value,\n disabled = false,\n required = false,\n id,\n onBlur,\n onFocus,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n \"aria-describedby\": ariaDescribedBy,\n className,\n style,\n ref,\n}: CheckboxProps) {\n const helperTextId = useId();\n const hasLabel = label != null;\n const hasHelperText = helperText != null;\n const bare = !hasLabel && !hasHelperText;\n\n const box = (\n <BaseCheckbox.Root\n // A real `<button>`, so the `<label>` around it names it natively: in\n // the server markup, and as one element for `getByLabelText`.\n nativeButton\n render={<button type=\"button\" />}\n id={id}\n inputRef={ref}\n checked={checked}\n defaultChecked={defaultChecked}\n onCheckedChange={(next) => onCheckedChange?.(next)}\n name={name}\n value={value}\n disabled={disabled}\n required={required}\n onBlur={onBlur}\n onFocus={onFocus}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={join(hasHelperText && helperTextId, ariaDescribedBy)}\n aria-invalid={error ? true : undefined}\n // Nothing to lay out around the box, so `className` and `style` land on it.\n className={bare ? join(styles.Box, className) : styles.Box}\n style={bare ? style : undefined}\n >\n {checkboxTick}\n </BaseCheckbox.Root>\n );\n\n if (bare) return box;\n\n return (\n <div className={join(styles.Checkbox, className)} style={style}>\n {hasLabel ? (\n <label className={rowStyles.Row} data-invalid={error ? \"\" : undefined}>\n {box}\n <span className={rowStyles.Text}>\n {label}\n {required && <RequiredAsterisk className={rowStyles.Asterisk} />}\n </span>\n </label>\n ) : (\n box\n )}\n {hasHelperText && (\n <FieldHelperText id={helperTextId} error={error}>\n {helperText}\n </FieldHelperText>\n )}\n </div>\n );\n}\n","// `Country` is owned by the hooks/country module and re-exported from the\n// package root via the hooks barrel. Validators import it from there directly so\n// the validation barrel does not produce an ambiguous duplicate `Country`\n// export at the package root.\n\n/**\n * Result of a validation check. `normalized` holds the canonical form of the\n * input (e.g. digits only, or E.164 phone) when the value is valid.\n */\nexport interface ValidationResult {\n valid: boolean;\n normalized?: string;\n}\n\nexport const invalid = (): ValidationResult => ({ valid: false });\n\nexport const valid = (normalized: string): ValidationResult => ({\n valid: true,\n normalized,\n});\n","import type { Country } from \"../hooks/country/countries\";\nimport { invalid, valid, type ValidationResult } from \"./types\";\n\n/**\n * Luhn (mod-10) checksum over a string of digits.\n */\nfunction passesLuhn(digits: string): boolean {\n let sum = 0;\n let double = digits.length % 2 === 0;\n\n for (const char of digits) {\n let value = Number(char);\n if (double) {\n value *= 2;\n if (value > 9) value -= 9;\n }\n sum += value;\n double = !double;\n }\n\n return sum % 10 === 0;\n}\n\n/**\n * Swedish personnummer. Accepts 10 or 12 digit forms (with or without\n * separators) and validates the trailing Luhn check digit.\n */\nexport function isValidSwedishNationalId(value: string): boolean {\n let digits = value.replace(/\\D/g, \"\");\n\n if (digits.length === 12) {\n digits = digits.slice(2);\n }\n\n if (digits.length !== 10) {\n return false;\n }\n\n const month = Number(digits.slice(2, 4));\n const day = Number(digits.slice(4, 6));\n\n if (month < 1 || month > 12 || day < 1 || day > 31) {\n return false;\n }\n\n return passesLuhn(digits);\n}\n\nconst FINNISH_CHECK_CHARACTERS = \"0123456789ABCDEFHJKLMNPRSTUVWXY\";\n\n/**\n * Finnish henkilötunnus (HETU). Validates structure, date, century marker and\n * the mod-31 check character.\n */\nexport function isValidFinnishNationalId(value: string): boolean {\n const normalized = value.replace(/\\s/g, \"\").toUpperCase();\n\n if (!/^\\d{6}[+\\-A]\\d{3}[0-9A-Y]$/.test(normalized)) {\n return false;\n }\n\n const day = Number(normalized.slice(0, 2));\n const month = Number(normalized.slice(2, 4));\n const yearShort = Number(normalized.slice(4, 6));\n const centurySign = normalized[6];\n\n const centuryBase =\n centurySign === \"+\"\n ? 1800\n : centurySign === \"-\"\n ? 1900\n : centurySign === \"A\"\n ? 2000\n : null;\n\n if (centuryBase === null) {\n return false;\n }\n\n const year = centuryBase + yearShort;\n const date = new Date(year, month - 1, day);\n\n if (\n date.getFullYear() !== year ||\n date.getMonth() !== month - 1 ||\n date.getDate() !== day\n ) {\n return false;\n }\n\n const checksumNumber = Number(\n `${normalized.slice(0, 6)}${normalized.slice(7, 10)}`,\n );\n\n return normalized[10] === FINNISH_CHECK_CHARACTERS[checksumNumber % 31];\n}\n\nconst NO_WEIGHTS_1 = [3, 7, 6, 1, 8, 9, 4, 5, 2];\nconst NO_WEIGHTS_2 = [5, 4, 3, 2, 7, 6, 5, 4, 3, 2];\n\nfunction norwegianControlDigit(digits: number[], weights: number[]): number {\n const sum = weights.reduce((acc, weight, i) => acc + weight * digits[i], 0);\n const remainder = sum % 11;\n return remainder === 0 ? 0 : 11 - remainder;\n}\n\n/**\n * Norwegian fødselsnummer. 11 digits with two mod-11 control digits.\n */\nexport function isValidNorwegianNationalId(value: string): boolean {\n const digits = value.replace(/\\D/g, \"\");\n\n if (digits.length !== 11) {\n return false;\n }\n\n const numbers = digits.split(\"\").map(Number);\n const day = Number(digits.slice(0, 2));\n const month = Number(digits.slice(2, 4));\n\n // Accounts for D-numbers (day + 40) and H-numbers (month + 40).\n const realDay = day > 40 ? day - 40 : day;\n const realMonth = month > 40 ? month - 40 : month;\n\n if (realMonth < 1 || realMonth > 12 || realDay < 1 || realDay > 31) {\n return false;\n }\n\n const k1 = norwegianControlDigit(numbers.slice(0, 9), NO_WEIGHTS_1);\n if (k1 === 11 || k1 !== numbers[9]) {\n return false;\n }\n\n const k2 = norwegianControlDigit(numbers.slice(0, 10), NO_WEIGHTS_2);\n if (k2 === 11 || k2 !== numbers[10]) {\n return false;\n }\n\n return true;\n}\n\nconst SPANISH_DNI_LETTERS = \"TRWAGMYFPDXBNJZSQVHLCKE\";\nconst SPANISH_NIE_PREFIX: Record<string, string> = { X: \"0\", Y: \"1\", Z: \"2\" };\n\n/**\n * Spanish DNI / NIE. Validates the control letter (mod-23). NIE prefixes\n * (X/Y/Z) are mapped to their numeric equivalents before the check.\n */\nexport function isValidSpanishNationalId(value: string): boolean {\n const normalized = value.replace(/[\\s-]/g, \"\").toUpperCase();\n\n if (!/^[XYZ]?\\d{7,8}[A-Z]$/.test(normalized)) {\n return false;\n }\n\n const letter = normalized.slice(-1);\n let numberPart = normalized.slice(0, -1);\n\n const prefix = numberPart[0];\n if (prefix in SPANISH_NIE_PREFIX) {\n numberPart = SPANISH_NIE_PREFIX[prefix] + numberPart.slice(1);\n }\n\n if (numberPart.length !== 8) {\n return false;\n }\n\n return SPANISH_DNI_LETTERS[Number(numberPart) % 23] === letter;\n}\n\nconst NATIONAL_ID_VALIDATORS: Record<Country, (value: string) => boolean> = {\n SE: isValidSwedishNationalId,\n FI: isValidFinnishNationalId,\n NO: isValidNorwegianNationalId,\n ES: isValidSpanishNationalId,\n};\n\n/**\n * Validates a national identity number for the given country.\n */\nexport function isValidNationalId(country: Country, value: string): boolean {\n return NATIONAL_ID_VALIDATORS[country](value);\n}\n\n/**\n * Validates a national identity number and returns the normalized (uppercased,\n * separator-free) value when valid.\n */\nexport function validateNationalId(\n country: Country,\n value: string,\n): ValidationResult {\n if (!isValidNationalId(country, value)) {\n return invalid();\n }\n\n return valid(value.replace(/[\\s-]/g, \"\").toUpperCase());\n}\n\n/**\n * The length of a national identity number once {@link normalizeNationalId}\n * has removed its formatting. Swedish numbers are 12 digits: the form with the\n * full year of birth (`YYYYMMDDNNNN`). The 10-digit form passes\n * {@link isValidNationalId} but is 10 long here, so a form that needs the\n * full year can ask for it.\n */\nexport const NATIONAL_ID_LENGTH: Readonly<Record<Country, number>> = {\n SE: 12,\n FI: 11,\n NO: 11,\n ES: 9,\n};\n\n// Hyphen, non-breaking hyphen, figure dash, en dash, em dash, horizontal bar\n// and minus sign. Phones and pasted text turn a typed \"-\" into one of these.\nconst DASHES = /[‐-―−]/g;\n\nconst NATIONAL_ID_NORMALIZERS: Record<Country, (value: string) => string> = {\n // Digits only: separators, the \"+\" of the 10-digit form and stray dots go.\n SE: (value) => value.replace(/\\D/g, \"\"),\n // The century sign (`+`, `-`, `A`…) and the check character are part of\n // the number, so only what cannot be part of it goes.\n FI: (value) =>\n value\n .replace(DASHES, \"-\")\n .toUpperCase()\n .replace(/[^0-9A-Z+-]/g, \"\"),\n NO: (value) => value.replace(/\\D/g, \"\"),\n ES: (value) => value.toUpperCase().replace(/[^0-9A-Z]/g, \"\"),\n};\n\n/**\n * Removes the formatting people type or paste into a national identity\n * number (spaces, separators, dashes of every kind) and keeps what the\n * checksum needs. It does not validate: pass the result to\n * {@link isValidNationalId}, and compare its length with\n * {@link NATIONAL_ID_LENGTH}.\n *\n * @example\n * normalizeNationalId(\"SE\", \"19811218–9876\") // → \"198112189876\"\n * normalizeNationalId(\"FI\", \"131052-308t\") // → \"131052-308T\"\n */\nexport function normalizeNationalId(country: Country, value: string): string {\n return NATIONAL_ID_NORMALIZERS[country](value);\n}\n\n/**\n * Whether two national identity numbers are the same person's, as for \"the\n * co-applicant must not be the main applicant\". Both are normalised first,\n * and both must be valid: an empty or invalid number never matches.\n */\nexport function isSameNationalId(\n country: Country,\n a: string | null | undefined,\n b: string | null | undefined,\n): boolean {\n const first = normalizeNationalId(country, a ?? \"\");\n const second = normalizeNationalId(country, b ?? \"\");\n\n return first !== \"\" && first === second && isValidNationalId(country, first);\n}\n","// Generated by scripts/generate-mobile-patterns.mjs from libphonenumber-js\n// 1.13.10 (\"mobile\" metadata). Do not edit by hand.\n\n/**\n * The national number of a mobile phone, per country. A number is a mobile\n * when the whole national number matches.\n */\nexport const MOBILE_PATTERNS = {\n SE: \"7[023689]\\\\d{7}\",\n FI: \"4946\\\\d{2,6}|(?:4[0-8]|50)\\\\d{4,8}\",\n NO: \"(?:4[015-9]|9\\\\d)\\\\d{6}\",\n ES: \"96906(?:09|10)\\\\d\\\\d|(?:590(?:10[0-2]|600)|97390\\\\d)\\\\d{3}|(?:6\\\\d|7[1-48])\\\\d{7}\",\n} as const;\n","import {\n parsePhoneNumberFromString,\n type CountryCode,\n} from \"libphonenumber-js\";\nimport { MOBILE_PATTERNS } from \"./mobile-patterns\";\nimport { invalid, valid, type ValidationResult } from \"./types\";\n\nexport type { CountryCode };\n\n/**\n * Validates a phone number for the given country, delegating to\n * `libphonenumber-js` so every country's rules stay accurate and maintained.\n *\n * Accepts national format (with or without a leading trunk `0`) and\n * international format (`+CC` / `00CC`). On success, `normalized` holds the\n * canonical E.164 form (e.g. `+358401234567`).\n */\nexport function validatePhoneNumber(\n country: CountryCode,\n value: string,\n): ValidationResult {\n // Strip all whitespace so the display grouping (e.g. \"400 12 345\") never\n // reaches the parser; validation runs on the bare number.\n const cleaned = value.replace(/\\s/g, \"\");\n if (cleaned === \"\") {\n return invalid();\n }\n\n const parsed = parsePhoneNumberFromString(cleaned, country);\n if (!parsed || !parsed.isValid()) {\n return invalid();\n }\n\n return valid(parsed.number);\n}\n\n/**\n * Boolean convenience wrapper around {@link validatePhoneNumber}.\n */\nexport function isValidPhoneNumber(\n country: CountryCode,\n value: string,\n): boolean {\n return validatePhoneNumber(country, value).valid;\n}\n\n/**\n * The countries {@link validateMobileNumber} knows mobile numbers for: Lea\n * Bank's markets.\n */\nexport type MobileCountry = keyof typeof MOBILE_PATTERNS;\n\nconst MOBILE_REGEXPS = Object.fromEntries(\n Object.entries(MOBILE_PATTERNS).map(([country, pattern]) => [\n country,\n new RegExp(`^(?:${pattern})$`),\n ]),\n) as Record<MobileCountry, RegExp>;\n\n/**\n * Whether {@link validateMobileNumber} can tell a mobile number from a\n * landline in this country.\n */\nexport function isMobileCountry(country: string): country is MobileCountry {\n return Object.prototype.hasOwnProperty.call(MOBILE_PATTERNS, country);\n}\n\n/**\n * Validates a mobile phone number of the given country, as\n * {@link validatePhoneNumber} does, and also rejects:\n *\n * - landlines and other numbers that are not a mobile (`08-123 456 78`), and\n * - a valid number of another country (`+47 412 34 567` for `\"SE\"`).\n *\n * Mobile numbers come from libphonenumber-js's own metadata\n * (`scripts/generate-mobile-patterns.mjs`). On success, `normalized` holds\n * the E.164 form (`+46701234567`).\n */\nexport function validateMobileNumber(\n country: MobileCountry,\n value: string,\n): ValidationResult {\n const cleaned = value.replace(/\\s/g, \"\");\n if (cleaned === \"\") {\n return invalid();\n }\n\n const parsed = parsePhoneNumberFromString(cleaned, country);\n if (\n !parsed ||\n !parsed.isValid() ||\n parsed.country !== country ||\n !MOBILE_REGEXPS[country].test(parsed.nationalNumber)\n ) {\n return invalid();\n }\n\n return valid(parsed.number);\n}\n\n/**\n * Whether two phone numbers are the same number, as for \"the co-applicant\n * must not be the main applicant\". Both are read as numbers of `country`\n * unless they carry a country code (`+46…`, `0046…`), and both must be\n * valid: an empty or invalid number never matches.\n */\nexport function isSamePhone(\n country: CountryCode,\n a: string | null | undefined,\n b: string | null | undefined,\n): boolean {\n const first = validatePhoneNumber(country, a ?? \"\");\n const second = validatePhoneNumber(country, b ?? \"\");\n\n return first.valid && second.valid && first.normalized === second.normalized;\n}\n","export const modulo97 = (digits: string): number => {\n let remainder = 0;\n\n for (const digit of digits) {\n remainder = (remainder * 10 + Number(digit)) % 97;\n }\n\n return remainder;\n};\n\nexport const replaceLatinLettersWithNumbers = (value: string): string =>\n value.replace(/[A-Z]/g, (char) => String(char.charCodeAt(0) - 55));\n","import type { Country } from \"../hooks/country/countries\";\nimport { modulo97, replaceLatinLettersWithNumbers } from \"./modulo-97\";\nimport { invalid, valid, type ValidationResult } from \"./types\";\n\nconst IBAN_LENGTHS: Record<Country, number> = {\n SE: 24,\n FI: 18,\n NO: 15,\n ES: 24,\n};\n\nfunction normalize(value: string): string {\n return value.replace(/[\\s-]/g, \"\").toUpperCase();\n}\n\n/**\n * Generic IBAN check: structure, country length and the ISO 7064 mod-97\n * checksum.\n */\nexport function isValidIban(value: string, country?: Country): boolean {\n const iban = normalize(value);\n\n if (!/^[A-Z]{2}\\d{2}[A-Z0-9]+$/.test(iban)) {\n return false;\n }\n\n const ibanCountry = iban.slice(0, 2) as Country;\n\n if (country && ibanCountry !== country) {\n return false;\n }\n\n const expectedLength = IBAN_LENGTHS[ibanCountry];\n if (expectedLength && iban.length !== expectedLength) {\n return false;\n }\n\n const rearranged = iban.slice(4) + iban.slice(0, 4);\n return modulo97(replaceLatinLettersWithNumbers(rearranged)) === 1;\n}\n\nconst NO_ACCOUNT_WEIGHTS = [5, 4, 3, 2, 7, 6, 5, 4, 3, 2];\n\n/**\n * Norwegian kontonummer: 11 digits with a mod-11 control digit.\n */\nexport function isValidNorwegianAccount(value: string): boolean {\n const digits = value.replace(/\\D/g, \"\");\n\n if (digits.length !== 11) {\n return false;\n }\n\n const numbers = digits.split(\"\").map(Number);\n const sum = NO_ACCOUNT_WEIGHTS.reduce(\n (acc, weight, i) => acc + weight * numbers[i],\n 0,\n );\n const remainder = sum % 11;\n const control = remainder === 0 ? 0 : 11 - remainder;\n\n return control < 10 && control === numbers[10];\n}\n\n/**\n * Swedish clearing + account number. Bank-specific checksums vary, so this is a\n * structural check: a 4-5 digit clearing number followed by a 6-10 digit\n * account number.\n */\nexport function isValidSwedishAccount(value: string): boolean {\n const digits = value.replace(/\\D/g, \"\");\n return digits.length >= 10 && digits.length <= 15;\n}\n\n/**\n * Validates a bank account number for the given country.\n *\n * - **FI / ES** require a valid IBAN.\n * - **NO** accepts a domestic kontonummer or a Norwegian IBAN.\n * - **SE** accepts a domestic clearing + account number or a Swedish IBAN.\n */\nexport function validateBankAccount(\n country: Country,\n value: string,\n): ValidationResult {\n const normalized = normalize(value);\n const looksLikeIban = /^[A-Z]{2}\\d{2}/.test(normalized);\n\n switch (country) {\n case \"FI\":\n case \"ES\":\n return isValidIban(normalized, country) ? valid(normalized) : invalid();\n case \"NO\":\n if (looksLikeIban) {\n return isValidIban(normalized, \"NO\") ? valid(normalized) : invalid();\n }\n return isValidNorwegianAccount(normalized)\n ? valid(normalized.replace(/\\D/g, \"\"))\n : invalid();\n case \"SE\":\n if (looksLikeIban) {\n return isValidIban(normalized, \"SE\") ? valid(normalized) : invalid();\n }\n return isValidSwedishAccount(normalized)\n ? valid(normalized.replace(/\\D/g, \"\"))\n : invalid();\n }\n}\n\n/**\n * Boolean convenience wrapper around {@link validateBankAccount}.\n */\nexport function isValidBankAccount(country: Country, value: string): boolean {\n return validateBankAccount(country, value).valid;\n}\n","import { modulo97 } from \"./modulo-97\";\nimport { invalid, valid, type ValidationResult } from \"./types\";\n\nconst normalize = (value: string): string =>\n value.replace(/\\s/g, \"\").toUpperCase();\n\n/** Finnish domestic reference: 3–19 base digits and a 7-3-1 check digit. */\nexport function isValidFinnishDomesticReference(value: string): boolean {\n const reference = normalize(value);\n if (!/^[1-9]\\d{3,19}$/.test(reference)) {\n return false;\n }\n\n const base = reference.slice(0, -1);\n const checkDigit = Number(reference.at(-1));\n const weights = [7, 3, 1];\n let sum = 0;\n\n for (let index = base.length - 1, weightIndex = 0; index >= 0; index--) {\n sum += Number(base[index]) * weights[weightIndex % weights.length];\n weightIndex++;\n }\n\n return (10 - (sum % 10)) % 10 === checkDigit;\n}\n\n/** ISO 11649 RF reference using Finland's 4–20 digit reference length. */\nexport function isValidFinnishRfReference(value: string): boolean {\n const reference = normalize(value);\n if (!/^RF\\d{2}[1-9]\\d{3,19}$/.test(reference)) {\n return false;\n }\n\n const rearranged = `${reference.slice(4)}2715${reference.slice(2, 4)}`;\n return modulo97(rearranged) === 1;\n}\n\n/** Validates either a Finnish domestic or ISO 11649 RF reference. */\nexport function validateFinnishReference(value: string): ValidationResult {\n const normalized = normalize(value);\n\n return isValidFinnishDomesticReference(normalized) ||\n isValidFinnishRfReference(normalized)\n ? valid(normalized)\n : invalid();\n}\n\n/** Boolean convenience wrapper around {@link validateFinnishReference}. */\nexport function isValidFinnishReference(value: string): boolean {\n return validateFinnishReference(value).valid;\n}\n","import { invalid, valid, type ValidationResult } from \"./types\";\n\n// Pragmatic RFC-5322-ish pattern: good enough for UI validation without the\n// false negatives of an overly strict expression.\nconst EMAIL_PATTERN =\n /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+$/;\n\n/**\n * Common consumer email domains across the Nordics and Spain, used to power the\n * `@`-triggered autocomplete dropdown.\n */\nexport const COMMON_EMAIL_DOMAINS: readonly string[] = [\n \"gmail.com\",\n \"outlook.com\",\n \"hotmail.com\",\n \"icloud.com\",\n \"yahoo.com\",\n \"live.com\",\n \"live.se\",\n \"live.no\",\n \"hotmail.se\",\n \"hotmail.no\",\n \"telia.com\",\n \"online.no\",\n \"telenor.no\",\n \"elisanet.fi\",\n \"suomi24.fi\",\n \"telia.se\",\n \"comhem.se\",\n \"kolumbus.fi\",\n \"pp.inet.fi\",\n \"luukku.com\",\n \"me.com\",\n \"msn.com\",\n \"gmx.com\",\n \"yahoo.se\",\n \"yahoo.no\",\n];\n\n/**\n * Validates an email address and returns the lowercased value when valid.\n */\nexport function validateEmail(value: string): ValidationResult {\n const trimmed = value.trim();\n\n if (!EMAIL_PATTERN.test(trimmed)) {\n return invalid();\n }\n\n const atIndex = trimmed.lastIndexOf(\"@\");\n if (atIndex >= 0) {\n const domain = trimmed.slice(atIndex + 1).toLowerCase();\n if (domain.endsWith(\".con\")) {\n return invalid();\n }\n }\n\n return valid(trimmed.toLowerCase());\n}\n\n/**\n * Boolean convenience wrapper around {@link validateEmail}.\n */\nexport function isValidEmail(value: string): boolean {\n return validateEmail(value).valid;\n}\n\n/**\n * Given the current email input, returns a list of completed email suggestions\n * based on common domains. Suggestions only appear once an `@` has been typed,\n * and are filtered by whatever partial domain follows it.\n *\n * @example\n * getEmailDomainSuggestions(\"alice@\") // → [\"alice@gmail.com\", ...]\n * getEmailDomainSuggestions(\"alice@gm\") // → [\"alice@gmail.com\"]\n */\nexport function getEmailDomainSuggestions(\n value: string,\n domains: readonly string[] = COMMON_EMAIL_DOMAINS,\n): string[] {\n const atIndex = value.indexOf(\"@\");\n\n if (atIndex < 1) {\n return [];\n }\n\n const localPart = value.slice(0, atIndex);\n const domainPart = value.slice(atIndex + 1).toLowerCase();\n\n // Don't suggest once a complete domain has been typed.\n if (domainPart.includes(\".\") && domains.includes(domainPart)) {\n return [];\n }\n\n return domains\n .filter((domain) => domain.startsWith(domainPart))\n .map((domain) => `${localPart}@${domain}`);\n}\n\n/**\n * Whether two email addresses are the same, ignoring case and surrounding\n * spaces, as for \"the co-applicant must not be the main applicant\". An empty\n * address never matches.\n */\nexport function isSameEmail(\n a: string | null | undefined,\n b: string | null | undefined,\n): boolean {\n const first = (a ?? \"\").trim().toLowerCase();\n const second = (b ?? \"\").trim().toLowerCase();\n\n return first !== \"\" && first === second;\n}\n\n/*\n * Typo suggestions, the approach of Mailcheck (github.com/mailcheck/mailcheck),\n * written here with our own lists: compare the domain with known domains and,\n * failing that, the top-level domain with known ones.\n */\n\n// Real domains a typo or two away from a common one. They are never\n// \"corrected\": anna@mail.com is not a typo of gmail.com.\nconst REAL_LOOKALIKES = [\n \"mail.com\",\n \"email.com\",\n \"ymail.com\",\n \"googlemail.com\",\n \"gmx.net\",\n \"gmx.de\",\n \"gmx.se\",\n \"aol.com\",\n \"mac.com\",\n \"live.dk\",\n \"live.fi\",\n \"hotmail.dk\",\n \"hotmail.fi\",\n \"hotmail.es\",\n \"outlook.es\",\n \"yahoo.es\",\n \"yahoo.dk\",\n \"tele2.se\",\n \"telenor.se\",\n \"telenor.com\",\n \"spray.se\",\n \"bredband.net\",\n];\n\n// Top-level domains people use for email. One that is not here and is one\n// typo from exactly one that is gets corrected (\"con\" → \"com\"), so the list\n// errs on the long side: a real one missing from it could be \"corrected\".\nconst TOP_LEVEL_DOMAINS = [\n \"com\",\n \"net\",\n \"org\",\n \"se\",\n \"no\",\n \"fi\",\n \"es\",\n \"nu\",\n \"dk\",\n \"is\",\n \"ax\",\n \"fo\",\n \"gl\",\n \"de\",\n \"eu\",\n \"uk\",\n \"co.uk\",\n \"fr\",\n \"it\",\n \"nl\",\n \"be\",\n \"at\",\n \"ch\",\n \"pl\",\n \"ee\",\n \"lv\",\n \"lt\",\n \"pt\",\n \"ie\",\n \"us\",\n \"ca\",\n \"info\",\n \"biz\",\n \"edu\",\n \"gov\",\n \"io\",\n \"me\",\n \"app\",\n \"dev\",\n];\n\n/** Edits (insert, delete, replace, swap two neighbours) from `a` to `b`. */\nfunction editDistance(a: string, b: string): number {\n const d = Array.from({ length: a.length + 1 }, (_, i) =>\n Array.from({ length: b.length + 1 }, (_, j) =>\n i === 0 ? j : j === 0 ? i : 0,\n ),\n );\n for (let i = 1; i <= a.length; i++) {\n for (let j = 1; j <= b.length; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n d[i][j] = Math.min(\n d[i - 1][j] + 1,\n d[i][j - 1] + 1,\n d[i - 1][j - 1] + cost,\n );\n if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {\n d[i][j] = Math.min(d[i][j], d[i - 2][j - 2] + 1);\n }\n }\n }\n return d[a.length][b.length];\n}\n\n/** The one candidate within `limit` edits, nearest first. A tie is no answer. */\nfunction closest(\n value: string,\n candidates: readonly string[],\n limit: number,\n): string | undefined {\n let best: string | undefined;\n let bestDistance = limit + 1;\n let tie = false;\n for (const candidate of candidates) {\n const distance = editDistance(value, candidate);\n if (distance < bestDistance) {\n [best, bestDistance, tie] = [candidate, distance, false];\n } else if (distance === bestDistance && candidate !== best) {\n tie = true;\n }\n }\n return tie ? undefined : best;\n}\n\n/**\n * Suggests a correction for a mistyped email domain, or `undefined` when there\n * is nothing to suggest. It never changes a domain it knows, nor a domain that\n * is merely uncommon (`anna@firma.se` stays).\n *\n * @example\n * suggestEmailCorrection(\"anna@gnail.con\") // → \"anna@gmail.com\"\n * suggestEmailCorrection(\"anna@hotmial.com\") // → \"anna@hotmail.com\"\n * suggestEmailCorrection(\"anna@firma.con\") // → \"anna@firma.com\"\n * suggestEmailCorrection(\"anna@firma.se\") // → undefined\n */\nexport function suggestEmailCorrection(\n value: string,\n domains: readonly string[] = COMMON_EMAIL_DOMAINS,\n): string | undefined {\n const trimmed = value.trim();\n const at = trimmed.lastIndexOf(\"@\");\n if (at < 1) return undefined;\n\n const local = trimmed.slice(0, at);\n const domain = trimmed\n .slice(at + 1)\n .toLowerCase()\n .replace(/\\.+$/, \"\");\n if (!/^[a-z0-9-]+(\\.[a-z0-9-]+)*$/.test(domain)) return undefined;\n\n const known = [...domains, ...REAL_LOOKALIKES];\n if (known.includes(domain)) return undefined;\n\n const suggest = (fixed: string | undefined) =>\n fixed && fixed !== domain && !REAL_LOOKALIKES.includes(fixed)\n ? `${local}@${fixed}`\n : undefined;\n const nameOf = (entry: string) => entry.slice(0, entry.indexOf(\".\"));\n const tldOf = (entry: string) => entry.slice(entry.indexOf(\".\") + 1);\n\n const dot = domain.indexOf(\".\");\n\n // No top-level domain at all: \"anna@gmail\", \"anna@hotmial\".\n if (dot < 0) {\n const names = [...new Set(domains.map(nameOf))];\n const name = closest(domain, names, domain.length >= 6 ? 2 : 1);\n return suggest(domains.find((entry) => nameOf(entry) === name));\n }\n\n const name = domain.slice(0, dot);\n const tld = domain.slice(dot + 1);\n\n // A real top-level domain: only a typo in the name, of one edit, against\n // the known domains with that top-level domain: \"hotmial.com\". A name with\n // a different real top-level domain (online.de) is somebody else's.\n if (TOP_LEVEL_DOMAINS.includes(tld)) {\n const sameTld = known.filter((entry) => tldOf(entry) === tld);\n return suggest(closest(domain, sameTld, 1));\n }\n\n // A top-level domain that does not exist: a known domain one typo away, or\n // two in a longer one (\"gnail.con\"), or else the nearest real top-level\n // domain (\"firma.con\").\n const near = closest(domain, known, domain.length >= 9 ? 2 : 1);\n if (near) return suggest(near);\n\n const fixedTld = closest(tld, TOP_LEVEL_DOMAINS, 1);\n return fixedTld ? suggest(`${name}.${fixedTld}`) : undefined;\n}\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n */\n\n.Link {\n --link-color: var(--lea-palette-secondary-base);\n\n color: var(--link-color);\n /* Size, line height and family come from the surrounding text. */\n font-weight: var(--lea-font-weight-regular);\n /*\n * Underlined at rest: in a sentence the colour alone does not set a link\n * apart from the words around it, least of all `secondary`, which is the\n * colour of body text.\n */\n text-decoration: underline;\n text-decoration-thickness: 0.0625rem;\n /* Clears the descenders of the font; there is no token for it. */\n text-underline-offset: 0.3rem;\n cursor: pointer;\n}\n\n/*\n * The brand colours as text: a step of the same scale dark enough to read at\n * body size. `tertiary.base` is too light for text on white.\n */\n.Link[data-color=\"primary\"] {\n --link-color: var(--lea-color-purple-700);\n}\n\n.Link[data-color=\"tertiary\"] {\n --link-color: var(--lea-color-teal-700);\n}\n\n.Link[data-color=\"contrast\"] {\n --link-color: var(--lea-palette-text-contrast);\n}\n\n/* `render={<button type=\"button\" />}`: an action that looks like a link. */\nbutton.Link {\n padding: 0;\n border: 0;\n appearance: none;\n background: none;\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n letter-spacing: inherit;\n text-align: inherit;\n}\n\n.Link:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n/*\n * `arrow`: a call to action of its own, as the website's CTA links. The\n * chevron is 24px whatever the text size, 4px after the text.\n */\n.Link[data-arrow] {\n --link-hover-color: var(--lea-palette-primary-base);\n\n display: inline-flex;\n align-items: center;\n gap: var(--lea-spacing-0-5);\n transition: color var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n/*\n * On a dark surface the hover is light purple. Plain links keep their colour\n * on hover: on a status or brand fill (a filled `Alert`, say) a purple hover\n * would drop below 4.5:1, so it is only given to links with the arrow, which\n * sit on the website's white, grey, beige, black and deep purple surfaces.\n */\n.Link[data-arrow][data-color=\"contrast\"] {\n --link-hover-color: var(--lea-palette-text-contrast-hover);\n}\n\n.Link[data-arrow][data-color=\"primary\"],\n.Link[data-arrow][data-color=\"tertiary\"] {\n --link-hover-color: var(--link-color);\n}\n\n.Text {\n min-width: 0;\n}\n\n.Arrow {\n width: var(--lea-spacing-3);\n height: var(--lea-spacing-3);\n flex-shrink: 0;\n transition: translate var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n/* Hover thickens the line, from 1px to 2px, without moving the text. */\n@media (hover: hover) {\n .Link:hover {\n text-decoration-thickness: 0.125rem;\n }\n\n .Link[data-arrow]:hover {\n color: var(--link-hover-color);\n }\n\n .Link[data-arrow]:hover .Arrow {\n translate: var(--lea-spacing-1) 0;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Link[data-arrow],\n .Arrow {\n transition-duration: 0.01ms;\n }\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { ChevronRight } from \"lucide-react\";\nimport type { Ref } from \"react\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport type { ColorRoles } from \"../../color-roles\";\nimport styles from \"./link.module.css\";\n\nexport type LinkColor = ColorRoles<\n \"secondary\" | \"primary\" | \"tertiary\" | \"contrast\"\n>;\n\nexport type LinkProps = Omit<useRender.ComponentProps<\"a\">, \"ref\" | \"color\"> & {\n /**\n * The colour of the link: `secondary` is the near-black of body text,\n * `primary` the brand purple, `tertiary` the brand teal. Use `contrast`\n * (white) on a dark surface.\n * @default \"secondary\"\n */\n color?: LinkColor;\n /**\n * Adds a chevron after the text, for a link that stands on its own as a call\n * to action, as on the website. On hover the chevron slides to the right and\n * the text turns purple (`secondary`) or light purple (`contrast`). Checked\n * for contrast on white, `bg.subtle`, `bg.muted` and the status tints\n * (`secondary`), and on `bg.contrast` and `bg.contrast-accent` (`contrast`).\n * @default false\n */\n arrow?: boolean;\n /** The rendered element: the `<a>`, or the element passed to `render`. */\n ref?: Ref<HTMLElement>;\n};\n\n/**\n * A link inside running text: underlined, so it is told apart from the words\n * around it by more than its colour, and in the size and line height of the\n * text around it. Renders an `a`; pass a router link\n * through `render`:\n *\n * ```tsx\n * <Link href=\"/om-oss\">Om oss</Link>\n * <Link render={<NextLink href=\"/om-oss\" />}>Om oss</Link>\n * <Link render={<RouterLink to=\"/om-oss\" />}>Om oss</Link>\n * ```\n *\n * For an action that only looks like a link, render a button:\n * `<Link render={<button type=\"button\" />} onClick={…}>`. For a link that\n * looks like a button, use `Button` with `render`.\n *\n * With `arrow`, the link is a call to action of its own, sized by the text\n * around it: `<Text textStyle=\"body-lg\"><Link arrow href=\"/lan\">Se alle\n * lån</Link></Text>`.\n */\nexport function Link({\n color = \"secondary\",\n arrow = false,\n render,\n ref,\n className,\n ...rest\n}: LinkProps) {\n return useRender({\n render,\n ref,\n defaultTagName: \"a\",\n props: {\n ...rest,\n \"data-color\": color,\n \"data-arrow\": arrow || undefined,\n className: mergeClassName(styles.Link, className)({}),\n ...(arrow && {\n children: (\n <>\n <span className={styles.Text}>{rest.children}</span>\n <ChevronRight aria-hidden className={styles.Arrow} />\n </>\n ),\n }),\n },\n });\n}\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n *\n * `--_gap` arrives inline from the `gap` prop of the root and of each item.\n */\n\n.Root {\n display: flex;\n flex-direction: column;\n gap: calc(var(--lea-spacing) * var(--_gap));\n padding: 0;\n margin: 0;\n list-style: none;\n counter-reset: list-counter;\n}\n\n.Item {\n display: flex;\n align-items: flex-start;\n gap: calc(var(--lea-spacing) * var(--_gap));\n counter-increment: list-counter;\n}\n\n/* As tall as one line of body text, so the marker centres on the first line\n of the item instead of on the whole block. */\n.Marker {\n display: flex;\n min-width: var(--lea-spacing-3);\n height: calc(\n var(--lea-text-body-md-font-size) * var(--lea-text-body-md-line-height)\n );\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n}\n\n.Marker[data-marker=\"dot\"]::before {\n width: 0.375rem;\n height: 0.375rem;\n border-radius: var(--lea-radius-full);\n background-color: var(--lea-palette-text-base);\n content: \"\";\n}\n\n/* heading-xs's size and weight on a line as tall as the digit, so the number\n centres in the marker like the dot does. */\n.Marker[data-marker=\"number\"] {\n color: var(--lea-palette-text-base);\n font-size: var(--lea-font-step-0);\n font-weight: var(--lea-font-weight-semibold);\n line-height: var(--lea-line-height-none);\n}\n\n.Marker[data-marker=\"number\"]::before {\n content: counter(list-counter) \".\";\n}\n\n.Content {\n min-width: 0;\n flex: 1;\n}\n","\"use client\";\n\nimport {\n type ComponentProps,\n type CSSProperties,\n type ReactNode,\n createContext,\n useContext,\n} from \"react\";\n\nimport styles from \"./lists.module.css\";\nimport { Text } from \"../text/text\";\n\n/**\n * List is a composition component for rendering ordered or unordered lists.\n *\n * `List.Root` renders the wrapping `<ul>` (default) or `<ol>` element via the\n * `variant` prop. Each `List.Item` shows a marker: a dot for `ul`, an\n * auto-incrementing number for `ol`, or a custom icon when one is passed.\n *\n * @example Unordered list with default dots\n * <List.Root>\n * <List.Item>Fast response</List.Item>\n * <List.Item>Account data report</List.Item>\n * </List.Root>\n *\n * @example Ordered list with auto-numbering\n * <List.Root variant=\"ol\">\n * <List.Item>First step</List.Item>\n * <List.Item>Second step</List.Item>\n * </List.Root>\n *\n * @example Per-item icon marker\n * <List.Root>\n * <List.Item icon={<CircleCheck size={20} />}>Verified</List.Item>\n * </List.Root>\n */\n\ntype ListVariant = \"ul\" | \"ol\";\n\ninterface ListContextValue {\n variant: ListVariant;\n}\n\nconst ListContext = createContext<ListContextValue | null>(null);\n\nconst useListContext = () => {\n const context = useContext(ListContext);\n if (!context) {\n throw new Error(\"List parts must be used within <List.Root>\");\n }\n return context;\n};\n\nconst joinClassNames = (...names: (string | undefined)[]) =>\n names.filter(Boolean).join(\" \");\n\ntype RootProps = Omit<ComponentProps<\"ul\">, \"ref\"> & {\n /**\n * The type of list to render.\n * @default \"ul\"\n */\n variant?: ListVariant;\n /**\n * The gap between the list items, as a multiple of the spacing step.\n * @default 1.5\n */\n gap?: number;\n};\n\nexport const Root = ({\n variant = \"ul\",\n gap = 1.5,\n className,\n style,\n children,\n ...rest\n}: RootProps) => {\n const Element = variant;\n\n return (\n <ListContext.Provider value={{ variant }}>\n <Element\n {...rest}\n className={joinClassNames(styles.Root, className)}\n style={{ \"--_gap\": gap, ...style } as CSSProperties}\n >\n {children}\n </Element>\n </ListContext.Provider>\n );\n};\n\ntype ItemProps = Omit<ComponentProps<\"li\">, \"ref\"> & {\n /** Custom marker rendered in place of the default dot/number. */\n icon?: ReactNode;\n /**\n * The gap between a custom `icon` and the text, as a multiple of the spacing\n * step. The dot and the number keep their own spacing.\n * @default 1.5\n */\n gap?: number;\n};\n\nexport const Item = ({\n icon,\n gap = 1.5,\n className,\n style,\n children,\n ...rest\n}: ItemProps) => {\n const { variant } = useListContext();\n const marker = icon ? \"icon\" : variant === \"ol\" ? \"number\" : \"dot\";\n\n return (\n <li\n {...rest}\n className={joinClassNames(styles.Item, className)}\n style={{ \"--_gap\": icon ? gap : 0, ...style } as CSSProperties}\n >\n <span className={styles.Marker} data-marker={marker}>\n {icon}\n </span>\n <div className={styles.Content}>\n {typeof children === \"string\" ? <Text>{children}</Text> : children}\n </div>\n </li>\n );\n};\n","/*\n * The \"Did you mean …?\" line under the field. It sits where the helper text\n * does and is set like it (field-parts.module.css), so the two read as one\n * column. The address in it is a `Link` rendered as a button.\n */\n\n.Suggestion {\n /* Present even when empty: a live region only announces changes to an\n element that is already in the page. Empty, it takes no room. */\n margin: 0;\n}\n\n.Suggestion p {\n /* 14px, where the text inside a field starts. */\n margin-inline: calc(var(--lea-spacing) * 1.75);\n margin-block: var(--lea-spacing-0-5) 0;\n color: var(--lea-color-neutral-600);\n font-size: var(--lea-text-body-sm-font-size);\n font-weight: var(--lea-text-body-sm-font-weight);\n letter-spacing: var(--lea-text-body-sm-letter-spacing);\n line-height: var(--lea-text-body-sm-line-height);\n overflow-wrap: anywhere;\n}\n","\"use client\";\n\nimport { Autocomplete as BaseAutocomplete } from \"@base-ui/react/autocomplete\";\nimport { useMemo, useRef, useState } from \"react\";\n\nimport { createTranslation } from \"../../../i18n\";\nimport {\n COMMON_EMAIL_DOMAINS,\n getEmailDomainSuggestions,\n suggestEmailCorrection,\n} from \"../../../validation\";\nimport { Link } from \"../../typography\";\nimport { ComboField, type ComboFieldBaseProps } from \"../combo-field\";\nimport styles from \"./email-input.module.css\";\nimport listStyles from \"../option-list.module.css\";\n\nexport type EmailInputProps = ComboFieldBaseProps & {\n /** The address (controlled). Pair with `onValueChange`. */\n value?: string;\n /** The address when first rendered, for uncontrolled use. */\n defaultValue?: string;\n /** Called with the new text on every change, and with a suggestion when one is picked. */\n onValueChange?: (value: string) => void;\n /** Domains offered once the user types `@`. Defaults to common providers. */\n domains?: readonly string[];\n /** Most suggestions shown at once. @default 6 */\n maxSuggestions?: number;\n /**\n * When focus leaves the field, offers a fix for a mistyped domain below it:\n * \"Mente du anna@gmail.com?\" for `anna@gnail.con`, with the address as a\n * button that puts it in the field. The value never changes by itself, and\n * validation is not affected. See `suggestEmailCorrection`.\n * @default true\n */\n suggestCorrection?: boolean;\n /**\n * What the browser may fill in. The default lets it offer the user's own\n * address; pass `\"off\"` when the address is somebody else's.\n * @default \"email\"\n */\n autoComplete?: string;\n};\n\n/**\n * Email text field that suggests common domains in a list once the user\n * types `@`, on Base UI's `Autocomplete`, in the same box as `Input`. The\n * text is the value: a suggestion only saves typing.\n *\n * ```tsx\n * <EmailInput label=\"E-post\" value={email} onValueChange={setEmail} />\n * ```\n */\nexport function EmailInput({\n value,\n defaultValue = \"\",\n onValueChange,\n domains = COMMON_EMAIL_DOMAINS,\n maxSuggestions = 6,\n autoComplete = \"email\",\n suggestCorrection = true,\n name,\n onBlur,\n ref,\n ...field\n}: EmailInputProps) {\n const labels = useEmailInputLabels();\n const isControlled = value !== undefined;\n const [internal, setInternal] = useState(defaultValue);\n const current = isControlled ? value : internal;\n\n const inputRef = useRef<HTMLInputElement | null>(null);\n const setInput = (element: HTMLInputElement | null) => {\n inputRef.current = element;\n if (typeof ref === \"function\") return ref(element);\n if (ref) ref.current = element;\n };\n\n // Worked out when focus leaves the field, and gone as soon as the text\n // changes, so it never interrupts typing.\n const [correction, setCorrection] = useState<string>();\n const shownCorrection =\n suggestCorrection && !field.disabled ? correction : undefined;\n\n const change = (next: string) => {\n if (!isControlled) setInternal(next);\n setCorrection(undefined);\n onValueChange?.(next);\n };\n\n const acceptCorrection = () => {\n if (correction == null) return;\n change(correction);\n // The button goes away; focus goes back to the field, not to the page.\n inputRef.current?.focus();\n };\n\n // There is no \"no suggestions\" text, so with nothing to suggest the list is\n // closed, not open and empty.\n const [open, setOpen] = useState(false);\n\n const suggestions = useMemo(\n () => getEmailDomainSuggestions(current, domains).slice(0, maxSuggestions),\n [current, domains, maxSuggestions],\n );\n\n return (\n <ComboField\n {...field}\n ref={setInput}\n onBlur={(event) => {\n if (suggestCorrection)\n setCorrection(suggestEmailCorrection(current, domains));\n onBlur?.(event);\n }}\n afterHelperText={\n suggestCorrection && (\n <div role=\"status\" className={styles.Suggestion}>\n {shownCorrection != null && (\n <p>\n {labels.didYouMean}{\" \"}\n <Link\n render={<button type=\"button\" />}\n color=\"primary\"\n aria-label={`${labels.use} ${shownCorrection}`}\n onClick={acceptCorrection}\n >\n {shownCorrection}\n </Link>\n ?\n </p>\n )}\n </div>\n )\n }\n clearable\n inputProps={{\n type: \"email\",\n name,\n autoComplete,\n autoCapitalize: \"none\",\n spellCheck: false,\n }}\n renderRoot={(children) => (\n <BaseAutocomplete.Root\n items={suggestions}\n // The suggestions are already made from the text, so nothing is filtered out.\n filter={null}\n open={open && suggestions.length > 0}\n onOpenChange={setOpen}\n value={current}\n onValueChange={change}\n disabled={field.disabled}\n required={field.required}\n highlightItemOnHover={false}\n >\n {children}\n </BaseAutocomplete.Root>\n )}\n >\n <BaseAutocomplete.List className={listStyles.List}>\n {(suggestion: string) => (\n <BaseAutocomplete.Item\n key={suggestion}\n value={suggestion}\n className={listStyles.Item}\n >\n <span className={listStyles.ItemText}>{suggestion}</span>\n </BaseAutocomplete.Item>\n )}\n </BaseAutocomplete.List>\n </ComboField>\n );\n}\n\nconst useEmailInputLabels = createTranslation({\n didYouMean: {\n no: \"Mente du\",\n se: \"Menade du\",\n fi: \"Tarkoititko\",\n es: \"¿Quisiste decir\",\n },\n use: {\n no: \"Bruk\",\n se: \"Använd\",\n fi: \"Käytä\",\n es: \"Usar\",\n },\n});\n","\"use client\";\n\nimport { Input as BaseInput } from \"@base-ui/react/input\";\nimport {\n useEffect,\n useId,\n useLayoutEffect,\n useRef,\n type ComponentPropsWithoutRef,\n type CSSProperties,\n type MouseEvent,\n type ReactNode,\n type Ref,\n} from \"react\";\n\nimport {\n FieldGroupLabel,\n FieldHelperText,\n fieldMessage,\n RequiredAsterisk,\n} from \"../field-parts\";\nimport styles from \"../input.module.css\";\n\ntype NativeInputProps = Omit<\n ComponentPropsWithoutRef<\"input\">,\n | \"value\"\n | \"defaultValue\"\n | \"size\"\n | \"color\"\n | \"children\"\n | \"className\"\n | \"style\"\n>;\n\nexport type InputProps = NativeInputProps & {\n /** The label, linked to the input for you. */\n label?: ReactNode;\n /** Floating inside the field, or as a line above it. @default \"inside\" */\n labelPlacement?: \"inside\" | \"outside\";\n /**\n * Help text behind an info button next to the label. Needs\n * `labelPlacement=\"outside\"`: a floating label has no room for it.\n */\n tooltip?: ReactNode;\n /** Helper / error text shown below the input and linked via `aria-describedby`. */\n helperText?: ReactNode;\n /** Puts the input, its label and its helper text into the error state. */\n error?: boolean;\n /**\n * A message for a value that is allowed but unusual, such as an unusually\n * high amount. Shown in amber in place of the helper text, without the\n * error state: the field is not marked invalid. `error` wins when both are set.\n */\n warning?: ReactNode;\n /** The text (controlled). Pair with `onValueChange`. */\n value?: string;\n /** The text when first rendered, for uncontrolled use. */\n defaultValue?: string;\n /** Called with the new text on every change. `onChange` still gets the event. */\n onValueChange?: (value: string) => void;\n /** Content before the text, inside the field, e.g. an icon. */\n startAdornment?: ReactNode;\n /** Content after the text, inside the field, e.g. a show / hide button. */\n endAdornment?: ReactNode;\n /**\n * Renders a `<textarea>` that grows with its content. The native attributes\n * and event handlers then apply to the textarea.\n */\n multiline?: boolean;\n /** Fixed number of rows when `multiline`. */\n rows?: number;\n /** Fewest rows shown when `multiline`. */\n minRows?: number;\n /** Most rows shown when `multiline`, before it scrolls. */\n maxRows?: number;\n /** Class on the outermost element, for layout. */\n className?: string;\n /** Inline style on the outermost element, for layout. */\n style?: CSSProperties;\n /** The `<input>`, or the `<textarea>` when `multiline`. */\n ref?: Ref<HTMLInputElement | HTMLTextAreaElement>;\n};\n\nconst join = (...parts: (string | false | null | undefined)[]) =>\n parts.filter(Boolean).join(\" \") || undefined;\n\n// `useLayoutEffect` warns when a component is rendered on the server.\nconst useIsoLayoutEffect =\n typeof document === \"undefined\" ? useEffect : useLayoutEffect;\n\n/**\n * Sizes a textarea to its content, between `rows` (its `minRows`) and\n * `maxRows`. It listens to the element itself, so it also follows typing in\n * an uncontrolled textarea, which causes no render.\n */\nfunction useAutosize(enabled: boolean, maxRows?: number) {\n const elementRef = useRef<HTMLElement | null>(null);\n\n const resize = () => {\n const element = elementRef.current;\n if (!element) return;\n\n // Back to the height the `rows` attribute gives, to measure from there.\n element.style.height = \"auto\";\n const computed = getComputedStyle(element);\n const max =\n maxRows != null\n ? parseFloat(computed.lineHeight) * maxRows +\n parseFloat(computed.paddingTop) +\n parseFloat(computed.paddingBottom)\n : Infinity;\n const content = element.scrollHeight;\n element.style.height = `${Math.min(content, max)}px`;\n element.style.overflowY = content > max ? \"auto\" : \"hidden\";\n };\n\n useIsoLayoutEffect(() => {\n if (enabled) resize();\n });\n\n useEffect(() => {\n const element = elementRef.current;\n if (!enabled || !element) return;\n\n element.addEventListener(\"input\", resize);\n // The lines rewrap when the width changes.\n const observer =\n typeof ResizeObserver === \"undefined\" ? null : new ResizeObserver(resize);\n observer?.observe(element);\n return () => {\n element.removeEventListener(\"input\", resize);\n observer?.disconnect();\n };\n // `resize` reads nothing but the element and `maxRows`.\n }, [enabled, maxRows]);\n\n return elementRef;\n}\n\n/**\n * Text input, on Base UI's `Input` primitive. The label, the helper text and\n * the ids that link them to the input are all rendered and wired for you.\n *\n * Takes the native `<input>` attributes (`type`, `name`, `placeholder`,\n * `autoComplete`, `inputMode`, `maxLength`, `onKeyDown`, …), which land on the\n * input itself. `className` and `style` go on the outermost element. Pass\n * `multiline` (and `minRows`) for a textarea. Set `labelPlacement=\"outside\"`\n * for a label above the field, which can also carry a `tooltip`.\n *\n * ```tsx\n * <Input\n * label=\"E-post\"\n * helperText=\"Vi deler den aldri.\"\n * value={email}\n * onValueChange={setEmail}\n * />\n * ```\n */\nexport function Input({\n label,\n labelPlacement = \"inside\",\n tooltip,\n helperText,\n error = false,\n warning,\n value,\n defaultValue,\n onValueChange,\n startAdornment,\n endAdornment,\n multiline = false,\n rows,\n minRows,\n maxRows,\n className,\n style,\n ref,\n id,\n disabled = false,\n required = false,\n placeholder,\n \"aria-describedby\": ariaDescribedBy,\n ...inputAttributes\n}: InputProps) {\n const generatedId = useId();\n const inputId = id ?? generatedId;\n const labelId = useId();\n const helperTextId = useId();\n const hasLabel = label != null;\n const labelOutside = hasLabel && labelPlacement === \"outside\";\n const message = fieldMessage(helperText, warning, error);\n const hasHelperText = message.content != null;\n\n // A fixed `rows` is left alone; otherwise the textarea follows its content.\n const autosize = multiline && rows == null;\n const autosizeRef = useAutosize(autosize, maxRows);\n\n const setControl = (element: HTMLInputElement | null) => {\n autosizeRef.current = element;\n if (typeof ref === \"function\") return ref(element);\n if (ref) ref.current = element;\n };\n\n // The label, the padding and the adornments are part of the field, so a\n // click on any of them goes to the control, unless it hit something that\n // takes clicks itself (a show / hide button in an adornment).\n const focusControl = (event: MouseEvent<HTMLDivElement>) => {\n const target = event.target as Element;\n if (target.closest(\"button, a, input, textarea, select, [tabindex]\"))\n return;\n event.currentTarget.querySelector<HTMLElement>(\"input, textarea\")?.focus();\n };\n\n return (\n <div className={join(styles.Field, className)} style={style}>\n {labelOutside && (\n <FieldGroupLabel\n id={labelId}\n htmlFor={inputId}\n tooltip={tooltip}\n required={required}\n error={error}\n disabled={disabled}\n >\n {label}\n </FieldGroupLabel>\n )}\n <div\n className={styles.Box}\n data-invalid={error ? \"\" : undefined}\n data-disabled={disabled ? \"\" : undefined}\n data-has-label={hasLabel && !labelOutside ? \"\" : undefined}\n data-label-outside={labelOutside ? \"\" : undefined}\n onClick={focusControl}\n >\n {startAdornment != null && (\n <span className={styles.Adornment} data-position=\"start\">\n {startAdornment}\n </span>\n )}\n <div className={styles.Inner}>\n {hasLabel && !labelOutside && (\n <label htmlFor={inputId} className={styles.Label}>\n {label}\n {required && <RequiredAsterisk />}\n </label>\n )}\n <BaseInput\n {...inputAttributes}\n ref={setControl}\n id={inputId}\n value={value}\n defaultValue={defaultValue}\n onValueChange={(next) => onValueChange?.(next)}\n disabled={disabled}\n required={required}\n // Never empty: `:placeholder-shown` is how the stylesheet tells an\n // empty control from a filled one, and it needs a placeholder to work.\n placeholder={placeholder || \" \"}\n aria-invalid={error}\n aria-describedby={join(\n hasHelperText && helperTextId,\n ariaDescribedBy,\n )}\n className={styles.Control}\n render={\n multiline ? <textarea rows={rows ?? minRows ?? 1} /> : undefined\n }\n />\n </div>\n {endAdornment != null && (\n <span className={styles.Adornment} data-position=\"end\">\n {endAdornment}\n </span>\n )}\n </div>\n {hasHelperText && (\n <FieldHelperText\n id={helperTextId}\n error={error}\n warning={message.warning}\n >\n {message.content}\n </FieldHelperText>\n )}\n </div>\n );\n}\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n *\n * Shared by Select and MultiSelect. The box, its border states and the\n * adornments come from input.module.css, and the list from\n * option-list.module.css; this file adds the trigger, the label (which floats\n * on a value, not on focus) and the arrow.\n */\n\n/*\n * `.Field` in front of a selector lifts it over the rules of input.module.css\n * for the same element, whichever of the two stylesheets loads last.\n */\n.Field .Box {\n /* White, where Input shows the surface it sits on. As it always was. */\n background-color: var(--lea-palette-bg-base);\n cursor: pointer;\n}\n\n.Field .Box[data-disabled] {\n cursor: not-allowed;\n}\n\n/* The list is in a portal, so focus is not inside the box while it is open. */\n.Field .Box[data-open]:not([data-invalid]) {\n --input-border-color: var(--lea-palette-border-accent);\n --input-border-width: 2px;\n}\n\n/* The field and its open list read as one shape: square where they meet. */\n.Box[data-popup-side=\"bottom\"] {\n border-end-end-radius: 0;\n border-end-start-radius: 0;\n}\n\n.Box[data-popup-side=\"top\"] {\n border-start-end-radius: 0;\n border-start-start-radius: 0;\n}\n\n/* An empty layer over the whole box: a click anywhere in the field opens the list. */\n.Trigger {\n position: absolute;\n border-radius: inherit;\n cursor: inherit;\n inset: 0;\n /* The border of the box is the focus indicator, as in Input. */\n outline: none;\n}\n\n/* What the field shows lies over the trigger and lets clicks through to it. */\n.Box > :not(.Trigger) {\n position: relative;\n pointer-events: none;\n}\n\n.Box :is(button, a, input) {\n pointer-events: auto;\n}\n\n.Inner {\n flex-direction: column;\n}\n\n/* The value, or the chips. One line: a long value is cut off with an ellipsis. */\n.Display {\n overflow: hidden;\n min-height: calc(var(--lea-text-body-md-line-height) * 1em);\n box-sizing: content-box;\n padding-block: var(--input-padding-top) calc(var(--lea-spacing) * 0.75);\n padding-inline: var(--input-inset-start) var(--lea-spacing-0-5);\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.Placeholder {\n color: var(--lea-palette-text-muted);\n}\n\n/* While the label rests over the field the placeholder is hidden, so the two never overlap. */\n.Box[data-has-label]:not(:focus-within, [data-open]) .Placeholder {\n visibility: hidden;\n}\n\n/*\n * The label of Input, with one difference: it floats when there is a value or\n * a placeholder to make room for, not on focus alone. An empty select has\n * nothing to show under a floated label.\n */\n.Label {\n position: absolute;\n z-index: 1;\n overflow: hidden;\n max-width: calc(100% - var(--input-inset-start));\n /*\n * neutral-600 rather than text-subtle: a field has no background of its\n * own, and text-subtle falls below 4.5:1 on the tinted surfaces it can sit on.\n */\n color: var(--lea-color-neutral-600);\n inset-block-start: 0;\n inset-inline-start: var(--input-inset-start);\n scale: 1;\n text-overflow: ellipsis;\n transform-origin: top left;\n /* Resting, the label is centred in the field, where the value will be. */\n translate: 0\n calc((var(--input-height) - var(--lea-text-body-md-line-height) * 1em) / 2);\n transition: color var(--lea-duration-normal) var(--lea-easing-standard),\n scale var(--lea-duration-normal) var(--lea-easing-standard),\n translate var(--lea-duration-normal) var(--lea-easing-standard);\n white-space: nowrap;\n}\n\n.Box[data-filled] .Label,\n.Box[data-has-placeholder]:focus-within .Label,\n.Box[data-has-placeholder][data-open] .Label {\n max-width: calc((100% - var(--input-inset-start)) / 0.75);\n scale: 0.75;\n translate: 0 calc(var(--lea-spacing) * 0.75);\n}\n\n.Box:focus-within .Label,\n.Box[data-open] .Label {\n color: var(--lea-palette-text-accent);\n}\n\n.Box[data-invalid] .Label {\n color: var(--lea-palette-status-error-base);\n}\n\n.Box[data-disabled] .Label {\n color: var(--lea-palette-text-muted);\n}\n\n.Icon {\n width: 1.25rem;\n height: 1.25rem;\n flex-shrink: 0;\n align-self: center;\n margin-inline: var(--lea-spacing-0-5) var(--input-padding-inline);\n color: var(--lea-palette-text-base);\n transition: rotate var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n.Box[data-open] .Icon {\n rotate: 180deg;\n}\n\n.Box[data-disabled] .Icon {\n color: var(--lea-palette-text-muted);\n}\n\n.EndAdornment {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n align-self: center;\n color: var(--lea-palette-text-subtle);\n margin-inline-start: var(--lea-spacing-1);\n white-space: nowrap;\n}\n\n.SrOnly {\n position: absolute;\n overflow: hidden;\n width: 1px;\n height: 1px;\n padding: 0;\n border: 0;\n margin: -1px;\n clip-path: inset(50%);\n white-space: nowrap;\n}\n\n/*\n * `size=\"sm\"`: a compact field, 32px tall and 8px in at the sides, the\n * website's country select. The label is a line above it (`FieldGroupLabel`),\n * so the value is centred and nothing floats; an icon at the start is centred\n * with it.\n */\n.Box[data-size=\"sm\"] {\n --input-height: var(--lea-spacing-4);\n --input-padding-inline: var(--lea-spacing-1);\n}\n\n.Box[data-size=\"sm\"] .Display {\n padding-block: calc(\n (var(--input-height) - var(--lea-text-body-md-line-height) * 1em) / 2\n );\n}\n\n/* Before `.Adornment` in input.module.css: `.Field` lifts it over that rule. */\n.Field .Box[data-size=\"sm\"] > [data-position=\"start\"] {\n margin-block-start: 0;\n margin-inline: var(--lea-spacing-1);\n}\n\n/* The icon of an option (a flag), in the list and in the field. */\n.OptionIcon {\n display: flex;\n width: 1.25rem;\n height: 1.25rem;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n}\n\n.OptionIcon > :is(svg, img) {\n width: 100%;\n height: 100%;\n}\n\n/* MultiSelect: the chips, on one line. */\n.Chips {\n position: relative;\n display: flex;\n min-width: 0;\n}\n\n.ChipRow {\n display: flex;\n overflow: hidden;\n min-width: 0;\n flex: 1;\n flex-wrap: nowrap;\n align-items: center;\n gap: var(--lea-spacing-0-5);\n}\n\n/* Every chip and a badge, laid out unseen to be measured. */\n.ChipMeasure {\n position: absolute;\n display: flex;\n overflow: hidden;\n height: 0;\n gap: var(--lea-spacing-0-5);\n inset-block-start: 0;\n inset-inline-start: 0;\n visibility: hidden;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Label,\n .Icon {\n transition-duration: 0.01ms;\n }\n}\n","\"use client\";\n\nimport { Select as BaseSelect } from \"@base-ui/react/select\";\nimport { ChevronDown } from \"lucide-react\";\nimport {\n useEffect,\n useId,\n useRef,\n useState,\n type ComponentProps,\n type CSSProperties,\n type FocusEvent,\n type ReactNode,\n type Ref,\n} from \"react\";\n\nimport {\n FieldGroupLabel,\n FieldHelperText,\n RequiredAsterisk,\n} from \"./field-parts\";\nimport inputStyles from \"./input.module.css\";\nimport listStyles from \"./option-list.module.css\";\nimport styles from \"./select.module.css\";\nimport { useBlurOutside } from \"./use-blur-outside\";\n\n/*\n * Internal. The field that `Select` and `MultiSelect` share: the box of\n * `Input`, a trigger with the `combobox` role, and the list in a popup, on\n * Base UI's `Select`. Not part of the public API, so this file isn't\n * re-exported from `index.ts`.\n *\n * The trigger is an empty layer over the whole box, so a click anywhere in\n * the field opens the list. What the field shows (the label, the value or the\n * chips, the adornments, the arrow) lies on top of it and lets clicks through.\n * That keeps the remove buttons of `MultiSelect`'s chips out of the trigger:\n * a control inside a control is invalid, and its keys would open the list.\n */\n\n/** One choice in a `Select` or `MultiSelect`. */\nexport type SelectOption<Value extends string = string> = {\n value: Value;\n label: string;\n /** Shown, but not selectable. */\n disabled?: boolean;\n /**\n * A picture before the label, 20px: a flag, a logo. Decoration, hidden\n * from assistive tech, so the label has to say it all. In a `Select` the\n * chosen option's icon also shows in the field, unless `startAdornment`\n * takes its place.\n */\n icon?: ReactNode;\n};\n\n/**\n * `\"md\"`: the 54px field with a floating label. `\"sm\"`: a compact 32px field\n * for a narrow place (the country list in the website's footer), with its\n * label as a line above it.\n */\nexport type SelectSize = \"sm\" | \"md\";\n\n/** The props `Select` and `MultiSelect` have in common. */\nexport type SelectFieldBaseProps = {\n /** Floating label, linked to the select for you. */\n label?: ReactNode;\n /** Helper / error text shown below the control and linked via `aria-describedby`. */\n helperText?: ReactNode;\n /** Puts the select, its label and its helper text into the error state. */\n error?: boolean;\n /** Marks the select as required, with an asterisk after the label. */\n required?: boolean;\n disabled?: boolean;\n /**\n * Text shown while nothing is chosen. With a `label` it appears once the\n * label has floated up: on focus and while the list is open.\n */\n placeholder?: string;\n /** Content before the value, inside the field, e.g. an icon. */\n startAdornment?: ReactNode;\n /** Content after the value, inside the field, left of the dropdown arrow. */\n endAdornment?: ReactNode;\n /** Whether the list is open (controlled). Pair with `onOpenChange`. */\n open?: boolean;\n /** Called when the list opens or closes. */\n onOpenChange?: (open: boolean) => void;\n /** Called when focus leaves the select, the open list included. */\n onBlur?: (event: FocusEvent<HTMLElement>) => void;\n /** Submits the value under this name in a native form. */\n name?: string;\n /** Id of the element with the `combobox` role. */\n id?: string;\n /** Names the select when there is no visible `label`. */\n \"aria-label\"?: string;\n \"aria-labelledby\"?: string;\n \"aria-describedby\"?: string;\n /** Class on the outermost element, for layout. */\n className?: string;\n /** Inline style on the outermost element, for layout. */\n style?: CSSProperties;\n /** The element that takes focus, the one with the `combobox` role. */\n ref?: Ref<HTMLElement>;\n};\n\ntype SelectFieldProps = SelectFieldBaseProps & {\n /** `Select` only; `MultiSelect` is always `md`. */\n size?: SelectSize;\n multiple: boolean;\n /** The value as Base UI holds it: `null` or an array when empty. */\n value: string | string[] | null;\n onValueChange: (value: string | string[] | null) => void;\n /** Whether something is chosen, which is what floats the label. */\n filled: boolean;\n /** What the field shows for the value: text, or the chips. */\n display: ReactNode;\n /**\n * Hides `display` from assistive tech, when it only repeats `valueText`.\n * Not for chips: their remove buttons are controls.\n */\n displayHidden?: boolean;\n /** The value as plain text, for assistive tech, inside the trigger. */\n valueText: string;\n /** The `SelectFieldItem`s. */\n children: ReactNode;\n};\n\nconst join = (...parts: (string | false | null | undefined)[]) =>\n parts.filter(Boolean).join(\" \") || undefined;\n\ntype Side = \"top\" | \"bottom\";\n\ntype PositionerElementProps = ComponentProps<\"div\"> & {\n side: string;\n onSideChange: (side: Side) => void;\n};\n\n/**\n * The popup's positioner, which also reports the side it ended up on. The\n * field squares the corners on that side so the two read as one shape, and\n * the popup is in a portal where the field's CSS cannot see it.\n */\nfunction PositionerElement({\n side,\n onSideChange,\n ...props\n}: PositionerElementProps) {\n useEffect(() => {\n onSideChange(side === \"top\" ? \"top\" : \"bottom\");\n }, [side, onSideChange]);\n\n return <div {...props} />;\n}\n\nexport function SelectField({\n label,\n helperText,\n error = false,\n required = false,\n disabled = false,\n placeholder,\n startAdornment,\n endAdornment,\n open: openProp,\n onOpenChange,\n onBlur,\n name,\n id,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n \"aria-describedby\": ariaDescribedBy,\n className,\n style,\n ref,\n size = \"md\",\n multiple,\n value,\n onValueChange,\n filled,\n display,\n displayHidden = false,\n valueText,\n children,\n}: SelectFieldProps) {\n const generatedId = useId();\n const triggerId = id ?? generatedId;\n const labelId = useId();\n const helperTextId = useId();\n const hasLabel = label != null;\n // A 32px field has no room for a label to float in: it goes above.\n const labelOutside = hasLabel && size === \"sm\";\n const hasHelperText = helperText != null;\n\n const [internalOpen, setInternalOpen] = useState(false);\n const open = openProp ?? internalOpen;\n const [side, setSide] = useState<Side>(\"bottom\");\n\n const boxRef = useRef<HTMLDivElement>(null);\n const popupRef = useRef<HTMLDivElement>(null);\n\n // Opening the list moves focus from the trigger into the popup, and\n // choosing moves it back. Neither is leaving the select.\n const handleBlur = useBlurOutside(onBlur, [boxRef, popupRef]);\n\n return (\n <div\n className={join(inputStyles.Field, styles.Field, className)}\n style={style}\n >\n {labelOutside && (\n <FieldGroupLabel\n id={labelId}\n required={required}\n error={error}\n disabled={disabled}\n >\n {label}\n </FieldGroupLabel>\n )}\n <BaseSelect.Root\n id={triggerId}\n name={name}\n multiple={multiple}\n value={value}\n onValueChange={onValueChange}\n open={open}\n onOpenChange={(next) => {\n if (openProp === undefined) setInternalOpen(next);\n onOpenChange?.(next);\n }}\n disabled={disabled}\n required={required}\n // The keyboard and the mouse each mark their own option, as in a\n // native list: hovering does not move the option the arrow keys are on.\n highlightItemOnHover={false}\n >\n <div\n ref={boxRef}\n className={join(inputStyles.Box, styles.Box)}\n data-invalid={error ? \"\" : undefined}\n data-disabled={disabled ? \"\" : undefined}\n data-size={size === \"md\" ? undefined : size}\n data-has-label={hasLabel && !labelOutside ? \"\" : undefined}\n data-has-placeholder={placeholder ? \"\" : undefined}\n data-filled={filled ? \"\" : undefined}\n data-open={open ? \"\" : undefined}\n data-popup-side={open ? side : undefined}\n onBlur={handleBlur}\n >\n <BaseSelect.Trigger\n // Rendered as a `div`, see above; Base UI types the ref for a button.\n ref={ref as Ref<HTMLButtonElement>}\n nativeButton={false}\n render={<div />}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy ?? (hasLabel ? labelId : undefined)}\n aria-describedby={join(\n hasHelperText && helperTextId,\n ariaDescribedBy,\n )}\n aria-invalid={error || undefined}\n aria-required={required || undefined}\n className={styles.Trigger}\n >\n <span className={styles.SrOnly}>{valueText}</span>\n </BaseSelect.Trigger>\n {startAdornment != null && (\n <span className={inputStyles.Adornment} data-position=\"start\">\n {startAdornment}\n </span>\n )}\n <div className={join(inputStyles.Inner, styles.Inner)}>\n {hasLabel && !labelOutside && (\n <span id={labelId} className={styles.Label}>\n {label}\n {required && <RequiredAsterisk />}\n </span>\n )}\n <div\n className={styles.Display}\n aria-hidden={displayHidden || undefined}\n >\n {filled\n ? display\n : placeholder && (\n <span className={styles.Placeholder} aria-hidden>\n {placeholder}\n </span>\n )}\n </div>\n </div>\n {endAdornment != null && (\n <span className={styles.EndAdornment}>{endAdornment}</span>\n )}\n <ChevronDown className={styles.Icon} aria-hidden />\n </div>\n <BaseSelect.Portal>\n <BaseSelect.Positioner\n side=\"bottom\"\n align=\"start\"\n sideOffset={0}\n alignItemWithTrigger={false}\n className={listStyles.Positioner}\n render={(props, state) => (\n <PositionerElement\n {...props}\n side={state.side}\n onSideChange={setSide}\n />\n )}\n >\n <BaseSelect.Popup\n ref={popupRef}\n className={listStyles.Popup}\n onBlur={handleBlur}\n >\n <BaseSelect.List className={listStyles.List}>\n {children}\n </BaseSelect.List>\n </BaseSelect.Popup>\n </BaseSelect.Positioner>\n </BaseSelect.Portal>\n </BaseSelect.Root>\n {hasHelperText && (\n <FieldHelperText id={helperTextId} error={error}>\n {helperText}\n </FieldHelperText>\n )}\n </div>\n );\n}\n\ntype SelectFieldItemProps = {\n option: SelectOption;\n disabled?: boolean;\n /** Content before the label, e.g. the checkbox of `MultiSelect`. */\n children?: ReactNode;\n};\n\n/** One option in the list. */\nexport function SelectFieldItem({\n option,\n disabled,\n children,\n}: SelectFieldItemProps) {\n return (\n <BaseSelect.Item\n value={option.value}\n label={option.label}\n disabled={disabled ?? option.disabled}\n className={listStyles.Item}\n >\n {children}\n {option.icon != null && <OptionIcon>{option.icon}</OptionIcon>}\n <BaseSelect.ItemText className={listStyles.ItemText}>\n {option.label}\n </BaseSelect.ItemText>\n </BaseSelect.Item>\n );\n}\n\n/** The icon of an option, in the list or in the field: 20px, decoration. */\nexport function OptionIcon({ children }: { children: ReactNode }) {\n return (\n <span aria-hidden className={styles.OptionIcon}>\n {children}\n </span>\n );\n}\n","\"use client\";\n\nimport {\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n type ReactNode,\n type Ref,\n} from \"react\";\n\nimport { Chip } from \"../../data-display/chip/chip\";\nimport { CheckboxBox } from \"../checkbox-box\";\nimport {\n SelectField,\n SelectFieldItem,\n type SelectFieldBaseProps,\n type SelectOption,\n} from \"../select-field\";\nimport styles from \"../select.module.css\";\n\ntype SelectedChipsProps = {\n items: SelectOption[];\n canDelete: boolean;\n onDelete: (value: string) => void;\n};\n\n// Gap between chips, in px (`--lea-spacing-0-5` in the stylesheet).\nconst CHIP_GAP = 4;\n\n// `useLayoutEffect` warns when a component is rendered on the server.\nconst useIsoLayoutEffect =\n typeof document === \"undefined\" ? useEffect : useLayoutEffect;\n\n/**\n * Renders the selected values as chips on a single line. When they don't all\n * fit, the overflow is collapsed into a trailing `+N` badge rather than\n * wrapping onto a second line. Widths are measured off-screen so the visible\n * row can reserve room for the badge before deciding how many chips to show.\n */\nfunction SelectedChips({ items, canDelete, onDelete }: SelectedChipsProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n const measureRef = useRef<HTMLDivElement>(null);\n const [visibleCount, setVisibleCount] = useState(items.length);\n\n const recompute = useCallback(() => {\n const container = containerRef.current;\n const measure = measureRef.current;\n if (!container || !measure) return;\n\n const available = container.clientWidth;\n // The measurement row renders every chip followed by a single badge.\n const children = Array.from(measure.children) as HTMLElement[];\n const badgeEl = children[children.length - 1];\n const chipEls = children.slice(0, -1);\n const badgeWidth = badgeEl ? badgeEl.offsetWidth + CHIP_GAP : 0;\n\n const total = chipEls.reduce(\n (sum, el, index) => sum + el.offsetWidth + (index > 0 ? CHIP_GAP : 0),\n 0,\n );\n if (total <= available) {\n setVisibleCount(items.length);\n return;\n }\n\n let used = 0;\n let count = 0;\n for (let i = 0; i < chipEls.length; i++) {\n const next = chipEls[i].offsetWidth + (i > 0 ? CHIP_GAP : 0);\n if (used + next + badgeWidth <= available) {\n used += next;\n count += 1;\n } else {\n break;\n }\n }\n setVisibleCount(count);\n }, [items.length]);\n\n const itemsKey = items.map((item) => item.value).join(\"|\");\n\n useIsoLayoutEffect(() => {\n recompute();\n // Re-measure when the set of chips or the delete affordance changes.\n }, [recompute, itemsKey, canDelete]);\n\n useEffect(() => {\n const container = containerRef.current;\n if (!container || typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(() => recompute());\n observer.observe(container);\n return () => observer.disconnect();\n }, [recompute]);\n\n const hiddenCount = Math.max(items.length - visibleCount, 0);\n\n const renderChip = (item: SelectOption) => (\n <Chip\n key={item.value}\n size=\"sm\"\n label={item.label}\n style={{ flexShrink: 0, maxWidth: \"100%\" }}\n onDelete={canDelete ? () => onDelete(item.value) : undefined}\n />\n );\n\n const badge = (count: number) => (\n <Chip\n color=\"neutral\"\n size=\"sm\"\n label={`+${count}`}\n style={{ flexShrink: 0 }}\n />\n );\n\n return (\n <div className={styles.Chips}>\n {/* Off-screen measurement row: always renders every chip + a badge. */}\n <div ref={measureRef} aria-hidden className={styles.ChipMeasure}>\n {items.map((item) => renderChip(item))}\n {badge(items.length)}\n </div>\n\n <div ref={containerRef} className={styles.ChipRow}>\n {items.slice(0, visibleCount).map((item) => renderChip(item))}\n {hiddenCount > 0 && badge(hiddenCount)}\n </div>\n </div>\n );\n}\n\nexport type MultiSelectProps<Value extends string = string> =\n SelectFieldBaseProps & {\n /** The choices, shown with a checkbox each. */\n options: readonly SelectOption<Value>[];\n /** Currently selected values (controlled). */\n value?: Value[];\n /** Initially selected values (uncontrolled). */\n defaultValue?: Value[];\n /** Called with the next array of selected values. */\n onValueChange?: (value: Value[]) => void;\n /** Minimum number of values that must stay selected. @default 1 */\n min?: number;\n /** Maximum number of values that can be selected. Unlimited when omitted. */\n max?: number;\n /** Override how the selected values are summarised in the closed control. */\n renderValue?: (selected: Value[]) => ReactNode;\n };\n\n/**\n * Multi-select on Base UI's `Select`, in the same box as `Select`, with the\n * label, the helper text and their ids wired. Each option has a checkbox, and\n * the closed control shows the selection as removable chips by default.\n *\n * The selection is bounded by `min` (default 1, so the last value can't be\n * removed) and `max` (unlimited by default): options that would breach either\n * bound are disabled, and the constraints are also enforced on change.\n *\n * Works controlled (`value`) or uncontrolled (`defaultValue`).\n */\nexport function MultiSelect<Value extends string = string>({\n options,\n value,\n defaultValue = [],\n onValueChange,\n renderValue,\n min = 1,\n max,\n disabled = false,\n ref,\n ...rest\n}: MultiSelectProps<Value>) {\n const isControlled = value !== undefined;\n const [internal, setInternal] = useState<Value[]>(defaultValue);\n const selected = isControlled ? value : internal;\n\n const triggerRef = useRef<HTMLElement | null>(null);\n const setTrigger: Ref<HTMLElement> = (element) => {\n triggerRef.current = element;\n if (typeof ref === \"function\") return ref(element);\n if (ref) ref.current = element;\n };\n\n const atMax = max !== undefined && selected.length >= max;\n const atMin = selected.length <= min;\n\n const commit = (next: Value[]) => {\n if (!isControlled) setInternal(next);\n onValueChange?.(next);\n };\n\n const handleChange = (next: Value[]) => {\n // Enforce the bounds even if a disabled option is somehow toggled.\n const isAdding = next.length > selected.length;\n if (isAdding && max !== undefined && next.length > max) return;\n const isRemoving = next.length < selected.length;\n if (isRemoving && next.length < min) return;\n\n commit(next);\n };\n\n const handleDelete = (valueToRemove: string) => {\n if (selected.length <= min) return;\n commit(selected.filter((v) => v !== valueToRemove));\n // The button that was pressed is gone; keep focus in the field.\n triggerRef.current?.focus();\n };\n\n const canDelete = !disabled && selected.length > min;\n\n // In the order they were chosen, like the chips of a tag field.\n const selectedOptions = selected.map(\n (v): SelectOption =>\n options.find((option) => option.value === v) ?? { value: v, label: v },\n );\n\n return (\n <SelectField\n {...rest}\n ref={setTrigger}\n disabled={disabled}\n multiple\n value={selected}\n onValueChange={(next) => handleChange((next ?? []) as Value[])}\n filled={selected.length > 0}\n display={\n renderValue ? (\n renderValue(selected)\n ) : (\n <SelectedChips\n items={selectedOptions}\n canDelete={canDelete}\n onDelete={handleDelete}\n />\n )\n }\n valueText={selectedOptions.map((option) => option.label).join(\", \")}\n >\n {options.map((option) => {\n const isSelected = selected.includes(option.value);\n const atBound = (atMax && !isSelected) || (atMin && isSelected);\n return (\n <SelectFieldItem\n key={option.value}\n option={option}\n disabled={atBound || option.disabled}\n >\n {/* A picture of the state. The option itself is what gets picked. */}\n <CheckboxBox checked={isSelected} />\n </SelectFieldItem>\n );\n })}\n </SelectField>\n );\n}\n","export type Country = \"NO\" | \"SE\" | \"FI\" | \"ES\";\n\nexport type Currency = \"NOK\" | \"SEK\" | \"EUR\";\n\nexport interface CountryConfig {\n country: Country;\n locale: string;\n currency: Currency;\n}\n\nexport const COUNTRIES: Record<Country, CountryConfig> = {\n NO: { country: \"NO\", locale: \"nb-NO\", currency: \"NOK\" },\n SE: { country: \"SE\", locale: \"sv-SE\", currency: \"SEK\" },\n FI: { country: \"FI\", locale: \"fi-FI\", currency: \"EUR\" },\n ES: { country: \"ES\", locale: \"es-ES\", currency: \"EUR\" },\n};\n","import { isValidElement, type ReactNode } from \"react\";\n\nimport {\n COUNTRIES,\n type Country,\n type Currency,\n} from \"../../../hooks/country/countries\";\nimport type { Language } from \"../../../i18n\";\n\n/**\n * A unit the library writes for you, in the language of `LeaProvider`:\n *\n * - `{ type: \"years\" }`: \"år\", \"vuotta\", \"años\". The word follows the number\n * in the field, so Finnish and Spanish read \"1 vuosi\" and \"2 vuotta\",\n * \"1 año\" and \"2 años\".\n * - `{ type: \"currency\" }`: the symbol of the currency, \"kr\" or \"€\". The\n * currency is the one of the language (`no` NOK, `se` SEK, `fi` and `es`\n * EUR); pass `currency` for another.\n */\nexport type NumberInputUnitSpec =\n { type: \"years\" } | { type: \"currency\"; currency?: Currency };\n\n/** What `NumberInput` takes as `unit`: your own content, or a unit spec. */\nexport type NumberInputUnit = ReactNode | NumberInputUnitSpec;\n\nconst COUNTRY_OF_LANGUAGE: Record<Language, Country> = {\n no: \"NO\",\n se: \"SE\",\n fi: \"FI\",\n es: \"ES\",\n};\n\n/** A spec, as opposed to a string, a number or an element. */\nexport function isUnitSpec(unit: NumberInputUnit): unit is NumberInputUnitSpec {\n return (\n typeof unit === \"object\" &&\n unit !== null &&\n !isValidElement(unit) &&\n \"type\" in unit &&\n (unit.type === \"years\" || unit.type === \"currency\")\n );\n}\n\n/**\n * The text of a unit spec for a language. `value` picks the plural of \"years\"\n * and is ignored for a currency; an empty field counts as zero.\n */\nexport function formatUnit(\n unit: NumberInputUnitSpec,\n language: Language,\n value: number | null | undefined,\n): string {\n const { locale, currency: languageCurrency } =\n COUNTRIES[COUNTRY_OF_LANGUAGE[language]];\n\n const options: Intl.NumberFormatOptions =\n unit.type === \"years\"\n ? { style: \"unit\", unit: \"year\", unitDisplay: \"long\" }\n : {\n style: \"currency\",\n currency: unit.currency ?? languageCurrency,\n currencyDisplay: \"narrowSymbol\",\n };\n\n // Only the unit is wanted. `formatToParts` hands it over on its own, in the\n // right plural, without the number next to it.\n const part = new Intl.NumberFormat(locale, options)\n .formatToParts(unit.type === \"years\" ? (value ?? 0) : 0)\n .find((p) => p.type === (unit.type === \"years\" ? \"unit\" : \"currency\"));\n\n return part?.value ?? \"\";\n}\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n *\n * The field itself is the box of `Input`, from input.module.css. This file\n * adds what only a number field has: the unit and the stepper buttons.\n */\n\n/* The unit, set off from the value by a short line. */\n.Unit {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n align-self: center;\n padding-inline-start: var(--lea-spacing-1-5);\n border-inline-start: 1px solid var(--lea-palette-border-subtle);\n margin-inline-end: var(--lea-spacing-1);\n color: var(--lea-palette-text-subtle);\n /* Tighter than body text, which sets how tall the line beside it is. */\n line-height: var(--lea-line-height-tight);\n white-space: nowrap;\n}\n\n.Box[data-disabled] .Unit {\n color: var(--lea-palette-text-muted);\n}\n\n.Steppers {\n display: flex;\n flex-shrink: 0;\n align-self: center;\n gap: var(--lea-spacing-0-5);\n margin-inline: var(--lea-spacing-1);\n}\n\n/* Next to a unit, the unit's own margin is the gap. */\n.Unit + .Steppers {\n margin-inline-start: 0;\n}\n\n.Stepper {\n --stepper-tint: color-mix(\n in srgb,\n var(--lea-palette-text-base) calc(var(--lea-opacity-hover) * 100%),\n transparent\n );\n\n display: inline-flex;\n width: 2.25rem;\n height: 2.25rem;\n box-sizing: border-box;\n align-items: center;\n justify-content: center;\n padding: 0;\n border: 2px solid var(--lea-palette-border-subtle);\n border-radius: var(--lea-radius-full);\n background-color: transparent;\n color: var(--lea-palette-text-subtle);\n cursor: pointer;\n transition: background-color var(--lea-duration-normal)\n var(--lea-easing-standard);\n}\n\n.Stepper svg {\n display: block;\n flex-shrink: 0;\n}\n\n.Stepper:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n.Stepper[data-disabled] {\n color: var(--lea-palette-text-muted);\n cursor: not-allowed;\n}\n\n@media (hover: hover) {\n .Stepper:hover:not([data-disabled]) {\n background-color: var(--stepper-tint);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Stepper {\n transition-duration: 0.01ms;\n }\n}\n","\"use client\";\n\nimport { NumberField as BaseNumberField } from \"@base-ui/react/number-field\";\nimport { Minus, Plus } from \"lucide-react\";\nimport {\n useId,\n useState,\n type CSSProperties,\n type MouseEvent,\n type ReactNode,\n type Ref,\n} from \"react\";\n\nimport { createTranslation, useLanguage } from \"../../../i18n\";\nimport {\n FieldGroupLabel,\n FieldHelperText,\n fieldMessage,\n RequiredAsterisk,\n} from \"../field-parts\";\nimport inputStyles from \"../input.module.css\";\nimport {\n formatUnit,\n isUnitSpec,\n type NumberInputUnit,\n} from \"./number-input-unit\";\nimport styles from \"./number-input.module.css\";\n\nexport type { NumberInputUnit, NumberInputUnitSpec } from \"./number-input-unit\";\n\nexport type NumberInputProps = Omit<\n BaseNumberField.Root.Props,\n \"ref\" | \"inputRef\" | \"render\" | \"className\" | \"style\"\n> & {\n /** The visible `<input>`. */\n ref?: Ref<HTMLInputElement>;\n /** The label, linked to the input for you. */\n label?: ReactNode;\n /** Floating inside the field, or as a line above it. @default \"inside\" */\n labelPlacement?: \"inside\" | \"outside\";\n /**\n * Help text behind an info button next to the label. Needs\n * `labelPlacement=\"outside\"`: a floating label has no room for it.\n */\n tooltip?: ReactNode;\n /** Helper / error text shown below the control and linked via `aria-describedby`. */\n helperText?: ReactNode;\n /** Renders the field in its error state (border, label and helper colour). */\n error?: boolean;\n /**\n * A message for a value that is allowed but unusual, such as an unusually\n * high amount. Shown in amber in place of the helper text, without the\n * error state: the field is not marked invalid. `error` wins when both are set.\n */\n warning?: ReactNode;\n /** Reflects `required` onto the control and the label asterisk. */\n required?: boolean;\n /** Disables the input and the stepper buttons. */\n disabled?: boolean;\n /** Placeholder shown while the field is empty. */\n placeholder?: string;\n /**\n * Decorative leading icon (e.g. a category glyph). Rendered `aria-hidden`, so\n * the visible label must carry the meaning.\n */\n startIcon?: ReactNode;\n /**\n * Trailing unit shown inside the field. Rendered as real text linked to the\n * input via `aria-describedby` — not a placeholder — so it is announced and\n * survives typing.\n *\n * - Your own content: `unit=\"kr/mnd\"`. It is not translated.\n * - `{ type: \"years\" }`: \"år\", \"vuotta\" or \"años\" in the language of\n * `LeaProvider`, in the plural that fits the number in the field.\n * - `{ type: \"currency\" }`: \"kr\" or \"€\" for the language (`{ type:\n * \"currency\", currency: \"EUR\" }` for another currency).\n */\n unit?: NumberInputUnit;\n /**\n * Whether to render the increment / decrement stepper buttons.\n * @default true\n */\n showSteppers?: boolean;\n /** Class on the outermost element, for layout. */\n className?: string;\n /** Inline style on the outermost element, for layout. */\n style?: CSSProperties;\n};\n\nconst join = (...parts: (string | false | null | undefined)[]) =>\n parts.filter(Boolean).join(\" \") || undefined;\n\n/**\n * Numeric input built on Base UI's `NumberField` (value / min / max / step /\n * formatting), in the same box as `Input`, with increment / decrement buttons.\n *\n * The label, the helper text and the ids that link them to the input are all\n * rendered and wired for you. Set `labelPlacement=\"outside\"` for a label above\n * the field, which can also carry a `tooltip`.\n *\n * For money and other open-ended amounts, set `showSteppers={false}` and use a\n * `unit` (e.g. `kr`) instead — a stepper is only helpful for small bounded\n * counts.\n *\n * `unit` takes your own text, or a unit the library writes in the language of\n * `LeaProvider`:\n *\n * ```tsx\n * <NumberInput label=\"Lånebeløp\" unit={{ type: \"currency\" }} showSteppers={false} />\n * <NumberInput label=\"Nedbetalingstid\" unit={{ type: \"years\" }} min={1} max={15} />\n * ```\n */\nexport function NumberInput({\n label,\n labelPlacement = \"inside\",\n tooltip,\n helperText,\n error = false,\n warning,\n required = false,\n disabled = false,\n id,\n placeholder,\n startIcon,\n unit,\n showSteppers = true,\n className,\n style,\n ref,\n value,\n defaultValue,\n onValueChange,\n ...rest\n}: NumberInputProps) {\n const language = useLanguage();\n // The plural of \"years\" follows the number in the field, controlled or not.\n const [typedValue, setTypedValue] = useState(defaultValue ?? null);\n const currentValue = value !== undefined ? value : typedValue;\n const unitContent = isUnitSpec(unit)\n ? formatUnit(unit, language, currentValue)\n : unit;\n const generatedId = useId();\n const inputId = id ?? generatedId;\n const labelId = useId();\n const helperTextId = useId();\n const unitId = useId();\n const stepperLabels = useStepperLabels();\n\n const hasLabel = label != null;\n const labelOutside = hasLabel && labelPlacement === \"outside\";\n const message = fieldMessage(helperText, warning, error);\n const hasHelperText = message.content != null;\n\n // The input is described by any combination of helper text + the trailing\n // unit, in reading order, and never points at an element that isn't rendered.\n const describedBy = join(\n hasHelperText && helperTextId,\n unitContent != null && unitContent !== \"\" && unitId,\n );\n\n // The label, the padding, the icon and the unit are part of the field, so a\n // click on any of them goes to the input. The stepper buttons keep theirs.\n const focusInput = (event: MouseEvent<HTMLDivElement>) => {\n const target = event.target as Element;\n if (target.closest(\"button, a, input, [tabindex]\")) return;\n event.currentTarget.querySelector(\"input\")?.focus();\n };\n\n return (\n <BaseNumberField.Root\n {...rest}\n value={value}\n defaultValue={defaultValue}\n onValueChange={(next, details) => {\n setTypedValue(next);\n onValueChange?.(next, details);\n }}\n id={inputId}\n disabled={disabled}\n required={required}\n className={join(inputStyles.Field, className)}\n style={style}\n >\n {labelOutside && (\n <FieldGroupLabel\n id={labelId}\n htmlFor={inputId}\n tooltip={tooltip}\n required={required}\n error={error}\n disabled={disabled}\n >\n {label}\n </FieldGroupLabel>\n )}\n <div\n className={join(inputStyles.Box, styles.Box)}\n data-invalid={error ? \"\" : undefined}\n data-disabled={disabled ? \"\" : undefined}\n data-has-label={hasLabel && !labelOutside ? \"\" : undefined}\n data-label-outside={labelOutside ? \"\" : undefined}\n onClick={focusInput}\n >\n {startIcon != null && (\n <span\n className={inputStyles.Adornment}\n data-position=\"start\"\n aria-hidden\n >\n {startIcon}\n </span>\n )}\n <div className={inputStyles.Inner}>\n {hasLabel && !labelOutside && (\n <label htmlFor={inputId} className={inputStyles.Label}>\n {label}\n {required && <RequiredAsterisk />}\n </label>\n )}\n <BaseNumberField.Input\n ref={ref}\n // Never empty: `:placeholder-shown` is how the stylesheet tells an\n // empty field from a filled one, and it needs a placeholder to work.\n placeholder={placeholder || \" \"}\n aria-invalid={error || undefined}\n aria-describedby={describedBy}\n className={inputStyles.Control}\n />\n </div>\n {unitContent != null && unitContent !== \"\" && (\n <span id={unitId} className={styles.Unit}>\n {unitContent}\n </span>\n )}\n {showSteppers && (\n <span className={styles.Steppers}>\n <BaseNumberField.Decrement\n aria-label={stepperLabels.decrease}\n className={styles.Stepper}\n >\n <Minus size={16} aria-hidden />\n </BaseNumberField.Decrement>\n <BaseNumberField.Increment\n aria-label={stepperLabels.increase}\n className={styles.Stepper}\n >\n <Plus size={16} aria-hidden />\n </BaseNumberField.Increment>\n </span>\n )}\n </div>\n {hasHelperText && (\n <FieldHelperText\n id={helperTextId}\n error={error}\n warning={message.warning}\n >\n {message.content}\n </FieldHelperText>\n )}\n </BaseNumberField.Root>\n );\n}\n\nconst useStepperLabels = createTranslation({\n increase: {\n no: \"Øk\",\n se: \"Öka\",\n fi: \"Lisää\",\n es: \"Aumentar\",\n },\n decrease: {\n no: \"Reduser\",\n se: \"Minska\",\n fi: \"Vähennä\",\n es: \"Disminuir\",\n },\n});\n","import {\n getCountries,\n getCountryCallingCode,\n type CountryCode,\n} from \"libphonenumber-js\";\n\nexport type { CountryCode };\n\n/**\n * Countries surfaced at the top of the country-code select. Lea Bank's core\n * markets, in priority order. The rest of the world follows, sorted by their\n * localised name.\n */\nexport const PINNED_COUNTRIES: readonly CountryCode[] = [\n \"FI\",\n \"SE\",\n \"NO\",\n \"ES\",\n];\n\nexport interface CountryOption {\n /** ISO 3166-1 alpha-2 code, e.g. `\"FI\"`. */\n code: CountryCode;\n /** International dialling code without the leading `+`, e.g. `\"358\"`. */\n dialCode: string;\n /** Localised country name, e.g. `\"Finland\"`. */\n name: string;\n /** Flag emoji derived from the ISO code. Purely decorative. */\n flag: string;\n /** Whether this is one of the pinned core-market countries. */\n pinned: boolean;\n}\n\n/**\n * Turns a two-letter ISO country code into its flag emoji by mapping each\n * letter to the matching Unicode regional-indicator symbol. No asset library\n * needed — the platform renders the pair as a flag.\n */\nexport function flagEmoji(code: string): string {\n return code\n .toUpperCase()\n .replace(/[A-Z]/g, (char) =>\n String.fromCodePoint(127397 + char.charCodeAt(0)),\n );\n}\n\n/**\n * Builds the ordered country list for the select: the pinned core markets\n * first, then every remaining country sorted by its name in the given locale.\n * Country names come from `Intl.DisplayNames`, so they follow the active locale\n * with no hand-maintained list.\n */\nexport function getCountryOptions(locale: string): CountryOption[] {\n const displayNames = new Intl.DisplayNames([locale], { type: \"region\" });\n const all = getCountries();\n\n const toOption = (code: CountryCode, pinned: boolean): CountryOption => ({\n code,\n dialCode: getCountryCallingCode(code),\n name: displayNames.of(code) ?? code,\n flag: flagEmoji(code),\n pinned,\n });\n\n const pinned = PINNED_COUNTRIES.filter((code) => all.includes(code)).map(\n (code) => toOption(code, true),\n );\n\n const rest = all\n .filter((code) => !PINNED_COUNTRIES.includes(code))\n .map((code) => toOption(code, false))\n .sort((a, b) => a.name.localeCompare(b.name, locale));\n\n return [...pinned, ...rest];\n}\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n *\n * The box, its border states, the label and the text field come from\n * input.module.css, and the options from option-list.module.css; this file\n * adds the country button at the start of the field and the popup it opens.\n */\n\n/*\n * `.Field` in front of a selector lifts it over the rules of input.module.css\n * for the same element, whichever of the two stylesheets loads last.\n */\n\n/* The country list is in a portal, so focus is not inside the box while it is open. */\n.Field .Box[data-country-open]:not([data-invalid]) {\n --input-border-color: var(--lea-palette-border-accent);\n --input-border-width: 2px;\n}\n\n/*\n * The label is always up: the country button takes the place where it would\n * rest, and with it up there is room for a placeholder that shows the format.\n */\n.Field .Label {\n max-width: calc(\n (100% - var(--input-inset-start) - var(--input-padding-inline)) / 0.75\n );\n scale: 0.75;\n translate: 0 calc(var(--lea-spacing) * 0.75);\n}\n\n.Field .Inner {\n /* 10px between the line after the country button and the number. */\n --input-inset-start: calc(var(--lea-spacing) * 1.25);\n}\n\n.Trigger {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n padding-block: 0;\n padding-inline: var(--lea-spacing-1) var(--lea-spacing-0-5);\n border: 0;\n /* The line between the country and the number. */\n border-inline-end: 1px solid var(--lea-palette-border-subtle);\n border-radius: var(--lea-radius-xl) 0 0 var(--lea-radius-xl);\n margin: 0;\n background: none;\n color: var(--lea-palette-text-base);\n cursor: pointer;\n font: inherit;\n gap: var(--lea-spacing-0-5);\n letter-spacing: inherit;\n white-space: nowrap;\n}\n\n@media (hover: hover) {\n .Trigger:hover:not(:disabled) {\n background-color: var(--lea-palette-bg-subtle);\n }\n}\n\n.Trigger[data-popup-open] {\n background-color: var(--lea-palette-bg-subtle);\n}\n\n/*\n * The dial code of a field with one country (`countries` of one): the\n * country button without the button. Same place, line and type, so a form\n * with both kinds lines up; no chevron and no hover, as there is nothing to\n * open.\n */\n.DialCode {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n padding-inline: var(--lea-spacing-1);\n border-inline-end: 1px solid var(--lea-palette-border-subtle);\n color: var(--lea-palette-text-base);\n gap: var(--lea-spacing-0-5);\n white-space: nowrap;\n}\n\n.Box[data-disabled] .DialCode {\n opacity: 0.6;\n}\n\n/* Inside the border of the field, which a ring around the button would cover. */\n.Trigger:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: -0.25rem;\n}\n\n.Trigger:disabled {\n cursor: not-allowed;\n opacity: 0.6;\n}\n\n/* One step up the type scale: a flag at the size of the text reads as smaller than it. */\n.Flag {\n font-size: var(--lea-font-step-1);\n line-height: var(--lea-line-height-none);\n}\n\n/* One width for every dial code, +1 or +358, so the number does not move with the country. */\n.Dial {\n min-width: 2.5rem;\n text-align: center;\n}\n\n.Chevron {\n width: 1rem;\n height: 1rem;\n flex-shrink: 0;\n color: var(--lea-palette-text-subtle);\n}\n\n/* As wide as the field, a little below it. */\n.Popup {\n overflow: hidden;\n width: var(--anchor-width);\n max-width: var(--available-width);\n box-sizing: border-box;\n border: 1px solid var(--lea-palette-border-accent);\n border-radius: var(--lea-radius-4xl);\n background-color: var(--lea-palette-bg-base);\n box-shadow: var(--lea-shadow-lg);\n color: var(--lea-palette-text-base);\n outline: none;\n transition: opacity var(--lea-duration-fast) var(--lea-easing-standard);\n}\n\n/*\n * The popup fades in, and goes at once: opened again while on its way out, it\n * came back with the search text it was closed with. See combo-field.module.css.\n */\n.Popup[data-starting-style] {\n opacity: 0;\n}\n\n.Popup[data-ending-style] {\n transition: none;\n}\n\n.Search {\n display: flex;\n align-items: center;\n padding-block: calc(var(--lea-spacing) * 1.25);\n padding-inline: calc(var(--lea-spacing) * 1.75);\n border-block-end: 1px solid var(--lea-palette-border-subtle);\n gap: var(--lea-spacing-1);\n}\n\n.Search svg {\n width: 1.125rem;\n height: 1.125rem;\n flex-shrink: 0;\n color: var(--lea-palette-text-subtle);\n}\n\n.SearchInput {\n min-width: 0;\n flex: 1;\n padding: 0;\n border: 0;\n margin: 0;\n background: none;\n color: inherit;\n font-family: inherit;\n font-size: var(--lea-text-body-md-font-size);\n font-weight: var(--lea-text-body-md-font-weight);\n letter-spacing: var(--lea-text-body-md-letter-spacing);\n line-height: var(--lea-text-body-md-line-height);\n /* The popup has the border that shows where focus is. */\n outline: none;\n}\n\n.SearchInput::placeholder {\n color: var(--lea-palette-text-muted);\n opacity: 1;\n}\n\n/*\n * 200px: five countries, then it scrolls. Less when the window is short, by\n * the height of the search box above it. The fallback is for the first\n * measurement, see option-list.module.css.\n */\n.List {\n overflow: hidden auto;\n max-height: min(12.5rem, var(--available-height, 100vh) - 4rem);\n}\n\n.Country {\n gap: calc(var(--lea-spacing) * 1.25);\n}\n\n.CountryName {\n overflow: hidden;\n min-width: 0;\n flex: 1;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/*\n * neutral-600 rather than text-subtle, which falls below 4.5:1 on the fill of\n * a row under the pointer. On the fill of the country in use it takes the\n * colour of the name.\n */\n.CountryDial {\n color: var(--lea-color-neutral-600);\n}\n\n.Country[data-selected] .CountryDial {\n color: inherit;\n}\n\n/* Between the pinned countries and the rest. */\n.Separator {\n flex-shrink: 0;\n height: 1px;\n background-color: var(--lea-palette-border-base);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Popup {\n transition-duration: 0.01ms;\n }\n}\n","\"use client\";\n\nimport { Combobox } from \"@base-ui/react/combobox\";\nimport {\n AsYouType,\n getCountryCallingCode,\n parsePhoneNumberFromString,\n} from \"libphonenumber-js\";\nimport { ChevronDown, Search } from \"lucide-react\";\nimport {\n Fragment,\n useId,\n useMemo,\n useRef,\n useState,\n type ChangeEvent,\n type ComponentPropsWithoutRef,\n type CSSProperties,\n type FocusEvent,\n type MouseEvent,\n type ReactNode,\n type Ref,\n type RefObject,\n} from \"react\";\n\nimport { createTranslation, useLanguage, type Language } from \"../../../i18n\";\nimport {\n isMobileCountry,\n validateMobileNumber,\n validatePhoneNumber,\n} from \"../../../validation/phone\";\nimport { invalid, valid } from \"../../../validation/types\";\nimport { FieldHelperText, RequiredAsterisk } from \"../field-parts\";\nimport inputStyles from \"../input.module.css\";\nimport listStyles from \"../option-list.module.css\";\nimport {\n getCountryOptions,\n type CountryCode,\n type CountryOption,\n} from \"./countries\";\nimport styles from \"./phone-number-input.module.css\";\n\n/** The default country and the locale used to name/sort the list, per language. */\nconst LANGUAGE_TO_COUNTRY: Record<Language, CountryCode> = {\n no: \"NO\",\n se: \"SE\",\n fi: \"FI\",\n es: \"ES\",\n};\n\nconst LANGUAGE_TO_LOCALE: Record<Language, string> = {\n no: \"nb-NO\",\n se: \"sv-SE\",\n fi: \"fi-FI\",\n es: \"es-ES\",\n};\n\n/** Details reported alongside every change so the consumer can react to validity. */\nexport interface PhoneNumberMeta {\n /** The selected country. */\n country: CountryCode;\n /** Canonical E.164 number when valid (e.g. `\"+358401234567\"`), otherwise `undefined`. */\n e164?: string;\n /** Whether the current number is a valid phone number for the country. */\n isValid: boolean;\n}\n\ntype NativeInputProps = Omit<\n ComponentPropsWithoutRef<\"input\">,\n | \"value\"\n | \"defaultValue\"\n | \"onChange\"\n | \"type\"\n | \"size\"\n | \"color\"\n | \"children\"\n | \"className\"\n | \"style\"\n>;\n\nexport type PhoneNumberInputProps = NativeInputProps & {\n /** Floating label, linked to the input for you. */\n label?: ReactNode;\n /** Helper / error text shown below the control and linked via `aria-describedby`. */\n helperText?: ReactNode;\n /**\n * Puts the input, its label and its helper text into the error state. When\n * set, true or false, the built-in check on blur stays out of it.\n */\n error?: boolean;\n /** Selected country (controlled). Pair with `onCountryChange`. */\n country?: CountryCode;\n /** Initial country when uncontrolled. Defaults to the country for the active locale. */\n defaultCountry?: CountryCode;\n /** Fired when the user picks a different country. */\n onCountryChange?: (country: CountryCode) => void;\n /** The national number as displayed (controlled). Pair with `onValueChange`. */\n value?: string;\n /** Initial national number when uncontrolled. */\n defaultValue?: string;\n /** Fired on every keystroke with the formatted national number and validity meta. */\n onValueChange?: (value: string, meta: PhoneNumberMeta) => void;\n /**\n * The countries the number may be from. With one, there is no country\n * list: its dial code stands fixed at the start of the field (`+46`). With\n * several, the list holds these alone.\n *\n * Either way the value is the national number without the trunk `0`\n * (`70 123 45 67`): a pasted `+46 70…`, `0046 70…` or `070…` is cleaned up to\n * it, and when focus leaves the field. A number of another country is\n * invalid. Left out, every country is in the list and the number is kept as\n * typed, as before.\n */\n countries?: readonly CountryCode[];\n /**\n * Accept mobile numbers only: a landline is invalid, with a message of its\n * own (\"Skriv inn et mobilnummer\"). Applies to Sweden, Finland, Norway and\n * Spain, as `validateMobileNumber`; a number of another country is checked\n * as any number. `meta.isValid` follows it.\n */\n mobileOnly?: boolean;\n /**\n * Show a built-in \"invalid number\" error once the field is blurred with a\n * non-empty, invalid value. Ignored when the consumer drives `error`.\n * @default true\n */\n validateOnBlur?: boolean;\n /** Class on the outermost element, for layout. */\n className?: string;\n /** Inline style on the outermost element, for layout. */\n style?: CSSProperties;\n /** The phone number `<input>`. */\n ref?: Ref<HTMLInputElement>;\n};\n\n/**\n * Formats the national number as the user types. Norwegian mobile numbers are\n * grouped 3-2-3 (e.g. `400 12 345`), which `libphonenumber-js` doesn't do on its\n * own (it pairs digits as `40 01 23 45`). Every other country keeps the library's\n * standard grouping via `AsYouType`.\n */\nfunction formatNationalNumber(country: CountryCode, input: string): string {\n if (country === \"NO\") {\n const digits = input.replace(/\\D/g, \"\").slice(0, 8);\n return [digits.slice(0, 3), digits.slice(3, 5), digits.slice(5, 8)]\n .filter(Boolean)\n .join(\" \");\n }\n return new AsYouType(country).input(input);\n}\n\n/**\n * The national number, grouped as in the international format but without\n * the dial code, for a field that shows the dial code in front of it:\n * `70 123 45 67` rather than `070-123 45 67`.\n */\nfunction formatWithoutDialCode(country: CountryCode, digits: string): string {\n if (country === \"NO\") return formatNationalNumber(country, digits);\n const dialCode = getCountryCallingCode(country);\n return new AsYouType(country)\n .input(`+${dialCode}${digits}`)\n .replace(`+${dialCode}`, \"\")\n .trim();\n}\n\n/** What typing gives, with the dial code in front of the field. */\nfunction formatTyped(country: CountryCode, text: string): string {\n // Someone typing \"070…\" or \"+46…\" by hand: left as typed until focus\n // leaves the field, rather than losing the first digit under their cursor.\n if (/^\\s*[+0]/.test(text)) return new AsYouType(country).input(text);\n return formatWithoutDialCode(country, text.replace(/\\D/g, \"\"));\n}\n\n/**\n * The text cleaned up to the national number of `country`, when it is a\n * number of that country (`+46 70…`, `0046 70…`, `070…`); otherwise as typed.\n */\nfunction cleanUp(country: CountryCode, text: string): string {\n const parsed = parsePhoneNumberFromString(\n text.replace(/[\\s-]/g, \"\"),\n country,\n );\n if (parsed && parsed.countryCallingCode === getCountryCallingCode(country)) {\n return formatWithoutDialCode(country, parsed.nationalNumber);\n }\n return formatTyped(country, text);\n}\n\ntype NumberCheck = { valid: boolean; normalized?: string; landline: boolean };\n\n/**\n * Validates the number for the field: of the selected country only, when the\n * countries are restricted, and a mobile only, with `mobileOnly`.\n */\nfunction checkNumber(\n country: CountryCode,\n value: string,\n { sameCountry, mobileOnly }: { sameCountry: boolean; mobileOnly: boolean },\n): NumberCheck {\n const ofCountry = () => {\n const parsed = parsePhoneNumberFromString(\n value.replace(/\\s/g, \"\"),\n country,\n );\n return parsed?.isValid() && parsed.country === country\n ? valid(parsed.number)\n : invalid();\n };\n\n if (mobileOnly && isMobileCountry(country)) {\n const mobile = validateMobileNumber(country, value);\n return mobile.valid\n ? { ...mobile, landline: false }\n : { valid: false, landline: ofCountry().valid };\n }\n\n return {\n ...(sameCountry ? ofCountry() : validatePhoneNumber(country, value)),\n landline: false,\n };\n}\n\nconst usePhoneLabels = createTranslation({\n countryLabel: {\n no: \"Landskode\",\n se: \"Landskod\",\n fi: \"Maakoodi\",\n es: \"Código de país\",\n },\n searchCountry: {\n no: \"Søk etter land\",\n se: \"Sök efter land\",\n fi: \"Hae maata\",\n es: \"Buscar país\",\n },\n noCountries: {\n no: \"Ingen land funnet\",\n se: \"Inga länder hittades\",\n fi: \"Maita ei löytynyt\",\n es: \"No se han encontrado países\",\n },\n invalid: {\n no: \"Ugyldig telefonnummer\",\n se: \"Ogiltigt telefonnummer\",\n fi: \"Virheellinen puhelinnumero\",\n es: \"Número de teléfono no válido\",\n },\n notMobile: {\n no: \"Skriv inn et mobilnummer\",\n se: \"Ange ett mobilnummer\",\n fi: \"Anna matkapuhelinnumero\",\n es: \"Introduce un número de móvil\",\n },\n});\n\nconst join = (...parts: (string | false | null | undefined)[]) =>\n parts.filter(Boolean).join(\" \") || undefined;\n\n/** What typing in the search box is matched against. */\nconst searchText = (option: CountryOption) =>\n `${option.name} ${option.code} ${option.dialCode} +${option.dialCode}`;\n\nconst sameCountry = (a: CountryOption, b: CountryOption) => a.code === b.code;\n\ntype CountrySelectProps = {\n options: CountryOption[];\n value: CountryCode;\n onChange: (country: CountryCode) => void;\n ariaLabel: string;\n searchLabel: string;\n noOptionsText: string;\n disabled: boolean;\n open: boolean;\n onOpenChange: (open: boolean) => void;\n /** The field, which the popup sits under and matches in width. */\n anchorRef: RefObject<HTMLDivElement | null>;\n};\n\n/**\n * The country button at the start of the field, and the popup it opens: a\n * search box over the list of countries, on Base UI's `Combobox`. The popup\n * is anchored to the whole field rather than to the button.\n */\nfunction CountrySelect({\n options,\n value,\n onChange,\n ariaLabel,\n searchLabel,\n noOptionsText,\n disabled,\n open,\n onOpenChange,\n anchorRef,\n}: CountrySelectProps) {\n const { contains } = Combobox.useFilter({ sensitivity: \"base\" });\n const [query, setQuery] = useState(\"\");\n // See `ComboField`: the option the arrow keys are on is marked once a key\n // has been pressed, not for a list opened with the mouse.\n const [keyboard, setKeyboard] = useState(false);\n\n const selected = options.find((option) => option.code === value) ?? null;\n const pinnedCount = options.filter((option) => option.pinned).length;\n\n return (\n <Combobox.Root<CountryOption>\n items={options}\n value={selected}\n onValueChange={(next) => {\n if (next) onChange(next.code);\n }}\n inputValue={query}\n onInputValueChange={setQuery}\n open={open}\n onOpenChange={onOpenChange}\n filter={(option, text) => contains(searchText(option), text)}\n itemToStringLabel={(option) => option.name}\n itemToStringValue={(option) => option.code}\n isItemEqualToValue={sameCountry}\n disabled={disabled}\n highlightItemOnHover={false}\n >\n <Combobox.Trigger\n className={styles.Trigger}\n aria-label={selected ? `${ariaLabel}, ${selected.name}` : ariaLabel}\n >\n {selected && (\n <>\n <span aria-hidden className={styles.Flag}>\n {selected.flag}\n </span>\n <span className={styles.Dial}>+{selected.dialCode}</span>\n </>\n )}\n <ChevronDown aria-hidden className={styles.Chevron} />\n </Combobox.Trigger>\n <Combobox.Portal>\n <Combobox.Positioner\n anchor={anchorRef}\n side=\"bottom\"\n align=\"start\"\n sideOffset={4}\n className={listStyles.Positioner}\n >\n <Combobox.Popup\n className={styles.Popup}\n // Base UI makes the popup a dialog, which needs a name.\n aria-label={ariaLabel}\n data-keyboard={keyboard ? \"\" : undefined}\n onKeyDownCapture={() => setKeyboard(true)}\n onPointerDownCapture={() => setKeyboard(false)}\n >\n <div className={styles.Search}>\n <Search aria-hidden />\n <Combobox.Input\n className={styles.SearchInput}\n placeholder={searchLabel}\n aria-label={searchLabel}\n />\n </div>\n <Combobox.Empty className={listStyles.Empty}>\n {noOptionsText}\n </Combobox.Empty>\n <Combobox.List className={join(listStyles.List, styles.List)}>\n {(option: CountryOption, index: number) => (\n <Fragment key={option.code}>\n {query === \"\" && pinnedCount > 0 && index === pinnedCount && (\n // A line to look at only: a listbox may hold nothing but\n // options, so it has no role of its own.\n <div role=\"presentation\" className={styles.Separator} />\n )}\n <Combobox.Item\n value={option}\n className={join(listStyles.Item, styles.Country)}\n >\n <span aria-hidden className={styles.Flag}>\n {option.flag}\n </span>\n <span className={styles.CountryName}>{option.name}</span>\n <span className={styles.CountryDial}>\n +{option.dialCode}\n </span>\n </Combobox.Item>\n </Fragment>\n )}\n </Combobox.List>\n </Combobox.Popup>\n </Combobox.Positioner>\n </Combobox.Portal>\n </Combobox.Root>\n );\n}\n\n/**\n * Phone number input with a searchable country-code picker at its start, in\n * the same box as `Input`. The number is formatted as you type and validated\n * with `libphonenumber-js`. Lea Bank's core markets (Finland, Sweden, Norway,\n * Spain) are pinned to the top of the country list; the default country follows\n * the active locale.\n *\n * The label, the helper text and the ids that link them to the input are all\n * rendered and wired for you. Takes the native `<input>` attributes (`name`,\n * `placeholder`, `onBlur`, `autoComplete`, …), which land on the number input.\n *\n * Both country and number can be **controlled** (`country` + `onCountryChange`,\n * `value` + `onValueChange`) or left **uncontrolled** (`defaultCountry` /\n * `defaultValue`).\n */\nexport function PhoneNumberInput({\n label,\n helperText,\n error,\n required = false,\n disabled = false,\n id,\n country,\n defaultCountry,\n onCountryChange,\n value,\n defaultValue,\n onValueChange,\n onBlur,\n countries,\n mobileOnly = false,\n validateOnBlur = true,\n className,\n style,\n ref,\n \"aria-describedby\": ariaDescribedBy,\n ...inputAttributes\n}: PhoneNumberInputProps) {\n const language = useLanguage();\n const t = usePhoneLabels();\n const generatedId = useId();\n const inputId = id ?? generatedId;\n const helperTextId = useId();\n const dialCodeId = useId();\n const boxRef = useRef<HTMLDivElement>(null);\n const [countryOpen, setCountryOpen] = useState(false);\n\n // Restricted to `countries`; with one, its dial code is fixed in front.\n const allowed = countries != null && countries.length > 0 ? countries : null;\n const fixed = allowed?.length === 1;\n const allowedKey = allowed?.join(\",\");\n\n // Keyed by the codes, as `countries` may be a new array on every render.\n const options = useMemo(() => {\n const all = getCountryOptions(LANGUAGE_TO_LOCALE[language]);\n const codes = allowedKey?.split(\",\");\n return codes ? all.filter((option) => codes.includes(option.code)) : all;\n }, [language, allowedKey]);\n\n const preferredCountry = defaultCountry ?? LANGUAGE_TO_COUNTRY[language];\n const [countryState, setCountryState] = useState<CountryCode>(\n allowed && !allowed.includes(preferredCountry)\n ? allowed[0]\n : preferredCountry,\n );\n const currentCountry = country ?? countryState;\n const checkOptions = { sameCountry: allowed != null, mobileOnly };\n\n const [valueState, setValueState] = useState<string>(defaultValue ?? \"\");\n const currentValue = value ?? valueState;\n\n const [touched, setTouched] = useState(false);\n\n const check = checkNumber(currentCountry, currentValue, checkOptions);\n const meta: PhoneNumberMeta = {\n country: currentCountry,\n e164: check.normalized,\n isValid: check.valid,\n };\n\n const showValidationError =\n validateOnBlur && touched && currentValue.trim() !== \"\" && !meta.isValid;\n const resolvedError = error ?? showValidationError;\n const resolvedHelper =\n helperText ??\n (showValidationError\n ? check.landline\n ? t.notMobile\n : t.invalid\n : undefined);\n const hasHelperText = resolvedHelper != null;\n\n const emit = (nextValue: string, nextCountry: CountryCode) => {\n const result = checkNumber(nextCountry, nextValue, checkOptions);\n onValueChange?.(nextValue, {\n country: nextCountry,\n e164: result.normalized,\n isValid: result.valid,\n });\n };\n\n const setNumber = (nextValue: string, nextCountry: CountryCode) => {\n if (value === undefined) setValueState(nextValue);\n if (nextCountry !== currentCountry) {\n if (country === undefined) setCountryState(nextCountry);\n onCountryChange?.(nextCountry);\n }\n emit(nextValue, nextCountry);\n };\n\n const handleCountryChange = (next: CountryCode) => {\n if (country === undefined) {\n setCountryState(next);\n }\n onCountryChange?.(next);\n // Re-emit so the consumer's meta (e.g. E.164) reflects the new dial code.\n emit(currentValue, next);\n };\n\n const handleInputChange = (event: ChangeEvent<HTMLInputElement>) => {\n const text = event.target.value;\n\n if (!allowed) {\n setNumber(formatNationalNumber(currentCountry, text), currentCountry);\n return;\n }\n\n // More than one character at once: pasted, or filled in by the browser.\n if (text.length - currentValue.length > 1) {\n // A number of another country in the list switches to that country.\n const parsed = parsePhoneNumberFromString(\n text.replace(/[\\s-]/g, \"\"),\n currentCountry,\n );\n const nextCountry =\n parsed?.country && allowed.includes(parsed.country)\n ? parsed.country\n : currentCountry;\n setNumber(cleanUp(nextCountry, text), nextCountry);\n return;\n }\n\n setNumber(formatTyped(currentCountry, text), currentCountry);\n };\n\n const handleBlur = (event: FocusEvent<HTMLInputElement>) => {\n setTouched(true);\n if (allowed) {\n const cleaned = cleanUp(currentCountry, currentValue);\n if (cleaned !== currentValue) setNumber(cleaned, currentCountry);\n }\n onBlur?.(event);\n };\n\n const fixedOption = fixed\n ? options.find((option) => option.code === currentCountry)\n : undefined;\n\n // The label and the padding are part of the field, so a click on them goes\n // to the number, unless it hit the country button. Clicks in the country\n // list arrive here as well, through the portal, and are not for the number.\n const focusInput = (event: MouseEvent<HTMLDivElement>) => {\n const target = event.target as Element;\n if (!event.currentTarget.contains(target)) return;\n if (target.closest(\"button, input\")) return;\n // By type: Base UI keeps a hidden input for the country in here too.\n event.currentTarget\n .querySelector<HTMLElement>('input[type=\"tel\"]')\n ?.focus();\n };\n\n return (\n <div\n className={join(inputStyles.Field, styles.Field, className)}\n style={style}\n >\n <div\n ref={boxRef}\n className={join(inputStyles.Box, styles.Box)}\n data-invalid={resolvedError ? \"\" : undefined}\n data-disabled={disabled ? \"\" : undefined}\n data-country-open={countryOpen ? \"\" : undefined}\n onClick={focusInput}\n >\n {fixed ? (\n fixedOption && (\n // The dial code is read with the field, as its description.\n <span className={styles.DialCode}>\n <span aria-hidden className={styles.Flag}>\n {fixedOption.flag}\n </span>\n <span id={dialCodeId} className={styles.Dial}>\n +{fixedOption.dialCode}\n </span>\n </span>\n )\n ) : (\n <CountrySelect\n options={options}\n value={currentCountry}\n onChange={handleCountryChange}\n ariaLabel={t.countryLabel}\n searchLabel={t.searchCountry}\n noOptionsText={t.noCountries}\n disabled={disabled}\n open={countryOpen}\n onOpenChange={setCountryOpen}\n anchorRef={boxRef}\n />\n )}\n <div className={join(inputStyles.Inner, styles.Inner)}>\n {label != null && (\n <label\n htmlFor={inputId}\n className={join(inputStyles.Label, styles.Label)}\n >\n {label}\n {required && <RequiredAsterisk />}\n </label>\n )}\n <input\n inputMode=\"tel\"\n autoComplete=\"tel-national\"\n {...inputAttributes}\n ref={ref}\n id={inputId}\n type=\"tel\"\n value={currentValue}\n onChange={handleInputChange}\n onBlur={handleBlur}\n disabled={disabled}\n required={required}\n aria-invalid={resolvedError}\n aria-describedby={join(\n fixedOption && dialCodeId,\n hasHelperText && helperTextId,\n ariaDescribedBy,\n )}\n className={inputStyles.Control}\n />\n </div>\n </div>\n {hasHelperText && (\n <FieldHelperText id={helperTextId} error={resolvedError}>\n {resolvedHelper}\n </FieldHelperText>\n )}\n </div>\n );\n}\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n */\n\n/* The options of a `RadioGroup.Root`, one under the other. */\n.Options {\n display: flex;\n flex-direction: column;\n gap: var(--lea-spacing-1);\n}\n\n/*\n * The radio itself: a bordered circle with a dot that springs in when it is\n * chosen. `RadioCard.Indicator` draws the same circle as a picture, from the\n * same classes, so the two can never drift apart.\n */\n.Circle {\n --radio-size: 1.3rem;\n --radio-dot-size: 0.6rem;\n\n display: inline-flex;\n width: var(--radio-size);\n height: var(--radio-size);\n box-sizing: border-box;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n padding: 0;\n border: 2px solid var(--lea-palette-border-base);\n border-radius: var(--lea-radius-full);\n margin: 0;\n background-color: var(--lea-palette-bg-base);\n cursor: pointer;\n transition: background-color var(--lea-duration-normal)\n var(--lea-easing-standard),\n border-color var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n.Circle[data-size=\"sm\"] {\n --radio-size: 1rem;\n --radio-dot-size: 0.45rem;\n}\n\n@media (hover: hover) {\n .Circle:hover:not([data-checked], [data-disabled]) {\n border-color: var(--lea-palette-border-strong);\n }\n}\n\n.Circle[data-checked] {\n border-color: var(--lea-palette-primary-base);\n background-color: var(--lea-palette-primary-base);\n}\n\n.Circle:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n.Circle[data-disabled] {\n cursor: not-allowed;\n opacity: 0.7;\n}\n\n.Dot {\n width: var(--radio-dot-size);\n height: var(--radio-dot-size);\n border-radius: var(--lea-radius-full);\n background-color: var(--lea-palette-primary-contrast);\n scale: 0;\n transition: scale var(--lea-duration-emphasis) var(--lea-easing-spring);\n}\n\n.Circle[data-checked] .Dot {\n scale: 1;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Circle,\n .Dot {\n transition-duration: 0.01ms;\n }\n}\n","import styles from \"./radio.module.css\";\n\nexport type RadioSize = \"sm\" | \"md\";\n\n/** The classes of the radio visual, for the real control to wear. */\nexport const radioCircleClassName = styles.Circle;\nexport const radioDotClassName = styles.Dot;\n\nexport type RadioCircleProps = {\n /**\n * Fills the circle and springs the dot in. Leave it out when a parent\n * selector decides instead, as `RadioCard.Root` does for its indicator.\n */\n checked?: boolean;\n size?: RadioSize;\n className?: string;\n};\n\n/**\n * Internal. The radio visual as a picture only, for places where something else\n * is the control: `RadioCard.Indicator` shows it inside a card, and the card's\n * own hidden radio is what gets picked. It looks exactly like\n * `RadioGroup.Item`, which puts the same classes on the real control.\n */\nexport const RadioCircle = ({\n checked = false,\n size = \"md\",\n className,\n}: RadioCircleProps) => (\n <span\n aria-hidden\n className={[styles.Circle, className].filter(Boolean).join(\" \")}\n data-size={size}\n data-checked={checked ? \"\" : undefined}\n >\n <span className={styles.Dot} />\n </span>\n);\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this part renders correctly without a provider.\n */\n\n/* The label, the options and the helper text of a radio group, stacked. */\n.Group {\n display: flex;\n width: 100%;\n min-width: 0;\n flex-direction: column;\n}\n","\"use client\";\n\nimport { RadioGroup as BaseRadioGroup } from \"@base-ui/react/radio-group\";\nimport {\n createContext,\n useContext,\n useId,\n useMemo,\n type CSSProperties,\n type FocusEvent,\n type ReactNode,\n type Ref,\n} from \"react\";\n\nimport { FieldGroupLabel, FieldHelperText } from \"./field-parts\";\nimport styles from \"./radio-group-root.module.css\";\nimport type { RadioSize } from \"./radio-circle\";\n\n/** What a radio can stand for. Compared with `===`, so `true` is not `\"true\"`. */\nexport type RadioValue = string | number | boolean;\n\nexport type RadioCardSize = \"sm\" | \"lg\";\n\ntype RadioGroupContextValue = {\n /** Size of the radio circle, set by `RadioGroup.Root`. */\n size?: RadioSize;\n /** Size of the cards, set by `RadioCard.Group`. */\n cardSize?: RadioCardSize;\n};\n\nconst RadioGroupContext = createContext<RadioGroupContextValue | null>(null);\n\n/** Reads the surrounding group. A radio cannot work without one. */\nexport const useRadioGroupContext = (component: string) => {\n const group = useContext(RadioGroupContext);\n if (!group) {\n throw new Error(\n `<${component}> must be rendered inside a <RadioGroup.Root> or <RadioCard.Group>.`,\n );\n }\n return group;\n};\n\n/**\n * Requires an accessible name so the group is never nameless. Provide a\n * visible `label`, or an `aria-label` / `aria-labelledby` when the question\n * is written somewhere else on the page.\n */\ntype RadioGroupAccessibleName =\n | { label: ReactNode; \"aria-label\"?: never; \"aria-labelledby\"?: never }\n | { label?: never; \"aria-label\": string; \"aria-labelledby\"?: never }\n | { label?: never; \"aria-label\"?: never; \"aria-labelledby\": string };\n\n/** The props `RadioGroup.Root` and `RadioCard.Group` have in common. */\nexport type RadioGroupBaseProps<T extends RadioValue> =\n RadioGroupAccessibleName & {\n /** Help text behind an info button next to the label. Needs a `label`. */\n tooltip?: ReactNode;\n /** Helper / error text shown below the options and linked via `aria-describedby`. */\n helperText?: ReactNode;\n /** Puts the group and its helper text into the error state. */\n error?: boolean;\n /** The chosen value (controlled). `null` means nothing is chosen. */\n value?: T | null;\n /** The chosen value when first rendered, for uncontrolled use. */\n defaultValue?: T;\n /** Called with the value of the option the user picks. */\n onValueChange?: (value: T) => void;\n /** Submits the choice under this name when inside a form. */\n name?: string;\n /** Disables every option. */\n disabled?: boolean;\n /** Marks the group as required and adds the asterisk to the label. */\n required?: boolean;\n /** Called when focus leaves the group, not when it moves between options. */\n onBlur?: (event: FocusEvent<HTMLDivElement>) => void;\n /** Id of the element with the `radiogroup` role. */\n id?: string;\n /** Extra ids describing the group, alongside the helper text. */\n \"aria-describedby\"?: string;\n /** Class on the outermost element, for layout. */\n className?: string;\n /** Inline style on the outermost element, for layout. */\n style?: CSSProperties;\n /**\n * The `<input>` of the radio that takes focus: the chosen one, or the\n * first that is not disabled. It is hidden, but `ref.current.focus()`\n * moves focus to the visible radio.\n */\n ref?: Ref<HTMLInputElement>;\n /**\n * `RadioGroup.Item` / `RadioCard.Root` elements, instead of or after `options`.\n */\n children?: ReactNode;\n };\n\ntype RadioGroupRootProps<T extends RadioValue> = RadioGroupBaseProps<T> & {\n size?: RadioSize;\n cardSize?: RadioCardSize;\n /** Layout of the options. */\n optionsClassName: string;\n optionsStyle?: CSSProperties;\n /** `\"many\"` when the options sit in more than one column. */\n optionsDataColumns?: string;\n};\n\nconst join = (...parts: (string | false | null | undefined)[]) =>\n parts.filter(Boolean).join(\" \") || undefined;\n\n/**\n * Internal. Everything `RadioGroup.Root` and `RadioCard.Group` share: Base UI's\n * `RadioGroup`, which holds the value and moves focus with the arrow keys,\n * and the label, tooltip, helper text and error state around the options.\n */\nexport function RadioGroupRoot<T extends RadioValue>({\n label,\n tooltip,\n helperText,\n error = false,\n value,\n defaultValue,\n onValueChange,\n name,\n disabled = false,\n required = false,\n onBlur,\n id,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n \"aria-describedby\": ariaDescribedBy,\n className,\n style,\n ref,\n size,\n cardSize,\n optionsClassName,\n optionsStyle,\n optionsDataColumns,\n children,\n}: RadioGroupRootProps<T>) {\n // Ids are wired by hand so the name and description of the group are\n // already in the markup the server sends.\n const labelId = useId();\n const helperTextId = useId();\n const hasLabel = label != null;\n const hasHelperText = helperText != null;\n\n const group = useMemo<RadioGroupContextValue>(\n () => ({ size, cardSize }),\n [size, cardSize],\n );\n\n return (\n <div className={join(styles.Group, className)} style={style}>\n {hasLabel && (\n <FieldGroupLabel\n id={labelId}\n tooltip={tooltip}\n required={required}\n error={error}\n disabled={disabled}\n >\n {label}\n </FieldGroupLabel>\n )}\n <RadioGroupContext.Provider value={group}>\n <BaseRadioGroup\n id={id}\n inputRef={ref}\n name={name}\n value={value}\n defaultValue={defaultValue}\n onValueChange={(next) => onValueChange?.(next as T)}\n disabled={disabled}\n required={required}\n aria-label={ariaLabel}\n aria-labelledby={hasLabel ? labelId : ariaLabelledBy}\n aria-describedby={join(\n hasHelperText && helperTextId,\n ariaDescribedBy,\n )}\n aria-invalid={error ? true : undefined}\n onBlur={(event) => {\n // Moving between two options is not leaving the group.\n if (event.currentTarget.contains(event.relatedTarget)) return;\n onBlur?.(event);\n }}\n className={optionsClassName}\n style={optionsStyle}\n data-columns={optionsDataColumns}\n >\n {children}\n </BaseRadioGroup>\n </RadioGroupContext.Provider>\n {hasHelperText && (\n <FieldHelperText id={helperTextId} error={error}>\n {helperText}\n </FieldHelperText>\n )}\n </div>\n );\n}\n","\"use client\";\n\nimport { Radio as BaseRadio } from \"@base-ui/react/radio\";\nimport type { CSSProperties, ReactNode, Ref } from \"react\";\n\nimport rowStyles from \"../choice-row.module.css\";\nimport {\n radioCircleClassName,\n radioDotClassName,\n type RadioSize,\n} from \"../radio-circle\";\nimport {\n RadioGroupRoot,\n useRadioGroupContext,\n type RadioGroupBaseProps,\n type RadioValue,\n} from \"../radio-group-root\";\nimport styles from \"../radio.module.css\";\n\nexport type { RadioSize as Size, RadioValue as Value };\n\n/**\n * One choice in the `options` list of a `RadioGroup.Root` or `RadioCard.Group`.\n */\nexport type Option<T extends RadioValue = string> = {\n value: T;\n label: ReactNode;\n disabled?: boolean;\n};\n\nexport type ItemProps<T extends RadioValue = RadioValue> = {\n /** What `onValueChange` on the group is called with when this radio is picked. */\n value: T;\n /** The text next to the radio. Without it, pass `aria-label`. */\n label?: ReactNode;\n /** Disables this radio only. */\n disabled?: boolean;\n /** Size of the circle. Defaults to the group's `size`. */\n size?: RadioSize;\n /** Id of the radio itself, the element with the `radio` role. */\n id?: string;\n /** Names the radio when it has no `label`. */\n \"aria-label\"?: string;\n /** Class on the outermost element. */\n className?: string;\n /** Inline style on the outermost element. */\n style?: CSSProperties;\n /**\n * The `<input type=\"radio\">` that holds the value. It is hidden, but\n * `ref.current.focus()` moves focus to the visible radio.\n */\n ref?: Ref<HTMLInputElement>;\n};\n\nconst join = (...parts: (string | false | null | undefined)[]) =>\n parts.filter(Boolean).join(\" \") || undefined;\n\n/**\n * One choice in a `RadioGroup.Root`, for when the `options` list is not enough:\n * a label with markup in it, or content between the choices. The group holds\n * the value, so a `RadioGroup.Item` only works inside one.\n *\n * ```tsx\n * <RadioGroup.Root label=\"Betaling\" value={value} onValueChange={setValue}>\n * <RadioGroup.Item value=\"monthly\" label=\"Månedlig\" />\n * <RadioGroup.Item value=\"yearly\" label={<>Årlig <Chip label=\"Spar 10 %\" /></>} />\n * </RadioGroup.Root>\n * ```\n */\nexport function Item<T extends RadioValue = RadioValue>({\n value,\n label,\n disabled = false,\n size,\n id,\n \"aria-label\": ariaLabel,\n className,\n style,\n ref,\n}: ItemProps<T>) {\n const group = useRadioGroupContext(\"RadioGroup.Item\");\n const bare = label == null;\n\n const circle = (\n <BaseRadio.Root\n // A real `<button>`, so the `<label>` around it names it natively: in\n // the server markup, and as one element for `getByLabelText`.\n nativeButton\n render={<button type=\"button\" />}\n id={id}\n inputRef={ref}\n value={value}\n disabled={disabled}\n aria-label={ariaLabel}\n data-size={size ?? group.size ?? \"md\"}\n className={\n bare ? join(radioCircleClassName, className) : radioCircleClassName\n }\n style={bare ? style : undefined}\n >\n <BaseRadio.Indicator keepMounted className={radioDotClassName} />\n </BaseRadio.Root>\n );\n\n if (bare) return circle;\n\n return (\n <label className={join(rowStyles.Row, className)} style={style}>\n {circle}\n <span className={rowStyles.Text}>{label}</span>\n </label>\n );\n}\n\nexport type RootProps<T extends RadioValue = string> =\n RadioGroupBaseProps<T> & {\n /** The choices, each rendered as a radio with its label. */\n options?: Option<T>[];\n /**\n * Size of the radio circles. A `size` on a `RadioGroup.Item` wins.\n * @default \"md\"\n */\n size?: RadioSize;\n };\n\n/**\n * A question with one answer, as a list of radios, on Base UI's accessible\n * `RadioGroup` primitive. The label, the helper text and the error state are\n * handled for you. Arrow keys move between the choices.\n *\n * Pass the choices as `options`. Values are compared with `===` and can be\n * strings, numbers or booleans, so a yes / no question can hold a real\n * `boolean`. For choices that need more than a label, pass `RadioGroup.Item`\n * children.\n *\n * ```tsx\n * <RadioGroup.Root\n * label=\"Hvor ofte vil du betale?\"\n * options={[\n * { value: \"monthly\", label: \"Månedlig\" },\n * { value: \"yearly\", label: \"Årlig\" },\n * ]}\n * value={value}\n * onValueChange={setValue}\n * />\n * ```\n */\nexport function Root<T extends RadioValue = string>({\n options,\n children,\n ...rest\n}: RootProps<T>) {\n return (\n <RadioGroupRoot {...rest} optionsClassName={styles.Options}>\n {options?.map((option) => (\n <Item\n key={String(option.value)}\n value={option.value}\n label={option.label}\n disabled={option.disabled}\n />\n ))}\n {children}\n </RadioGroupRoot>\n );\n}\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n */\n\n/* The cards of a `RadioCard.Group`, in as many columns as asked for. */\n.Options {\n display: grid;\n /* A list of cards sits tighter than a grid of them. */\n gap: var(--lea-spacing-1);\n grid-template-columns: repeat(var(--radio-card-columns, 1), 1fr);\n}\n\n.Options[data-columns=\"many\"] {\n gap: var(--lea-spacing-1-5);\n}\n\n.Card {\n --radio-card-padding-block: var(--lea-spacing-2);\n\n position: relative;\n display: flex;\n align-items: center;\n justify-content: flex-start;\n padding-block: var(--radio-card-padding-block);\n padding-inline: var(--lea-spacing-2);\n border: 1px solid var(--lea-palette-border-base);\n border-radius: var(--lea-radius-xl);\n background-color: var(--lea-palette-bg-base);\n color: var(--lea-palette-text-base);\n cursor: pointer;\n font-size: var(--lea-text-body-md-font-size);\n font-weight: var(--lea-text-body-md-font-weight);\n gap: var(--lea-spacing-1-5);\n letter-spacing: var(--lea-text-body-md-letter-spacing);\n line-height: var(--lea-text-body-md-line-height);\n text-align: start;\n transition: background-color var(--lea-duration-normal)\n var(--lea-easing-standard),\n border-color var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n.Card[data-size=\"sm\"] {\n --radio-card-padding-block: var(--lea-spacing-1-5);\n}\n\n.Card[data-orientation=\"vertical\"] {\n --radio-card-padding-block: var(--lea-spacing-4);\n\n flex-direction: column;\n justify-content: center;\n text-align: center;\n}\n\n.Card[data-orientation=\"vertical\"][data-size=\"sm\"] {\n --radio-card-padding-block: var(--lea-spacing-2);\n}\n\n@media (hover: hover) {\n .Card:hover:not(:has(.Radio[data-disabled])) {\n border-color: var(--lea-palette-border-strong);\n }\n}\n\n/*\n * The card reads its state off the radio inside it, which Base UI marks.\n * A chosen card changes border and fill, and its indicator fills in.\n */\n.Card:has(.Radio[data-checked]) {\n border-color: var(--lea-palette-primary-base);\n background-color: var(--lea-palette-bg-accent);\n}\n\n/* Keyboard focus is on the hidden radio, so the ring is drawn on the card. */\n.Card:has(.Radio:focus-visible) {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n.Card:has(.Radio[data-disabled]) {\n cursor: not-allowed;\n opacity: 0.6;\n}\n\n/*\n * The real control. It is there for the keyboard and for assistive\n * technology, and takes no room: the card around it is what you see and click.\n */\n.Radio {\n position: absolute;\n overflow: hidden;\n width: 1px;\n height: 1px;\n padding: 0;\n border: 0;\n margin: 0;\n clip-path: inset(50%);\n white-space: nowrap;\n}\n\n.Card:has(.Radio[data-checked]) .Indicator {\n border-color: var(--lea-palette-primary-base);\n background-color: var(--lea-palette-primary-base);\n}\n\n.Card:has(.Radio[data-checked]) .Indicator > * {\n scale: 1;\n}\n\n.Icon {\n flex-shrink: 0;\n color: var(--lea-palette-text-base);\n}\n\n.Content {\n display: flex;\n min-width: 0;\n flex: 1;\n flex-direction: column;\n align-items: flex-start;\n /* 2px between the two lines, tighter than any step: they read as one block. */\n gap: calc(var(--lea-spacing) * 0.25);\n text-align: start;\n}\n\n.Card[data-orientation=\"vertical\"] .Content {\n flex: none;\n align-items: center;\n text-align: center;\n}\n\n.Label {\n display: block;\n color: var(--lea-palette-text-base);\n font-size: var(--lea-text-label-lg-font-size);\n font-weight: var(--lea-text-label-lg-font-weight);\n letter-spacing: var(--lea-text-label-lg-letter-spacing);\n line-height: var(--lea-text-label-lg-line-height);\n}\n\n.Description {\n display: block;\n color: var(--lea-palette-text-subtle);\n font-size: var(--lea-text-body-sm-font-size);\n font-weight: var(--lea-text-body-sm-font-weight);\n letter-spacing: var(--lea-text-body-sm-letter-spacing);\n line-height: var(--lea-text-body-sm-line-height);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Card {\n transition-duration: 0.01ms;\n }\n}\n","\"use client\";\n\nimport { Radio as BaseRadio } from \"@base-ui/react/radio\";\nimport { LucideIcon } from \"lucide-react\";\nimport type { CSSProperties, ReactNode, Ref } from \"react\";\n\nimport styles from \"./radio-card.module.css\";\nimport type { Option } from \"../radio/radio\";\nimport { RadioCircle, type RadioSize } from \"../radio-circle\";\nimport {\n RadioGroupRoot,\n useRadioGroupContext,\n type RadioCardSize,\n type RadioGroupBaseProps,\n type RadioValue,\n} from \"../radio-group-root\";\n\nconst join = (...parts: (string | false | null | undefined)[]) =>\n parts.filter(Boolean).join(\" \") || undefined;\n\nexport type RootProps<T extends RadioValue = RadioValue> = {\n /** What `onValueChange` on the group is called with when this card is picked. */\n value: T;\n /** `\"horizontal\"` (indicator + text in a row) or `\"vertical\"` (icon stacked above text). */\n orientation?: \"horizontal\" | \"vertical\";\n /** Disables this card only. */\n disabled?: boolean;\n /** Padding of the card. Defaults to the group's `size`. */\n size?: RadioCardSize;\n /** Class on the card. */\n className?: string;\n /** Inline style on the card. */\n style?: CSSProperties;\n /** The card, which is a `<label>` around the radio. */\n ref?: Ref<HTMLLabelElement>;\n children?: ReactNode;\n};\n\n/**\n * One choice in a `RadioCard.Group`, for when the `options` list is not\n * enough. The whole card is clickable, and it is built from composable\n * pieces: `RadioCard.Content` (wraps the text), `RadioCard.Indicator`\n * (optional), `RadioCard.Icon`, `RadioCard.Label`, `RadioCard.Description`.\n *\n * Children are laid out as plain flex items, so the indicator is just another\n * child — place it before or after `RadioCard.Content` and it sits at that edge\n * (the content grows to fill the row, pushing the indicator to the side).\n *\n * The group holds the value, so a `RadioCard.Root` only works inside one.\n *\n * ```tsx\n * <RadioCard.Group label=\"Nedbetalingstid\" value={value} onValueChange={setValue}>\n * <RadioCard.Root value={10}>\n * <RadioCard.Content>\n * <RadioCard.Label>1316 kr/mnd</RadioCard.Label>\n * <RadioCard.Description>Over 10 år</RadioCard.Description>\n * </RadioCard.Content>\n * <RadioCard.Indicator />\n * </RadioCard.Root>\n * </RadioCard.Group>\n * ```\n */\nexport function Root<T extends RadioValue = RadioValue>({\n value,\n orientation = \"horizontal\",\n disabled = false,\n size,\n className,\n style,\n ref,\n children,\n}: RootProps<T>) {\n const group = useRadioGroupContext(\"RadioCard.Root\");\n\n return (\n <label\n ref={ref}\n className={join(styles.Card, className)}\n style={style}\n data-orientation={orientation}\n data-size={size ?? group.cardSize ?? \"lg\"}\n >\n <BaseRadio.Root\n // A real `<button>`, so the card, which is its `<label>`, names it\n // natively: in the server markup, and as one element for\n // `getByLabelText`.\n nativeButton\n render={<button type=\"button\" />}\n value={value}\n disabled={disabled}\n className={styles.Radio}\n />\n {children}\n </label>\n );\n}\n\nexport type IndicatorProps = {\n /** Size of the circle. @default \"md\" */\n size?: RadioSize;\n};\n\n/**\n * Optional radio dot for a `RadioCard.Root`. It is a picture of the card's\n * state, drawn like a `RadioGroup.Item`; the card's own radio is what gets\n * picked. Omit it for icon-only cards.\n */\nexport const Indicator = ({ size }: IndicatorProps) => (\n <RadioCircle size={size} className={styles.Indicator} />\n);\n\nexport type IconProps = {\n /** Icon box size (predefined, overridable). */\n size?: number | string;\n icon: LucideIcon;\n};\n\n/**\n * Fixed-size icon slot so every card's icon renders at the same size regardless\n * of the icon passed in.\n */\nexport const Icon = ({ size = 24, icon }: IconProps) => {\n const Glyph = icon;\n return <Glyph size={size} aria-hidden className={styles.Icon} />;\n};\n\n/**\n * Wraps the text lines (`RadioCard.Label` / `RadioCard.Description`) into a\n * column. Grows to fill the row in `horizontal` orientation — so a sibling\n * `RadioCard.Indicator` is pushed to the opposite edge — and centers in\n * `vertical`.\n */\nexport const Content = ({\n children,\n className,\n style,\n}: {\n children: ReactNode;\n className?: string;\n style?: CSSProperties;\n}) => (\n <span className={join(styles.Content, className)} style={style}>\n {children}\n </span>\n);\n\n/** Primary line of a `RadioCard.Root`. */\nexport const Label = ({ children }: { children: ReactNode }) => (\n <span className={styles.Label}>{children}</span>\n);\n\n/** Secondary, muted line of a `RadioCard.Root`. */\nexport const Description = ({ children }: { children: ReactNode }) => (\n <span className={styles.Description}>{children}</span>\n);\n\nexport type GroupProps<T extends RadioValue = string> =\n RadioGroupBaseProps<T> & {\n /**\n * The choices, each rendered as a card with a radio dot and its label.\n * For icons, descriptions or cards without the dot, pass `RadioCard.Root`\n * children instead.\n */\n options?: Option<T>[];\n /** How many cards sit side by side. @default 1 */\n columns?: number;\n /**\n * Padding of the cards. A `size` on a `RadioCard.Root` wins.\n * @default \"lg\"\n */\n size?: RadioCardSize;\n };\n\n/**\n * A question with one answer, as a set of selectable cards, on Base UI's\n * accessible `RadioGroup` primitive. The label, the tooltip, the helper text\n * and the error state are handled for you. Arrow keys move between the cards.\n *\n * Pass the choices as `options`. Values are compared with `===` and can be\n * strings, numbers or booleans, so a yes / no question can hold a real\n * `boolean`:\n *\n * ```tsx\n * <RadioCard.Group\n * label=\"Vil du legge til en medsøker?\"\n * columns={2}\n * size=\"sm\"\n * options={[\n * { value: true, label: \"Ja\" },\n * { value: false, label: \"Nei\" },\n * ]}\n * value={hasCoApplicant}\n * onValueChange={setHasCoApplicant}\n * />\n * ```\n *\n * For richer cards, pass `RadioCard.Root` children.\n */\nexport function Group<T extends RadioValue = string>({\n options,\n columns = 1,\n size,\n children,\n ...rest\n}: GroupProps<T>) {\n return (\n <RadioGroupRoot\n {...rest}\n cardSize={size}\n optionsClassName={styles.Options}\n optionsStyle={{ \"--radio-card-columns\": columns } as CSSProperties}\n optionsDataColumns={columns > 1 ? \"many\" : undefined}\n >\n {options?.map((option) => (\n <Root\n key={String(option.value)}\n value={option.value}\n disabled={option.disabled}\n >\n <Indicator />\n {option.label}\n </Root>\n ))}\n {children}\n </RadioGroupRoot>\n );\n}\n\nexport type { Option, RadioCardSize as Size, RadioValue as Value };\n","\"use client\";\n\nimport { useState } from \"react\";\n\nimport {\n OptionIcon,\n SelectField,\n SelectFieldItem,\n type SelectFieldBaseProps,\n type SelectOption,\n type SelectSize,\n} from \"../select-field\";\n\nexport type { SelectOption, SelectSize };\n\nexport type SelectProps<Value extends string = string> =\n SelectFieldBaseProps & {\n /** The choices. */\n options: readonly SelectOption<Value>[];\n /** The chosen value (controlled). `\"\"` means nothing is chosen. */\n value?: Value | \"\";\n /** The chosen value when first rendered, for uncontrolled use. */\n defaultValue?: Value | \"\";\n /** Called with the value of the option the user picks. */\n onValueChange?: (value: Value) => void;\n /**\n * `\"sm\"` is a compact 32px field for a narrow place, with the `label`\n * as a line above it instead of floating inside.\n * @default \"md\"\n */\n size?: SelectSize;\n };\n\n/**\n * Select on Base UI's `Select`, in the same box as `Input`. The label, the\n * helper text and the ids that link them to the control are all rendered and\n * wired for you.\n *\n * ```tsx\n * <Select\n * label=\"Land\"\n * options={[\n * { value: \"no\", label: \"Norge\" },\n * { value: \"se\", label: \"Sverige\" },\n * ]}\n * value={country}\n * onValueChange={setCountry}\n * />\n * ```\n *\n * Values are strings, and `\"\"` means nothing is chosen. Written with\n * `as const`, the options give `value` and `onValueChange` their type:\n * `\"no\" | \"se\"` here.\n *\n * An option can carry an `icon` (a flag, a logo), shown before its label in\n * the list and, once it is chosen, in the field. `size=\"sm\"` is a compact\n * 32px field with its label above it.\n */\nexport function Select<Value extends string = string>({\n options,\n value,\n defaultValue = \"\",\n onValueChange,\n startAdornment,\n ...rest\n}: SelectProps<Value>) {\n const isControlled = value !== undefined;\n const [internal, setInternal] = useState<Value | \"\">(defaultValue);\n const current = isControlled ? value : internal;\n\n const chosen = options.find((option) => option.value === current);\n\n return (\n <SelectField\n {...rest}\n startAdornment={\n startAdornment ??\n (chosen?.icon != null ? <OptionIcon>{chosen.icon}</OptionIcon> : null)\n }\n multiple={false}\n // Base UI's \"nothing chosen\" is `null`; an option may still have `\"\"`\n // as its value, e.g. \"None\", and is then a choice like any other.\n value={chosen ? chosen.value : null}\n onValueChange={(next) => {\n const nextValue = (next ?? \"\") as Value;\n if (!isControlled) setInternal(nextValue);\n onValueChange?.(nextValue);\n }}\n filled={chosen != null}\n display={chosen?.label}\n displayHidden\n valueText={chosen?.label ?? \"\"}\n >\n {options.map((option) => (\n <SelectFieldItem key={option.value} option={option} />\n ))}\n </SelectField>\n );\n}\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n *\n * The track, the min / max row and the helper text are styled in\n * slider-parts.module.css, which `AmountSlider` shares.\n */\n\n.Slider {\n display: flex;\n width: 100%;\n flex-direction: column;\n color: var(--lea-palette-text-base);\n font-size: var(--lea-text-body-md-font-size);\n font-weight: var(--lea-text-body-md-font-weight);\n line-height: var(--lea-text-body-md-line-height);\n letter-spacing: var(--lea-text-body-md-letter-spacing);\n}\n\n.Header {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: calc(var(--lea-spacing) * 2);\n}\n\n.Label {\n min-width: 0;\n font-weight: var(--lea-font-weight-medium);\n}\n\n.Value {\n margin-inline-start: auto;\n font-weight: var(--lea-font-weight-semibold);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n}\n\n/*\n * `color=\"contrast\"`, for a dark surface (the website's Calculator): white\n * text, fill and thumb, the thumb ringed in near-black as on the website.\n * The rail is the text colour at 40 %, so it shows on `bg.contrast` and on\n * `bg.contrast-accent` with the Calculator's 30 % black over it (3 : 1 or\n * more), where the website's black rail disappears on black. The parts read\n * these in slider-parts.module.css.\n */\n.Slider[data-color=\"contrast\"] {\n --slider-rail: color-mix(\n in srgb,\n var(--lea-palette-text-contrast) 40%,\n transparent\n );\n --slider-fill: var(--lea-palette-text-contrast);\n --slider-thumb-ring: var(--lea-palette-bg-contrast);\n --slider-focus: var(--lea-palette-text-contrast);\n --slider-secondary: color-mix(\n in srgb,\n var(--lea-palette-text-contrast) 75%,\n transparent\n );\n\n color: var(--lea-palette-text-contrast);\n}\n","\"use client\";\n\nimport { Field } from \"@base-ui/react/field\";\nimport { Slider as BaseSlider } from \"@base-ui/react/slider\";\nimport { useId, type ReactNode } from \"react\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport type { ColorRoles } from \"../../color-roles\";\nimport {\n renderWithoutElement,\n SliderHelperText,\n SliderRange,\n SliderTrack,\n} from \"../slider-parts\";\nimport styles from \"./slider.module.css\";\n\n/**\n * Requires an accessible name so the slider is never nameless. Provide a\n * visible `label`, or an `aria-label` when the design has none.\n */\n/**\n * `\"primary\"` for a light surface; `\"contrast\"` for a dark one\n * (`bg.contrast`, `bg.contrast-accent`, the website `Calculator`'s panel).\n */\nexport type SliderColor = ColorRoles<\"primary\" | \"contrast\">;\n\ntype SliderAccessibleName =\n | { label: ReactNode; \"aria-label\"?: never }\n | { label?: never; \"aria-label\": string };\n\nexport type SliderProps = Omit<\n BaseSlider.Root.Props<number>,\n | \"children\"\n | \"orientation\"\n | \"minStepsBetweenValues\"\n | \"thumbAlignment\"\n | \"thumbCollisionBehavior\"\n | \"aria-label\"\n | \"aria-labelledby\"\n> &\n SliderAccessibleName & {\n /** Helper / error text shown below the slider and linked via `aria-describedby`. */\n helperText?: ReactNode;\n /** Puts the slider and its helper text into the error state. */\n error?: boolean;\n /**\n * `\"primary\"`: purple on a light surface. `\"contrast\"`: white text, a\n * white fill and thumb, for a dark surface such as the website's\n * `Calculator`.\n * @default \"primary\"\n */\n color?: SliderColor;\n };\n\n/**\n * A single-value slider built on Base UI's accessible `Slider` primitive.\n *\n * The label, the current value, the `min` / `max` under each end of the track\n * and the helper text are all rendered and wired internally: the label names\n * the control, the helper text describes it, and `error` marks it invalid and\n * announces the helper text. Base UI renders a native range input, so arrow\n * keys, Page Up / Down, Home and End work, and the value is submitted when\n * `name` is set.\n *\n * Pass `format` and `locale` to control how the value, `min` and `max` are\n * written and announced. On a dark surface, pass `color=\"contrast\"`.\n *\n * ```tsx\n * <Slider\n * label=\"Lånebeløp\"\n * min={50000}\n * max={600000}\n * step={5000}\n * defaultValue={150000}\n * locale=\"nb-NO\"\n * format={{ style: \"currency\", currency: \"NOK\", maximumFractionDigits: 0 }}\n * onValueChange={(value) => setAmount(value)}\n * />\n * ```\n */\nexport function Slider({\n label,\n \"aria-label\": ariaLabel,\n helperText,\n error = false,\n color = \"primary\",\n min = 0,\n max = 100,\n format,\n locale,\n className,\n ...rest\n}: SliderProps) {\n // Base UI's `Slider.Label` only links itself after mount, which leaves the\n // server-rendered control nameless, so the label is wired by id here.\n const labelId = useId();\n const helperTextId = useId();\n\n return (\n // The field renders no element of its own, so `className`, `ref` and the\n // rest land on the slider.\n <Field.Root invalid={error} render={renderWithoutElement}>\n <BaseSlider.Root\n {...rest}\n min={min}\n max={max}\n format={format}\n locale={locale}\n aria-labelledby={label != null ? labelId : undefined}\n data-color={color}\n className={mergeClassName(styles.Slider, className)}\n >\n <div className={styles.Header}>\n {label != null && (\n <div id={labelId} className={styles.Label}>\n {label}\n </div>\n )}\n <BaseSlider.Value className={styles.Value} />\n </div>\n <SliderTrack\n aria-label={ariaLabel}\n aria-describedby={helperText != null ? helperTextId : undefined}\n />\n <SliderRange min={min} max={max} format={format} locale={locale} />\n {helperText != null && (\n <SliderHelperText id={helperTextId} error={error}>\n {helperText}\n </SliderHelperText>\n )}\n </BaseSlider.Root>\n </Field.Root>\n );\n}\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n */\n\n.Switch {\n display: inline-flex;\n width: 2.5rem;\n height: 1.5rem;\n flex-shrink: 0;\n align-items: center;\n padding: 0.125rem;\n border: none;\n border-radius: var(--lea-radius-full);\n background-color: var(--lea-color-neutral-200);\n cursor: pointer;\n transition: background-color var(--lea-duration-normal)\n var(--lea-easing-standard);\n}\n\n.Switch[data-checked] {\n background-color: var(--lea-palette-primary-base);\n}\n\n.Switch:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n.Switch[data-disabled] {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n.Thumb {\n display: block;\n width: 1.25rem;\n height: 1.25rem;\n border-radius: var(--lea-radius-full);\n background-color: var(--lea-palette-bg-base);\n transition: translate var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n.Thumb[data-checked] {\n translate: 1rem 0;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Switch,\n .Thumb {\n transition-duration: 0.01ms;\n }\n}\n","\"use client\";\n\nimport { Switch as BaseSwitch } from \"@base-ui/react/switch\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport styles from \"./switch.module.css\";\n\n/**\n * Requires an accessible name so the switch is never nameless. Provide exactly\n * one of `aria-label` or `aria-labelledby`.\n */\ntype SwitchAccessibleName =\n | { \"aria-label\": string; \"aria-labelledby\"?: never }\n | { \"aria-labelledby\": string; \"aria-label\"?: never };\n\nexport type SwitchProps = Omit<\n BaseSwitch.Root.Props,\n \"children\" | \"aria-label\" | \"aria-labelledby\"\n> &\n SwitchAccessibleName;\n\n/**\n * An on/off toggle built on Base UI's accessible `Switch` primitive.\n *\n * Base UI renders the `switch` role, keeps `aria-checked` in sync and submits\n * a hidden input when `name` is set. The thumb is rendered internally so it\n * can never be left out, and the slide respects `prefers-reduced-motion`.\n *\n * Unlike a checkbox, a switch takes effect immediately — use it for settings\n * that apply on toggle, not for values confirmed by a submit button.\n *\n * ```tsx\n * <Switch\n * aria-label=\"Analyse-informasjonskapsler\"\n * defaultChecked\n * onCheckedChange={(checked) => save(checked)}\n * />\n * ```\n */\nexport function Switch({ className, ...rest }: SwitchProps) {\n return (\n <BaseSwitch.Root\n {...rest}\n className={mergeClassName(styles.Switch, className)}\n >\n <BaseSwitch.Thumb className={styles.Thumb} />\n </BaseSwitch.Root>\n );\n}\n","/*\n * Responsive props arrive as inline `--_<prop>-<breakpoint>` properties, set\n * only for the breakpoints the consumer gave. Each media query falls back\n * through the smaller breakpoints, so `{ base: 1, md: 3 }` holds 1 until `md`.\n *\n * Custom properties inherit, so each rule resets every breakpoint first: a\n * nested `Grid` or `GridItem` would otherwise take its parent's columns, gap\n * or span. The inline properties of the props that are passed beat it.\n */\n.Grid {\n --_columns-base: initial;\n --_columns-sm: initial;\n --_columns-md: initial;\n --_columns-lg: initial;\n --_columns-xl: initial;\n --_gap-base: initial;\n --_gap-sm: initial;\n --_gap-md: initial;\n --_gap-lg: initial;\n --_gap-xl: initial;\n --_columns: var(--_columns-base, 12);\n --_gap: var(--_gap-base, 0);\n\n display: grid;\n min-width: 0;\n gap: calc(var(--lea-spacing) * var(--_gap));\n grid-template-columns: repeat(var(--_columns), minmax(0, 1fr));\n}\n\n/* A `GridItem` given no `span` takes the whole row; plain children take one column. */\n.GridItem {\n --_span-base: initial;\n --_span-sm: initial;\n --_span-md: initial;\n --_span-lg: initial;\n --_span-xl: initial;\n --_span: var(--_span-base, 1 / -1);\n\n min-width: 0;\n grid-column: var(--_span);\n}\n\n@media (min-width: 600px) {\n .Grid {\n --_columns: var(--_columns-sm, var(--_columns-base, 12));\n --_gap: var(--_gap-sm, var(--_gap-base, 0));\n }\n\n .GridItem {\n --_span: var(--_span-sm, var(--_span-base, 1 / -1));\n }\n}\n\n@media (min-width: 900px) {\n .Grid {\n --_columns: var(--_columns-md, var(--_columns-sm, var(--_columns-base, 12)));\n --_gap: var(--_gap-md, var(--_gap-sm, var(--_gap-base, 0)));\n }\n\n .GridItem {\n --_span: var(--_span-md, var(--_span-sm, var(--_span-base, 1 / -1)));\n }\n}\n\n@media (min-width: 1200px) {\n .Grid {\n --_columns: var(\n --_columns-lg,\n var(--_columns-md, var(--_columns-sm, var(--_columns-base, 12)))\n );\n --_gap: var(--_gap-lg, var(--_gap-md, var(--_gap-sm, var(--_gap-base, 0))));\n }\n\n .GridItem {\n --_span: var(\n --_span-lg,\n var(--_span-md, var(--_span-sm, var(--_span-base, 1 / -1)))\n );\n }\n}\n\n@media (min-width: 1536px) {\n .Grid {\n --_columns: var(\n --_columns-xl,\n var(\n --_columns-lg,\n var(--_columns-md, var(--_columns-sm, var(--_columns-base, 12)))\n )\n );\n --_gap: var(\n --_gap-xl,\n var(--_gap-lg, var(--_gap-md, var(--_gap-sm, var(--_gap-base, 0))))\n );\n }\n\n .GridItem {\n --_span: var(\n --_span-xl,\n var(\n --_span-lg,\n var(--_span-md, var(--_span-sm, var(--_span-base, 1 / -1)))\n )\n );\n }\n}\n","import type { CSSProperties } from \"react\";\n\nimport type { Breakpoint } from \"../../theme/tokens/breakpoints\";\n\n/** `base` applies from zero width; each other key from its breakpoint up. */\nexport type ResponsiveKey = \"base\" | Exclude<Breakpoint, \"xs\">;\n\n/**\n * A single value, or one per breakpoint, mobile-first. Unset breakpoints keep\n * the value of the nearest smaller one: `{ base: 1, md: 3 }` is 1 below `md`\n * and 3 from `md` up.\n */\nexport type Responsive<T> = T | Partial<Record<ResponsiveKey, T>>;\n\nconst isPerBreakpoint = <T>(\n value: Responsive<T>,\n): value is Partial<Record<ResponsiveKey, T>> =>\n typeof value === \"object\" && value !== null;\n\n/**\n * Writes a responsive prop as inline custom properties — `--_gap-base`,\n * `--_gap-md`, … — which the component's CSS Module picks up in its media\n * queries. Only the breakpoints that are set are written; the stylesheet\n * falls back through the smaller ones.\n *\n * Internal helper — not part of the public API.\n */\nexport function responsiveVars<T>(\n name: string,\n value: Responsive<T> | undefined,\n format: (value: T) => string | number = String,\n): CSSProperties {\n if (value === undefined) return {};\n\n const entries = isPerBreakpoint(value)\n ? Object.entries(value as Record<string, T | undefined>)\n : [[\"base\", value] as const];\n\n const vars: Record<string, string | number> = {};\n for (const [key, entry] of entries) {\n if (entry !== undefined) vars[`--_${name}-${key}`] = format(entry);\n }\n\n return vars as CSSProperties;\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\n\nimport type { SpacingFactor } from \"../../../theme/token-types\";\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport styles from \"./grid.module.css\";\nimport { type Responsive, responsiveVars } from \"../responsive\";\n\nexport type GridProps = Omit<useRender.ComponentProps<\"div\">, \"ref\"> & {\n /**\n * Number of equal-width columns.\n * @default 12\n */\n columns?: Responsive<number>;\n /**\n * Space between rows and columns, as a multiple of the spacing step\n * (`spacing(n)`).\n * @default 0\n */\n gap?: Responsive<SpacingFactor>;\n};\n\n/**\n * A grid of equal-width columns. Children fill one column each; wrap them in\n * `GridItem` to span several. Both props take a value per breakpoint,\n * mobile-first:\n *\n * ```tsx\n * <Grid columns={{ base: 1, sm: 2, lg: 3 }} gap={2}>\n * <Card />\n * <Card />\n * <Card />\n * </Grid>\n * ```\n */\nexport function Grid({\n columns,\n gap,\n render,\n className,\n style,\n ...rest\n}: GridProps) {\n return useRender({\n render,\n props: {\n ...rest,\n className: mergeClassName(styles.Grid, className)({}),\n style: {\n ...responsiveVars(\"columns\", columns),\n ...responsiveVars(\"gap\", gap),\n ...style,\n },\n },\n });\n}\n\nexport type GridItemSpan = number | \"full\";\n\nexport type GridItemProps = Omit<useRender.ComponentProps<\"div\">, \"ref\"> & {\n /**\n * How many of the grid's columns the item covers. `\"full\"` covers the whole\n * row whatever the column count.\n * @default \"full\"\n */\n span?: Responsive<GridItemSpan>;\n};\n\nconst spanValue = (span: GridItemSpan) =>\n span === \"full\" ? \"1 / -1\" : `span ${span} / span ${span}`;\n\n/**\n * An item in a `Grid`. It takes the whole row until `span` says otherwise, so\n * layouts stay single-column on small screens by default:\n *\n * ```tsx\n * <Grid gap={3}>\n * <GridItem span={{ md: 8 }}>Main content</GridItem>\n * <GridItem span={{ md: 4 }}>Sidebar</GridItem>\n * </Grid>\n * ```\n */\nexport function GridItem({\n span,\n render,\n className,\n style,\n ...rest\n}: GridItemProps) {\n return useRender({\n render,\n props: {\n ...rest,\n className: mergeClassName(styles.GridItem, className)({}),\n style: { ...responsiveVars(\"span\", span, spanValue), ...style },\n },\n });\n}\n","/*\n * Responsive props arrive as inline `--_<prop>-<breakpoint>` properties, set\n * only for the breakpoints the consumer gave. Each media query falls back\n * through the smaller breakpoints, so `{ base: 1, md: 3 }` holds 1 until `md`.\n *\n * Custom properties inherit, so the rule resets every breakpoint first: a\n * nested `Stack` would otherwise take its parent's direction and gap, or a\n * `Grid`'s gap. The inline properties of the props that are passed beat it.\n */\n.Stack {\n --_direction-base: initial;\n --_direction-sm: initial;\n --_direction-md: initial;\n --_direction-lg: initial;\n --_direction-xl: initial;\n --_gap-base: initial;\n --_gap-sm: initial;\n --_gap-md: initial;\n --_gap-lg: initial;\n --_gap-xl: initial;\n --_direction: var(--_direction-base, column);\n --_gap: var(--_gap-base, 0);\n\n display: flex;\n min-width: 0;\n flex-direction: var(--_direction);\n gap: calc(var(--lea-spacing) * var(--_gap));\n}\n\n@media (min-width: 600px) {\n .Stack {\n --_direction: var(--_direction-sm, var(--_direction-base, column));\n --_gap: var(--_gap-sm, var(--_gap-base, 0));\n }\n}\n\n@media (min-width: 900px) {\n .Stack {\n --_direction: var(\n --_direction-md,\n var(--_direction-sm, var(--_direction-base, column))\n );\n --_gap: var(--_gap-md, var(--_gap-sm, var(--_gap-base, 0)));\n }\n}\n\n@media (min-width: 1200px) {\n .Stack {\n --_direction: var(\n --_direction-lg,\n var(--_direction-md, var(--_direction-sm, var(--_direction-base, column)))\n );\n --_gap: var(--_gap-lg, var(--_gap-md, var(--_gap-sm, var(--_gap-base, 0))));\n }\n}\n\n@media (min-width: 1536px) {\n .Stack {\n --_direction: var(\n --_direction-xl,\n var(\n --_direction-lg,\n var(\n --_direction-md,\n var(--_direction-sm, var(--_direction-base, column))\n )\n )\n );\n --_gap: var(\n --_gap-xl,\n var(--_gap-lg, var(--_gap-md, var(--_gap-sm, var(--_gap-base, 0))))\n );\n }\n}\n\n.Stack[data-align=\"start\"] {\n align-items: flex-start;\n}\n\n.Stack[data-align=\"center\"] {\n align-items: center;\n}\n\n.Stack[data-align=\"end\"] {\n align-items: flex-end;\n}\n\n.Stack[data-align=\"stretch\"] {\n align-items: stretch;\n}\n\n.Stack[data-align=\"baseline\"] {\n align-items: baseline;\n}\n\n.Stack[data-justify=\"start\"] {\n justify-content: flex-start;\n}\n\n.Stack[data-justify=\"center\"] {\n justify-content: center;\n}\n\n.Stack[data-justify=\"end\"] {\n justify-content: flex-end;\n}\n\n.Stack[data-justify=\"between\"] {\n justify-content: space-between;\n}\n\n.Stack[data-justify=\"around\"] {\n justify-content: space-around;\n}\n\n.Stack[data-justify=\"evenly\"] {\n justify-content: space-evenly;\n}\n\n.Stack[data-wrap] {\n flex-wrap: wrap;\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\n\nimport type { SpacingFactor } from \"../../../theme/token-types\";\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport { type Responsive, responsiveVars } from \"../responsive\";\nimport styles from \"./stack.module.css\";\n\nexport type StackDirection = \"column\" | \"row\";\nexport type StackAlign = \"start\" | \"center\" | \"end\" | \"stretch\" | \"baseline\";\nexport type StackJustify =\n | \"start\"\n | \"center\"\n | \"end\"\n | \"between\"\n | \"around\"\n | \"evenly\";\n\nexport type StackProps = Omit<useRender.ComponentProps<\"div\">, \"ref\"> & {\n /**\n * Main axis. There is deliberately no reversed direction: it would make the\n * visual order differ from the reading and focus order.\n * @default \"column\"\n */\n direction?: Responsive<StackDirection>;\n /**\n * Space between children, as a multiple of the spacing step (`spacing(n)`).\n * @default 0\n */\n gap?: Responsive<SpacingFactor>;\n /** Cross-axis alignment (`align-items`). Stretches when unset. */\n align?: StackAlign;\n /** Main-axis distribution (`justify-content`). */\n justify?: StackJustify;\n /**\n * Lets children wrap onto new lines.\n * @default false\n */\n wrap?: boolean;\n};\n\n/**\n * Lays its children out in one direction with even, token-based spacing.\n * `direction` and `gap` take a value per breakpoint, mobile-first:\n *\n * ```tsx\n * <Stack direction={{ base: \"column\", md: \"row\" }} gap={{ base: 2, md: 3 }}>\n * <Card />\n * <Card />\n * </Stack>\n * ```\n *\n * Renders a `div`; pass `render` for another element, e.g.\n * `render={<ul />}` with `<li>` children.\n */\nexport function Stack({\n direction,\n gap,\n align,\n justify,\n wrap = false,\n render,\n className,\n style,\n ...rest\n}: StackProps) {\n return useRender({\n render,\n props: {\n ...rest,\n \"data-align\": align,\n \"data-justify\": justify,\n \"data-wrap\": wrap || undefined,\n className: mergeClassName(styles.Stack, className)({}),\n style: {\n ...responsiveVars(\"direction\", direction),\n ...responsiveVars(\"gap\", gap),\n ...style,\n },\n },\n });\n}\n\nexport type HStackProps = Omit<StackProps, \"direction\">;\n\n/**\n * A `Stack` fixed to `direction=\"row\"`. Children stay on one line unless\n * `wrap` is set.\n *\n * ```tsx\n * <HStack gap={1} justify=\"end\">\n * <Button variant=\"outline\">Avbryt</Button>\n * <Button>Lagre</Button>\n * </HStack>\n * ```\n */\nexport function HStack(props: HStackProps) {\n return <Stack {...props} direction=\"row\" />;\n}\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n */\n\n.Header {\n display: flex;\n width: 100%;\n align-items: center;\n justify-content: space-between;\n gap: var(--lea-spacing-2);\n}\n\n.Lead {\n display: flex;\n align-items: center;\n gap: var(--lea-spacing-2);\n}\n\n.Titles {\n display: flex;\n flex-direction: column;\n}\n\n.Title {\n margin: 0;\n color: var(--lea-palette-text-base);\n /* heading-sm's size at medium weight; the heading styles are semibold. */\n font-size: var(--lea-font-step-2);\n font-weight: var(--lea-font-weight-medium);\n line-height: var(--lea-line-height-normal);\n /* Focus is moved here from code when the step changes, never by Tab. */\n outline: none;\n}\n\n/* `titleAs=\"text\"`: body text at medium weight. */\n.Title[data-title-as=\"text\"] {\n font-size: var(--lea-font-step-0);\n}\n\n.Next {\n margin: 0;\n color: var(--lea-palette-text-subtle);\n font-size: var(--lea-text-body-sm-font-size);\n font-weight: var(--lea-text-body-sm-font-weight);\n line-height: var(--lea-text-body-sm-line-height);\n letter-spacing: var(--lea-text-body-sm-letter-spacing);\n}\n\n.Divider {\n border: 0;\n border-block-end: 1px solid var(--lea-palette-border-base);\n margin-block: var(--lea-spacing-1);\n margin-inline: 0;\n}\n\n.SrOnly {\n position: absolute;\n overflow: hidden;\n width: 1px;\n height: 1px;\n padding: 0;\n border: 0;\n margin: -1px;\n clip-path: inset(50%);\n white-space: nowrap;\n}\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n *\n * The diameter arrives inline from the `size` prop, and so does\n * `--_label-fit`: the largest font size at which the label stays inside the\n * ring.\n */\n\n.ProgressCircle {\n position: relative;\n flex-shrink: 0;\n /* The ring and the value are both drawn in the current colour. */\n color: var(--lea-palette-primary-base);\n}\n\n.ProgressCircle[data-color=\"tertiary\"] {\n color: var(--lea-palette-tertiary-base);\n}\n\n/* Turned so progress starts at 12 o'clock. */\n.Svg {\n display: block;\n transform: rotate(-90deg);\n transform-origin: center;\n}\n\n/* The unfilled part of the ring: the ring colour, faint. No opacity token is\n this strong without being the 60% highlight. */\n.Track {\n stroke-opacity: 0.2;\n}\n\n.Value {\n position: absolute;\n display: flex;\n align-items: center;\n justify-content: center;\n /* label-md's size and weight on a line as tall as the digits, so they\n centre on the ring. Smaller only where that size does not fit. */\n font-size: min(var(--lea-font-step-minus-1), var(--_label-fit));\n font-weight: var(--lea-font-weight-medium);\n line-height: var(--lea-line-height-none);\n inset: 0;\n}\n","\"use client\";\n\nimport {\n animate,\n motion,\n useMotionValue,\n useReducedMotion,\n} from \"motion/react\";\nimport { useEffect, type CSSProperties } from \"react\";\n\nimport type { ColorRoles } from \"../../color-roles\";\nimport styles from \"./progress-circle.module.css\";\n\nexport type ProgressCircleColor = ColorRoles<\"primary\" | \"tertiary\">;\n\n/*\n * How much room the `value/max` label takes, in em. The widths are the advance\n * of each digit, 0 to 9, and of the slash in Clash Grotesk at weight 500, and\n * the height is that of a digit; all measured in a browser. Measure again if\n * the font or the weight of the label changes.\n */\nconst DIGIT_WIDTHS = [\n 0.69, 0.33, 0.61, 0.6, 0.63, 0.64, 0.64, 0.54, 0.62, 0.64,\n];\nconst SLASH_WIDTH = 0.43;\nconst DIGIT_HEIGHT = 0.69;\n/** Air between the label and the ring, in px. */\nconst LABEL_GAP = 2;\n\n/**\n * The largest font size, in px, at which `text` stays inside a ring with a\n * hole of `diameter` px: the corners of the text are what reach the ring first.\n */\nconst fitFontSize = (text: string, diameter: number) => {\n const width = Array.from(text).reduce(\n (sum, char) =>\n // Anything but a digit or the slash counts as the widest digit.\n sum + (char === \"/\" ? SLASH_WIDTH : (DIGIT_WIDTHS[Number(char)] ?? 0.69)),\n 0,\n );\n const room = Math.max(diameter - 2 * LABEL_GAP, 0);\n\n return room / Math.hypot(width, DIGIT_HEIGHT);\n};\n\nexport type ProgressCircleProps = {\n /** Current value, e.g. the active step (1-based). Clamped to `0..max`. */\n value: number;\n /** Maximum value, e.g. the total number of steps. */\n max: number;\n /** Ring colour. Defaults to `primary`. */\n color?: ProgressCircleColor;\n /** Rendered diameter in px. Defaults to `48`. */\n size?: number;\n /** Ring thickness in px. Defaults to `4`. */\n strokeWidth?: number;\n /**\n * Show the `value/max` label in the centre. Defaults to `true`. The label\n * is set smaller when it would otherwise touch the ring, in a small circle\n * or with many digits. Below about 32px it is too small to read: leave it\n * out there.\n */\n showValue?: boolean;\n /**\n * Accessible name. When provided, the circle is exposed as a\n * `role=\"progressbar\"` (with `aria-valuetext={label}`) so it announces on its\n * own. When omitted, the circle is decorative (`aria-hidden`) — use this when\n * the surrounding component already conveys progress in text (as `FormStepper`\n * does).\n */\n label?: string;\n};\n\n/**\n * An animated circular progress indicator: the ring is an SVG whose stroke\n * animates as `value` changes, respecting `prefers-reduced-motion`. Colour is\n * limited to `primary` (purple) and `tertiary` (teal) so contrast stays owned\n * by the theme.\n *\n * ```tsx\n * <ProgressCircle value={2} max={5} color=\"tertiary\" label=\"Step 2 of 5\" />\n * ```\n */\nexport const ProgressCircle = ({\n value,\n max,\n color = \"primary\",\n size = 48,\n strokeWidth = 4,\n showValue = true,\n label,\n}: ProgressCircleProps) => {\n const prefersReducedMotion = useReducedMotion();\n\n const radius = (size - strokeWidth) / 2;\n const circumference = 2 * Math.PI * radius;\n const fraction = max > 0 ? Math.min(Math.max(value / max, 0), 1) : 0;\n const dashOffsetFor = (value: number) => circumference * (1 - value);\n\n const offset = useMotionValue(dashOffsetFor(0));\n\n useEffect(() => {\n const target = dashOffsetFor(fraction);\n if (prefersReducedMotion) {\n offset.set(target);\n return;\n }\n\n const controls = animate(offset, target, {\n duration: 0.6,\n ease: \"easeInOut\",\n });\n return () => controls.stop();\n // `circumference` is derived from size/strokeWidth, so it re-runs when those change.\n }, [fraction, circumference, prefersReducedMotion, offset]);\n\n const valueText = `${value}/${max}`;\n const labelFit = fitFontSize(valueText, size - 2 * strokeWidth);\n\n const accessibilityProps = label\n ? ({\n role: \"progressbar\",\n \"aria-label\": label,\n \"aria-valuemin\": 0,\n \"aria-valuemax\": max,\n \"aria-valuenow\": value,\n } as const)\n : ({ \"aria-hidden\": true } as const);\n\n return (\n <div\n {...accessibilityProps}\n className={styles.ProgressCircle}\n data-color={color}\n style={\n {\n width: size,\n height: size,\n \"--_label-fit\": `${labelFit.toFixed(2)}px`,\n } as CSSProperties\n }\n >\n <svg\n className={styles.Svg}\n width={size}\n height={size}\n viewBox={`0 0 ${size} ${size}`}\n >\n <circle\n className={styles.Track}\n cx={size / 2}\n cy={size / 2}\n r={radius}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={strokeWidth}\n />\n <motion.circle\n cx={size / 2}\n cy={size / 2}\n r={radius}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={strokeWidth}\n strokeLinecap=\"round\"\n strokeDasharray={circumference}\n style={{ strokeDashoffset: offset }}\n />\n </svg>\n {showValue && <span className={styles.Value}>{valueText}</span>}\n </div>\n );\n};\n","import { createTranslation } from \"../../i18n\";\n\n/**\n * Translated labels for `FormStepper`: the back button's accessible name, the\n * \"Next\" subtitle prefix, and the visually-hidden \"step X of Y\" context.\n * Consumers can override any of these per-instance via the `labels` prop.\n */\nexport const useStepperLabels = createTranslation({\n back: { no: \"Tilbake\", se: \"Tillbaka\", fi: \"Takaisin\", es: \"Atrás\" },\n next: { no: \"Neste\", se: \"Nästa\", fi: \"Seuraava\", es: \"Siguiente\" },\n step: { no: \"Trinn\", se: \"Steg\", fi: \"Vaihe\", es: \"Paso\" },\n of: { no: \"av\", se: \"av\", fi: \"/\", es: \"de\" },\n});\n\nexport type StepperLabels = Partial<ReturnType<typeof useStepperLabels>>;\n","\"use client\";\n\nimport { ArrowLeft } from \"lucide-react\";\nimport { useId, type ReactNode } from \"react\";\n\nimport { IconButton } from \"../../buttons/icon-button/icon-button\";\nimport styles from \"./form-stepper.module.css\";\nimport { ProgressCircle, type ProgressCircleColor } from \"../progress-circle/progress-circle\";\nimport { useStepperLabels, type StepperLabels } from \"../use-stepper-labels\";\n\nexport type FormStepperProps = {\n /** Active step (1-based). */\n currentStep: number;\n /** Total number of steps. */\n totalSteps: number;\n /** Title of the current step. Rendered as a heading by default (see `titleAs`). */\n title: ReactNode;\n /**\n * Name of the next step. When set, a translated `\"{Next}: {nextStep}\"`\n * subtitle is shown. Omit it on the last step.\n */\n nextStep?: ReactNode;\n /**\n * Called when the back button is pressed. The back button only renders when\n * this is provided, so omit it on the first step or in forms without a back\n * flow.\n */\n onBack?: () => void;\n /** Progress circle colour. Defaults to `primary`. */\n color?: ProgressCircleColor;\n /**\n * Render the title as a `heading` (default) or plain `text`. Prefer `heading`\n * so screen-reader users can navigate to it; only use `text` when another\n * heading already labels the view.\n */\n titleAs?: \"heading\" | \"text\";\n /** Heading level when `titleAs=\"heading\"`. Defaults to `h1`. */\n headingLevel?: \"h1\" | \"h2\" | \"h3\";\n /**\n * Id applied to the title. Defaults to a generated id. Use it to move focus to\n * the title when the step changes so the new step is announced.\n */\n titleId?: string;\n /** Override the built-in translated labels per-instance. */\n labels?: StepperLabels;\n /** Show the divider below the header. Defaults to `true`. */\n showDivider?: boolean;\n};\n\n/**\n * Header for a multi-step form. Shows the current step title (as a heading by\n * default), an optional `\"Next: ...\"` subtitle, an optional back button, and an\n * animated `ProgressCircle`.\n *\n * Accessibility is wired internally: the step count (`\"step X of Y\"`) is added\n * to the title as visually-hidden text and the circle is decorative, so the\n * count is announced once. The title carries `tabIndex={-1}`; move focus to it\n * (via `titleId`) whenever the step changes so screen-reader users hear the new\n * step.\n *\n * ```tsx\n * <FormStepper\n * currentStep={2}\n * totalSteps={5}\n * title=\"Civil status\"\n * nextStep=\"Employment\"\n * onBack={() => history.back()}\n * />\n * ```\n */\nexport const FormStepper = ({\n currentStep,\n totalSteps,\n title,\n nextStep,\n onBack,\n color = \"primary\",\n titleAs = \"heading\",\n headingLevel = \"h1\",\n titleId,\n labels,\n showDivider = true,\n}: FormStepperProps) => {\n const t = useStepperLabels();\n const l = { ...t, ...labels };\n const generatedId = useId();\n const resolvedTitleId = titleId ?? generatedId;\n\n const Title = titleAs === \"heading\" ? headingLevel : \"p\";\n const stepContext = `, ${l.step} ${currentStep} ${l.of} ${totalSteps}`;\n\n return (\n <>\n <div className={styles.Header}>\n <div className={styles.Lead}>\n {onBack && (\n <IconButton\n aria-label={l.back}\n onClick={onBack}\n variant=\"outline\"\n color=\"secondary\"\n >\n <ArrowLeft aria-hidden />\n </IconButton>\n )}\n <div className={styles.Titles}>\n <Title\n id={resolvedTitleId}\n tabIndex={-1}\n className={styles.Title}\n data-title-as={titleAs}\n >\n {title}\n <span className={styles.SrOnly}>{stepContext}</span>\n </Title>\n {nextStep != null && (\n <p className={styles.Next}>\n {l.next}: {nextStep}\n </p>\n )}\n </div>\n </div>\n <ProgressCircle value={currentStep} max={totalSteps} color={color} />\n </div>\n {showDivider && <hr className={styles.Divider} />}\n </>\n );\n};\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n */\n\n.LinearFormStepper {\n --stepper-bar: var(--lea-palette-primary-base);\n --stepper-track: var(--lea-palette-primary-subtle);\n\n display: flex;\n width: 100%;\n flex-direction: column;\n gap: calc(var(--lea-spacing) * 1.5);\n}\n\n.LinearFormStepper[data-color=\"tertiary\"] {\n --stepper-bar: var(--lea-palette-tertiary-base);\n --stepper-track: var(--lea-palette-tertiary-subtle);\n}\n\n.Progress {\n width: 100%;\n}\n\n.Track {\n position: relative;\n overflow: hidden;\n height: calc(var(--lea-spacing) * 0.75);\n border-radius: var(--lea-radius-full);\n background-color: var(--stepper-track);\n}\n\n.Indicator {\n height: 100%;\n border-radius: var(--lea-radius-full);\n background-color: var(--stepper-bar);\n transition: width var(--lea-duration-emphasis) var(--lea-easing-standard);\n}\n\n.Header {\n display: flex;\n align-items: center;\n gap: calc(var(--lea-spacing) * 2);\n}\n\n.Title {\n margin: 0;\n color: var(--lea-palette-text-base);\n /* heading-md's size at medium weight; the heading styles are semibold. */\n font-size: var(--lea-font-step-4);\n font-weight: var(--lea-font-weight-medium);\n line-height: var(--lea-line-height-snug);\n outline: none;\n}\n\n.TitleText {\n animation: stepper-title-in var(--lea-duration-normal)\n var(--lea-easing-standard);\n}\n\n@keyframes stepper-title-in {\n from {\n opacity: 0;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Indicator {\n transition: none;\n }\n\n .TitleText {\n animation: none;\n }\n}\n","\"use client\";\n\nimport { Progress } from \"@base-ui/react/progress\";\nimport { ArrowLeft } from \"lucide-react\";\nimport { useId, type ReactNode } from \"react\";\n\nimport { IconButton } from \"../../buttons/icon-button/icon-button\";\nimport styles from \"./linear-form-stepper.module.css\";\nimport type { ProgressCircleColor } from \"../progress-circle/progress-circle\";\nimport { useStepperLabels, type StepperLabels } from \"../use-stepper-labels\";\n\nexport type LinearFormStepperProps = {\n /** Active step (1-based). */\n currentStep: number;\n /**\n * Total number of steps. May change between renders, e.g. when the next step\n * from the API reveals a different path; the bar animates to the new value.\n */\n totalSteps: number;\n /** Title of the current step. Rendered as a heading and names the progress bar. */\n title: ReactNode;\n /**\n * Called when the back button is pressed. The back button only renders when\n * this is provided, so omit it on the first step or in forms without a back\n * flow.\n */\n onBack?: () => void;\n /**\n * Bar colour.\n * @default \"primary\"\n */\n color?: ProgressCircleColor;\n /**\n * Heading level of the title.\n * @default \"h1\"\n */\n headingLevel?: \"h1\" | \"h2\" | \"h3\";\n /**\n * Id applied to the title. Defaults to a generated id. Use it to move focus to\n * the title when the step changes so the new step is announced. Must be\n * unique in the document.\n */\n titleId?: string;\n /** Override the built-in translated labels per-instance. */\n labels?: StepperLabels;\n};\n\n/**\n * Header for a multi-step form whose steps aren't all known up front: a linear\n * progress bar above the current step's title, with an optional back button.\n * Use `FormStepper` instead when the full step list is known.\n *\n * The bar is a `progressbar` named by the title heading, with the step count\n * (`\"step X of Y\"`) as its value text. The title carries `tabIndex={-1}`; move\n * focus to it (via `titleId`) whenever the step changes so screen-reader users\n * hear the new step.\n *\n * ```tsx\n * <LinearFormStepper\n * currentStep={2}\n * totalSteps={5}\n * title=\"Refinancing\"\n * onBack={() => history.back()}\n * />\n * ```\n */\nexport const LinearFormStepper = ({\n currentStep,\n totalSteps,\n title,\n onBack,\n color = \"primary\",\n headingLevel: Heading = \"h1\",\n titleId,\n labels,\n}: LinearFormStepperProps) => {\n const t = useStepperLabels();\n const l = { ...t, ...labels };\n const generatedId = useId();\n const resolvedTitleId = titleId ?? generatedId;\n\n return (\n <div className={styles.LinearFormStepper} data-color={color}>\n <Progress.Root\n className={styles.Progress}\n value={currentStep}\n max={totalSteps}\n aria-labelledby={resolvedTitleId}\n getAriaValueText={() =>\n `${l.step} ${currentStep} ${l.of} ${totalSteps}`\n }\n >\n <Progress.Track className={styles.Track}>\n <Progress.Indicator className={styles.Indicator} />\n </Progress.Track>\n </Progress.Root>\n <div className={styles.Header}>\n {onBack && (\n <IconButton\n aria-label={l.back}\n onClick={onBack}\n variant=\"outline\"\n color=\"secondary\"\n >\n <ArrowLeft aria-hidden />\n </IconButton>\n )}\n <Heading id={resolvedTitleId} tabIndex={-1} className={styles.Title}>\n <span key={currentStep} className={styles.TitleText}>\n {title}\n </span>\n </Heading>\n </div>\n </div>\n );\n};\n","\"use client\";\n\nimport {\n createContext,\n useContext,\n useEffect,\n useState,\n type Dispatch,\n type ReactNode,\n type SetStateAction,\n} from \"react\";\n\nimport {\n LinearFormStepper,\n type LinearFormStepperProps,\n} from \"./linear-form-stepper\";\n\n/**\n * The step a flow is on, already resolved by the app. The library never\n * defines steps: the app maps its own step (e.g. from the API) to a position\n * and a translated title, then reports it.\n */\nexport type StepProgress = {\n /** Active step (1-based). */\n currentStep: number;\n /** Total number of steps. May change as the flow reveals its path. */\n totalSteps: number;\n /** Translated title of the current step. */\n title: string;\n};\n\nconst StepProgressStateContext = createContext<StepProgress | null>(null);\nconst ReportStepProgressContext = createContext<Dispatch<\n SetStateAction<StepProgress | null>\n> | null>(null);\n\nexport type StepProgressProviderProps = {\n children: ReactNode;\n /**\n * Progress to show on the first render, before any page has reported, e.g.\n * so a server-rendered layout paints the bar straight away.\n */\n initialProgress?: StepProgress;\n};\n\n/**\n * Holds the step a persistent flow header shows. Put it in the flow layout,\n * above the route's loading boundary, so the header survives step navigations\n * instead of remounting with the page, and its bar animates between steps.\n *\n * ```tsx\n * <StepProgressProvider>\n * <StepProgressHeader onBack={router.back} />\n * {children}\n * </StepProgressProvider>\n * ```\n */\nexport const StepProgressProvider = ({\n children,\n initialProgress,\n}: StepProgressProviderProps) => {\n const [progress, setProgress] = useState<StepProgress | null>(\n initialProgress ?? null,\n );\n\n return (\n <ReportStepProgressContext.Provider value={setProgress}>\n <StepProgressStateContext.Provider value={progress}>\n {children}\n </StepProgressStateContext.Provider>\n </ReportStepProgressContext.Provider>\n );\n};\n\n/**\n * The last reported step, or `null` before anything has been reported or\n * outside a `StepProgressProvider`. Use it to render `FormStepper` or a custom\n * header from the same state.\n */\nexport const useStepProgress = () => useContext(StepProgressStateContext);\n\n/**\n * Publishes the current step to the nearest `StepProgressProvider`. Pass\n * `null` while the step isn't resolved yet; nothing is reported then. Never\n * clears on unmount, so the header holds its value while the next route's\n * loading fallback is on screen.\n *\n * ```tsx\n * useReportStepProgress({ currentStep: 2, totalSteps: 5, title: t(\"income\") });\n * ```\n */\nexport const useReportStepProgress = (progress: StepProgress | null) => {\n const report = useContext(ReportStepProgressContext);\n const currentStep = progress?.currentStep;\n const totalSteps = progress?.totalSteps;\n const title = progress?.title;\n\n useEffect(() => {\n if (\n !report ||\n currentStep === undefined ||\n totalSteps === undefined ||\n title === undefined\n ) {\n return;\n }\n report({ currentStep, totalSteps, title });\n }, [report, currentStep, totalSteps, title]);\n};\n\nexport type StepProgressHeaderProps = Omit<\n LinearFormStepperProps,\n \"currentStep\" | \"totalSteps\" | \"title\"\n>;\n\n/**\n * A `LinearFormStepper` driven by the nearest `StepProgressProvider`. Renders\n * nothing until a step has been reported. Everything except the step itself\n * (`onBack`, `color`, `headingLevel`, …) is passed as props.\n */\nexport const StepProgressHeader = (props: StepProgressHeaderProps) => {\n const progress = useStepProgress();\n if (!progress) {\n return null;\n }\n\n return <LinearFormStepper {...props} {...progress} />;\n};\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n */\n\n.Tabs {\n --tabs-pill: var(--lea-palette-primary-base);\n\n /*\n * One column that may be narrower than the tabs in it. Without it the row of\n * tabs is the least width the control can have, and where the layout sizes\n * things by their content (a flex row, a centred column) it pushes the page\n * wider than the screen instead of scrolling inside its track.\n */\n display: grid;\n grid-template-columns: minmax(0, 1fr);\n}\n\n.Tabs[data-color=\"secondary\"] {\n --tabs-pill: var(--lea-palette-secondary-base);\n}\n\n/*\n * The track: a beige pill that holds the tabs. It scrolls sideways when they\n * do not fit, with the scrollbar hidden so the row stays a clean pill.\n */\n.List {\n position: relative;\n display: flex;\n overflow: auto hidden;\n max-width: 100%;\n box-sizing: border-box;\n flex-flow: row nowrap;\n align-items: center;\n padding: var(--lea-spacing-0-5);\n border-radius: var(--lea-radius-full);\n background-color: var(--lea-palette-bg-muted);\n gap: var(--lea-spacing-0-5);\n scrollbar-width: none;\n}\n\n.List::-webkit-scrollbar {\n display: none;\n}\n\n/* Above the pill, so the label of the active tab shows on top of it. */\n.Tab {\n position: relative;\n z-index: 1;\n display: inline-flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n /* 10px: with one line of text it makes a tab 36px tall, 44px with the track. */\n padding-block: calc(var(--lea-spacing) * 1.25);\n padding-inline: var(--lea-spacing-2);\n border: 0;\n border-radius: var(--lea-radius-full);\n margin: 0;\n background: none;\n /*\n * neutral-600 rather than text-subtle, so the inactive label clears 4.5:1\n * against the beige track.\n */\n color: var(--lea-color-neutral-600);\n cursor: pointer;\n font-family: inherit;\n /* label-lg on a line as tall as the text: the padding sets the height. */\n font-size: var(--lea-font-step-0);\n font-weight: var(--lea-font-weight-medium);\n gap: var(--lea-spacing-1);\n line-height: var(--lea-line-height-none);\n transition: color var(--lea-duration-normal) var(--lea-easing-standard);\n user-select: none;\n white-space: nowrap;\n}\n\n.Tab svg {\n display: block;\n flex-shrink: 0;\n}\n\n.Tab[data-active] {\n color: var(--lea-palette-text-contrast);\n}\n\n.Tab[data-disabled] {\n cursor: not-allowed;\n opacity: 0.45;\n pointer-events: none;\n}\n\n.Tab:focus-visible,\n.Panel:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n/*\n * The pill behind the active tab. Base UI writes the place and size of the\n * active tab to the `--active-tab-*` properties on this element.\n */\n.Indicator {\n position: absolute;\n z-index: 0;\n top: var(--active-tab-top);\n left: var(--active-tab-left);\n width: var(--active-tab-width);\n height: var(--active-tab-height);\n border-radius: var(--lea-radius-full);\n background-color: var(--tabs-pill);\n transition: top var(--lea-duration-normal) var(--lea-easing-standard),\n left var(--lea-duration-normal) var(--lea-easing-standard),\n width var(--lea-duration-normal) var(--lea-easing-standard),\n height var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n/* The panel takes focus, so keyboard users can reach content without controls. */\n.Panel {\n padding-block-start: var(--lea-spacing-2);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Tab,\n .Indicator {\n transition-duration: 0.01ms;\n }\n}\n","\"use client\";\n\nimport { Tabs as BaseTabs } from \"@base-ui/react/tabs\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport type { ColorRoles } from \"../../color-roles\";\nimport styles from \"./tabs.module.css\";\n\n/** The colours of the active \"pill\". */\nexport type Color = ColorRoles<\"primary\" | \"secondary\">;\n\nexport type RootProps = Omit<BaseTabs.Root.Props, \"color\"> & {\n /**\n * Colour of the active-tab \"pill\".\n * - `\"primary\"` — filled with the brand purple (default).\n * - `\"secondary\"` — filled with near-black, for a neutral surface.\n * @default \"primary\"\n */\n color?: Color;\n};\n\n/**\n * A segmented, pill-style tabs control built on Base UI's accessible `Tabs`\n * primitive. Renders a beige track with a sliding pill behind the active tab.\n *\n * Compose it from `Tabs.Root`, `Tabs.List` + `Tabs.Tab` (the tab bar) and,\n * optionally, `Tabs.Panel` for built-in content. Roles\n * (`tablist`/`tab`/`tabpanel`), roving `tabindex`, arrow-key navigation,\n * `aria-selected` and the `tab`↔`tabpanel` links are all wired by Base UI, so\n * relationships never dangle.\n *\n * The list scrolls horizontally when the tabs overflow (e.g. on mobile) and the\n * pill slide respects `prefers-reduced-motion`.\n *\n * ```tsx\n * <Tabs.Root defaultValue=\"overview\">\n * <Tabs.List aria-label=\"Account sections\">\n * <Tabs.Tab value=\"overview\">Overview</Tabs.Tab>\n * <Tabs.Tab value=\"activity\">Activity</Tabs.Tab>\n * </Tabs.List>\n * <Tabs.Panel value=\"overview\">Workspace stats and activity.</Tabs.Panel>\n * <Tabs.Panel value=\"activity\">Recent activity.</Tabs.Panel>\n * </Tabs.Root>\n * ```\n */\nexport function Root({ color = \"primary\", className, ...rest }: RootProps) {\n return (\n <BaseTabs.Root\n {...rest}\n data-color={color}\n className={mergeClassName(styles.Tabs, className)}\n />\n );\n}\n\n/**\n * Requires an accessible name so the underlying `tablist` is never nameless.\n * Provide exactly one of `aria-label` or `aria-labelledby`.\n */\ntype TabsListAccessibleName =\n | { \"aria-label\": string; \"aria-labelledby\"?: never }\n | { \"aria-labelledby\": string; \"aria-label\"?: never };\n\nexport type ListProps = Omit<\n BaseTabs.List.Props,\n \"render\" | \"aria-label\" | \"aria-labelledby\"\n> &\n TabsListAccessibleName;\n\n/**\n * Groups the `Tabs.Tab` buttons into the pill track. The sliding indicator is\n * rendered internally so it can never go missing.\n */\nexport function List({ children, className, ...rest }: ListProps) {\n return (\n <BaseTabs.List {...rest} className={mergeClassName(styles.List, className)}>\n {children}\n <Indicator />\n </BaseTabs.List>\n );\n}\n\nexport type TabProps = Omit<BaseTabs.Tab.Props, \"render\">;\n\n/**\n * An individual tab. Pass an icon and label as children; mark decorative icons\n * with `aria-hidden` so they aren't announced.\n *\n * ```tsx\n * <Tabs.Tab value=\"transactions\">\n * <Receipt size={18} aria-hidden />\n * Transaksjoner\n * </Tabs.Tab>\n * ```\n */\nexport function Tab({ className, ...rest }: TabProps) {\n return (\n <BaseTabs.Tab {...rest} className={mergeClassName(styles.Tab, className)} />\n );\n}\n\nexport type IndicatorProps = Omit<BaseTabs.Indicator.Props, \"render\">;\n\n/**\n * The sliding pill behind the active tab. `Tabs.List` already renders one, so\n * you only need this when composing a custom list.\n */\nexport function Indicator({ className, ...rest }: IndicatorProps) {\n return (\n <BaseTabs.Indicator\n {...rest}\n className={mergeClassName(styles.Indicator, className)}\n />\n );\n}\n\nexport type PanelProps = Omit<BaseTabs.Panel.Props, \"render\">;\n\n/**\n * Content shown when the `Tabs.Tab` with the matching `value` is active.\n * Optional — omit it to use `Tabs` purely as a bar that toggles content you\n * render elsewhere (drive it with `value`/`onValueChange`).\n */\nexport function Panel({ className, ...rest }: PanelProps) {\n return (\n <BaseTabs.Panel\n {...rest}\n className={mergeClassName(styles.Panel, className)}\n />\n );\n}\n","/*\n * The website's breadcrumbs: 14px medium, the links grey and the current page\n * black, a \"/\" between them, 4px apart. The grey is `neutral-600` rather than\n * `text.subtle`, so the links pass 4.5:1 on the grey and beige sections and\n * the sidebar as well as on white (6.5, 6.1, 5.4); it is also closest to the\n * website's own grey.\n */\n.Breadcrumbs {\n min-width: 0;\n}\n\n.List {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--lea-spacing-0-5);\n padding: 0;\n margin: 0;\n list-style: none;\n font-size: var(--lea-text-label-md-font-size);\n font-weight: var(--lea-text-label-md-font-weight);\n letter-spacing: var(--lea-text-label-md-letter-spacing);\n line-height: var(--lea-text-label-md-line-height);\n}\n\n.Item {\n display: inline-flex;\n min-width: 0;\n align-items: center;\n gap: var(--lea-spacing-0-5);\n}\n\n.Separator {\n color: var(--lea-color-neutral-600);\n font-weight: var(--lea-font-weight-regular);\n user-select: none;\n}\n\n.Link,\n.Text {\n color: var(--lea-color-neutral-600);\n}\n\n.Link {\n border-radius: var(--lea-radius-sm);\n text-decoration: none;\n transition: color var(--lea-duration-fast) var(--lea-easing-standard);\n}\n\n.Link:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n.Current {\n color: var(--lea-palette-text-base);\n}\n\n@media (hover: hover) {\n .Link:hover {\n color: var(--lea-palette-text-base);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Link {\n transition-duration: 0.01ms;\n }\n}\n","import { createTranslation } from \"../../../i18n\";\n\n/** The accessible name of the `nav` around `Breadcrumbs`. */\nexport const useBreadcrumbsLabels = createTranslation({\n label: {\n no: \"Brødsmulesti\",\n se: \"Brödsmulor\",\n fi: \"Murupolku\",\n es: \"Ruta de navegación\",\n },\n});\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport type { ComponentProps, ReactElement, ReactNode } from \"react\";\n\nimport styles from \"./breadcrumbs.module.css\";\nimport { useBreadcrumbsLabels } from \"./use-breadcrumbs-labels\";\n\nexport type BreadcrumbsItem = {\n /** The text of the crumb. */\n label: ReactNode;\n /** Where the crumb leads. */\n href?: string;\n /**\n * A router link to render instead of an `<a>`, carrying its own `href`:\n * `<NextLink href=\"/lan\" />`.\n */\n render?: ReactElement;\n};\n\nexport type BreadcrumbsProps = Omit<ComponentProps<\"nav\">, \"children\"> & {\n /**\n * The way from the start page to this page, in order. The last item is the\n * current page: it is plain text marked `aria-current=\"page\"`, whatever it\n * is given. An item before it without `href` or `render` is plain text too.\n */\n items: BreadcrumbsItem[];\n};\n\nfunction Crumb({ item }: { item: BreadcrumbsItem }) {\n const isLink = item.href != null || item.render != null;\n\n const link = useRender({\n enabled: isLink,\n render: item.render,\n defaultTagName: \"a\",\n props: {\n href: item.href,\n className: styles.Link,\n children: item.label,\n },\n });\n\n return link ?? <span className={styles.Text}>{item.label}</span>;\n}\n\n/**\n * Where the page sits on the website: a `nav` with a translated name around a\n * list of links, the last one the current page.\n *\n * ```tsx\n * <Breadcrumbs\n * items={[\n * { label: \"Hjem\", href: \"/\" },\n * { label: \"Lån\", render: <NextLink href=\"/lan\" /> },\n * { label: \"Forbrukslån\" },\n * ]}\n * />\n * ```\n *\n * The separators are hidden from screen readers; the list says how many\n * steps there are. Renders nothing without items.\n */\nexport function Breadcrumbs({ items, className, ...rest }: BreadcrumbsProps) {\n const labels = useBreadcrumbsLabels();\n\n if (items.length === 0) return null;\n\n const last = items.length - 1;\n\n return (\n <nav\n aria-label={labels.label}\n {...rest}\n className={[styles.Breadcrumbs, className].filter(Boolean).join(\" \")}\n >\n <ol className={styles.List}>\n {items.map((item, index) => (\n <li key={index} className={styles.Item}>\n {index > 0 && (\n <span aria-hidden className={styles.Separator}>\n /\n </span>\n )}\n {index === last ? (\n <span aria-current=\"page\" className={styles.Current}>\n {item.label}\n </span>\n ) : (\n <Crumb item={item} />\n )}\n </li>\n ))}\n </ol>\n </nav>\n );\n}\n","/*\n * The website's page numbers: 14px pills with a 1px ring, 32px wide at least\n * and 27px tall, 8px apart. The current page is filled white, as in Figma,\n * and its ring turns `border.strong`, so it stands out on a white page too and\n * not by its fill alone.\n */\n.Pagination {\n min-width: 0;\n}\n\n.List {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--lea-spacing-1);\n padding: 0;\n margin: 0;\n list-style: none;\n}\n\n.Item {\n --pagination-tint: color-mix(\n in srgb,\n var(--lea-palette-text-base) calc(var(--lea-opacity-hover) * 100%),\n transparent\n );\n\n box-sizing: border-box;\n display: inline-flex;\n min-width: var(--lea-spacing-4);\n align-items: center;\n justify-content: center;\n padding: var(--lea-spacing-0-5) var(--lea-spacing-1);\n border: 1px solid var(--lea-palette-border-base);\n border-radius: var(--lea-radius-full);\n margin: 0;\n background-color: transparent;\n color: var(--lea-palette-text-base);\n cursor: pointer;\n font-family: inherit;\n font-size: var(--lea-font-step-minus-1);\n font-weight: var(--lea-font-weight-regular);\n /* \"normal\" on the website: the tight line gives its 27px. */\n line-height: var(--lea-line-height-tight);\n text-align: center;\n text-decoration: none;\n white-space: nowrap;\n transition: background-color var(--lea-duration-normal)\n var(--lea-easing-standard),\n border-color var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n.Item:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n.Item[data-current] {\n border-color: var(--lea-palette-border-strong);\n background-color: var(--lea-palette-bg-base);\n cursor: default;\n}\n\n.Item[data-disabled] {\n cursor: not-allowed;\n opacity: 0.4;\n}\n\n@media (hover: hover) {\n .Item:not([data-current], [data-disabled]):hover {\n background-color: var(--pagination-tint);\n }\n}\n\n.Ellipsis {\n padding-inline: var(--lea-spacing-0-5);\n color: var(--lea-palette-text-base);\n font-size: var(--lea-font-step-minus-1);\n line-height: var(--lea-line-height-tight);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Item {\n transition-duration: 0.01ms;\n }\n}\n","import { createTranslation } from \"../../i18n\";\n\n/**\n * The words of `Pagination` and `PaginationDots`. `{page}` is replaced with\n * the page number.\n */\nexport const usePaginationLabels = createTranslation({\n label: { no: \"Sider\", se: \"Sidor\", fi: \"Sivut\", es: \"Páginas\" },\n previous: { no: \"Forrige\", se: \"Föregående\", fi: \"Edellinen\", es: \"Anterior\" },\n next: { no: \"Neste\", se: \"Nästa\", fi: \"Seuraava\", es: \"Siguiente\" },\n page: {\n no: \"Side {page}\",\n se: \"Sida {page}\",\n fi: \"Sivu {page}\",\n es: \"Página {page}\",\n },\n goToPage: {\n no: \"Gå til side {page}\",\n se: \"Gå till sida {page}\",\n fi: \"Siirry sivulle {page}\",\n es: \"Ir a la página {page}\",\n },\n});\n\n/** Puts the page number into a label from `usePaginationLabels`. */\nexport const withPage = (label: string, page: number) =>\n label.replace(\"{page}\", String(page));\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { useState, type ComponentProps, type ReactElement } from \"react\";\n\nimport styles from \"./pagination.module.css\";\nimport { usePaginationLabels, withPage } from \"../use-pagination-labels\";\n\nexport type PaginationProps = Omit<\n ComponentProps<\"nav\">,\n \"children\" | \"onChange\" | \"defaultValue\"\n> & {\n /** How many pages there are. */\n count: number;\n /** The current page, counted from 1. Pass it to control the pagination. */\n value?: number;\n /**\n * The page shown first when the pagination is not controlled.\n * @default 1\n */\n defaultValue?: number;\n /** Called with the page number when another page is chosen. */\n onValueChange?: (page: number) => void;\n /**\n * Renders the pages as links instead of buttons: return the link to a page,\n * with its `href`. An `<a>` or a router link:\n * `renderLink={(page) => <NextLink href={`/okonomiguiden?side=${page}`} />}`.\n * Previous and next become links too, and plain text where there is no\n * page to go to.\n */\n renderLink?: (page: number) => ReactElement;\n};\n\ntype Item = number | \"start-ellipsis\" | \"end-ellipsis\";\n\n/**\n * Every page up to seven; from eight, the first and the last page, the\n * current one with its neighbours, and an ellipsis for each gap. The same\n * pages as the website's blog list.\n */\nfunction paginationItems(page: number, count: number): Item[] {\n if (count <= 7) return Array.from({ length: count }, (_, i) => i + 1);\n\n const start = Math.max(2, page - 1);\n const end = Math.min(count - 1, page + 1);\n const items: Item[] = [1];\n\n if (start > 2) items.push(\"start-ellipsis\");\n for (let i = start; i <= end; i += 1) items.push(i);\n if (end < count - 1) items.push(\"end-ellipsis\");\n items.push(count);\n\n return items;\n}\n\nconst clamp = (page: number, count: number) =>\n Math.min(Math.max(Math.round(page), 1), Math.max(count, 1));\n\ntype PageControlProps = {\n page: number;\n label?: string;\n current?: boolean;\n disabled?: boolean;\n renderLink?: (page: number) => ReactElement;\n onSelect: (page: number) => void;\n children: string | number;\n};\n\n/**\n * One page, previous or next: a link when `renderLink` is given, otherwise\n * a button. A disabled button keeps its focus (`aria-disabled`), so pressing\n * \"Forrige\" on page 2 does not drop focus on the page.\n */\nfunction PageControl({\n page,\n label,\n current = false,\n disabled = false,\n renderLink,\n onSelect,\n children,\n}: PageControlProps) {\n const shared = {\n className: styles.Item,\n \"aria-label\": label,\n \"aria-current\": current ? (\"page\" as const) : undefined,\n \"data-current\": current ? \"\" : undefined,\n \"data-disabled\": disabled ? \"\" : undefined,\n children,\n };\n\n const isLink = renderLink != null && !disabled;\n\n const link = useRender({\n enabled: isLink,\n render: isLink ? renderLink(page) : undefined,\n defaultTagName: \"a\",\n props: { ...shared, onClick: () => onSelect(page) },\n });\n\n if (link) return link;\n\n if (renderLink != null) {\n return <span {...shared} aria-disabled=\"true\" />;\n }\n\n return (\n <button\n {...shared}\n type=\"button\"\n aria-disabled={disabled || undefined}\n onClick={() => {\n if (!disabled) onSelect(page);\n }}\n />\n );\n}\n\n/**\n * Numbered pages with previous and next, for a list too long for one page (the\n * website's blog and job lists). From eight pages it shows the first, the last\n * and the current page with its neighbours.\n *\n * ```tsx\n * const [page, setPage] = useState(1);\n * <Pagination count={12} value={page} onValueChange={setPage} />\n * ```\n *\n * Wrapped in a `nav` with a translated name; the current page carries\n * `aria-current=\"page\"`, and each number is named \"Side 3\" in the\n * `LeaProvider` language. Renders nothing for a single page.\n */\nexport function Pagination({\n count,\n value: valueProp,\n defaultValue = 1,\n onValueChange,\n renderLink,\n className,\n ...rest\n}: PaginationProps) {\n const labels = usePaginationLabels();\n const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);\n const page = clamp(valueProp ?? uncontrolledValue, count);\n\n if (count <= 1) return null;\n\n const select = (next: number) => {\n if (valueProp === undefined) setUncontrolledValue(next);\n if (next !== page) onValueChange?.(next);\n };\n\n return (\n <nav\n aria-label={labels.label}\n {...rest}\n className={[styles.Pagination, className].filter(Boolean).join(\" \")}\n >\n <ul className={styles.List}>\n <li>\n <PageControl\n page={page - 1}\n disabled={page <= 1}\n renderLink={renderLink}\n onSelect={select}\n >\n {labels.previous}\n </PageControl>\n </li>\n {paginationItems(page, count).map((item) =>\n typeof item === \"number\" ? (\n <li key={item}>\n <PageControl\n page={item}\n label={withPage(labels.page, item)}\n current={item === page}\n renderLink={renderLink}\n onSelect={select}\n >\n {item}\n </PageControl>\n </li>\n ) : (\n <li key={item} aria-hidden className={styles.Ellipsis}>\n …\n </li>\n ),\n )}\n <li>\n <PageControl\n page={page + 1}\n disabled={page >= count}\n renderLink={renderLink}\n onSelect={select}\n >\n {labels.next}\n </PageControl>\n </li>\n </ul>\n </nav>\n );\n}\n","/*\n * The website's dots: 8px, 8px apart, the current one `text.base`. Each\n * button is 16 × 24 with its dot in the middle, so the dots keep their 8px\n * gap while each answers to twice its width and three times its height. The\n * negative margins give the row the size of the dots themselves (a row of\n * four is 56 × 8), so it lines up as in Figma.\n *\n * The other dots are `neutral-500`, not the website's 30 % black: a dot is the\n * whole of its button, so it needs 3:1 against the section (4.6 on white, 4.3\n * on `bg.subtle`, 3.8 on `bg.muted`; 30 % black is 2.0).\n */\n.PaginationDots {\n display: flex;\n align-items: center;\n margin-block: calc(var(--lea-spacing) * -1);\n margin-inline: calc(var(--lea-spacing-0-5) * -1);\n}\n\n.Dot {\n --dot-color: var(--lea-color-neutral-500);\n\n display: grid;\n width: var(--lea-spacing-2);\n height: var(--lea-spacing-3);\n flex-shrink: 0;\n place-items: center;\n padding: 0;\n border: 0;\n margin: 0;\n background: none;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n}\n\n.Dot::before {\n width: var(--lea-spacing-1);\n height: var(--lea-spacing-1);\n border-radius: var(--lea-radius-full);\n background-color: var(--dot-color);\n content: \"\";\n transition: background-color var(--lea-duration-fast)\n var(--lea-easing-standard);\n}\n\n.Dot[data-current] {\n --dot-color: var(--lea-palette-text-base);\n\n cursor: default;\n}\n\n/* The ring goes round the dot, not the larger button. */\n.Dot:focus-visible {\n outline: none;\n}\n\n.Dot:focus-visible::before {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n@media (hover: hover) {\n .Dot:not([data-current]):hover {\n --dot-color: var(--lea-color-neutral-700);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Dot::before {\n transition-duration: 0.01ms;\n }\n}\n","\"use client\";\n\nimport { useState, type ComponentProps } from \"react\";\n\nimport styles from \"./pagination-dots.module.css\";\nimport { usePaginationLabels, withPage } from \"../use-pagination-labels\";\n\nexport type PaginationDotsProps = Omit<\n ComponentProps<\"div\">,\n \"children\" | \"onChange\" | \"defaultValue\"\n> & {\n /** How many pages there are. */\n count: number;\n /** The current page, counted from 1. Pass it to control the dots. */\n value?: number;\n /**\n * The page shown first when the dots are not controlled.\n * @default 1\n */\n defaultValue?: number;\n /** Called with the page number when a dot is pressed. */\n onValueChange?: (page: number) => void;\n /**\n * The accessible name of each dot, when \"Gå til side 3\" does not say\n * enough: `getItemLabel={(page) => \"Vis omtale \" + page}`.\n */\n getItemLabel?: (page: number) => string;\n};\n\n/**\n * A row of dots, one per page, for a carousel or a paged list (the website's\n * testimonials and partners). The current page is black.\n *\n * ```tsx\n * <PaginationDots count={4} value={page} onValueChange={setPage} />\n * ```\n *\n * Each dot is a button named \"Gå til side 3\" in the `LeaProvider` language,\n * the current one marked `aria-current`, in a group named \"Sider\". The dots\n * are 8px, 8px apart, and each answers to a 16 × 24 area around it. Put them\n * beside previous and next buttons, which are easier to hit. Renders nothing\n * for a single page.\n */\nexport function PaginationDots({\n count,\n value: valueProp,\n defaultValue = 1,\n onValueChange,\n getItemLabel,\n className,\n ...rest\n}: PaginationDotsProps) {\n const labels = usePaginationLabels();\n const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);\n const page = valueProp ?? uncontrolledValue;\n\n if (count <= 1) return null;\n\n const select = (next: number) => {\n if (valueProp === undefined) setUncontrolledValue(next);\n if (next !== page) onValueChange?.(next);\n };\n\n return (\n <div\n role=\"group\"\n aria-label={labels.label}\n {...rest}\n className={[styles.PaginationDots, className].filter(Boolean).join(\" \")}\n >\n {Array.from({ length: count }, (_, index) => {\n const dot = index + 1;\n const current = dot === page;\n\n return (\n <button\n key={dot}\n type=\"button\"\n className={styles.Dot}\n aria-label={getItemLabel?.(dot) ?? withPage(labels.goToPage, dot)}\n aria-current={current || undefined}\n data-current={current ? \"\" : undefined}\n onClick={() => select(dot)}\n />\n );\n })}\n </div>\n );\n}\n",".AppHeader {\n display: flex;\n flex-direction: column;\n width: 100%;\n}\n\n.AppHeader:not([data-brand-divider]) {\n border-bottom: 1px solid var(--lea-palette-border-base);\n}\n\n.AppHeaderContent {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: calc(var(--lea-spacing) * 2);\n padding-block: var(--lea-spacing);\n padding-inline: calc(var(--lea-spacing) * 2);\n}\n\n.AppHeaderContent[data-fluid] {\n max-width: none;\n}\n\n.AppHeader[data-has-children] .AppHeaderContent {\n justify-content: space-between;\n}\n\n.AppHeaderLogo {\n display: flex;\n flex-shrink: 0;\n}\n\n.AppHeaderActions {\n display: flex;\n align-items: center;\n gap: var(--lea-spacing);\n}\n","import { Children, type ReactNode } from \"react\";\n\nimport { Container } from \"../../layout/container/container\";\nimport styles from \"./app-header.module.css\";\nimport { BrandDivider } from \"../../data-display/brand-divider/brand-divider\";\n\nexport type AppHeaderProps = {\n /**\n * Shows the brand divider under the header. Without it, the header gets a\n * bottom border instead.\n * @default true\n */\n brandDivider?: boolean;\n /**\n * Lets the content span the full width instead of stopping at the `lg`\n * breakpoint. The side padding stays.\n * @default false\n */\n fluid?: boolean;\n /**\n * Content for the right-hand side, e.g. buttons. Without children the logo\n * is centred.\n */\n children?: ReactNode;\n /**\n * The logo to display in the header.\n */\n logo?: ReactNode;\n /**\n * Additional classes to apply to the header.\n */\n className?: string;\n};\n\nexport const AppHeader = ({\n brandDivider = true,\n fluid = false,\n children,\n logo,\n className,\n}: AppHeaderProps) => {\n const hasChildren = Children.toArray(children).length > 0;\n\n return (\n <header\n className={[styles.AppHeader, className].filter(Boolean).join(\" \")}\n data-brand-divider={brandDivider || undefined}\n data-has-children={hasChildren || undefined}\n >\n <Container\n className={styles.AppHeaderContent}\n data-fluid={fluid || undefined}\n >\n <div className={styles.AppHeaderLogo}>\n {logo ? logo : <img src=\"/leabank-logo.png\" alt=\"Logo\" width={100} />}\n </div>\n {hasChildren && (\n <div className={styles.AppHeaderActions}>{children}</div>\n )}\n </Container>\n {brandDivider && <BrandDivider size=\"sm\" />}\n </header>\n );\n};\n","/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so this component renders correctly without a provider.\n * `--lea-spacing` is the 0.5rem base unit, multiplied to match `spacing(n)`.\n */\n\n/*\n * Each variant overrides the local properties below; the defaults are\n * `outline`. `.Item` and `.Header` only read them, so hover and active states\n * are one rule each rather than one per variant.\n */\n.Root {\n --accordion-gap: var(--lea-spacing);\n --accordion-item-bg: transparent;\n --accordion-item-border: var(--lea-palette-border-subtle);\n --accordion-item-radius: var(--lea-radius-2xl);\n --accordion-header-radius: var(--lea-radius-2xl);\n --accordion-hover-border: var(--lea-palette-border-strong);\n --accordion-hover-bg: transparent;\n --accordion-active-bg: var(--lea-color-purple-50);\n\n display: flex;\n width: 100%;\n flex-direction: column;\n gap: var(--accordion-gap);\n}\n\n.Root[data-variant=\"solid\"] {\n --accordion-item-bg: var(--lea-palette-bg-subtle);\n --accordion-item-border: transparent;\n --accordion-hover-border: transparent;\n --accordion-hover-bg: var(--lea-color-neutral-100);\n --accordion-active-bg: var(--lea-color-neutral-200);\n}\n\n/* White cards on a grey section: one step darker on hover and press. The\n website's FAQ: 16px apart, 16px inside the header, 18px semibold titles. */\n.Root[data-variant=\"card\"] {\n --accordion-gap: var(--lea-spacing-2);\n --accordion-item-bg: var(--lea-palette-bg-base);\n --accordion-item-border: transparent;\n --accordion-item-radius: var(--lea-radius-4xl);\n --accordion-header-radius: var(--lea-radius-4xl);\n --accordion-hover-border: transparent;\n --accordion-hover-bg: var(--lea-palette-bg-subtle);\n --accordion-active-bg: var(--lea-color-neutral-100);\n}\n\n/* No surface of their own, so hover and pressed lighten whatever the accordion\n sits on with translucent white; the hint of text colour keeps them visible\n on white. */\n.Root[data-variant=\"divided\"],\n.Root[data-variant=\"ghost\"] {\n --accordion-highlight: color-mix(\n in srgb,\n var(--lea-color-white) calc(var(--lea-opacity-highlight) * 100%),\n transparent\n );\n --accordion-item-border: transparent;\n --accordion-header-radius: var(--lea-radius-sm);\n --accordion-hover-border: transparent;\n --accordion-hover-bg: color-mix(\n in srgb,\n var(--lea-palette-text-base) calc(var(--lea-opacity-hover) * 100%),\n var(--accordion-highlight)\n );\n --accordion-active-bg: color-mix(\n in srgb,\n var(--lea-palette-text-base) calc(var(--lea-opacity-pressed) * 100%),\n var(--accordion-highlight)\n );\n}\n\n.Root[data-variant=\"divided\"] {\n --accordion-gap: 0;\n --accordion-item-radius: 0;\n}\n\n.Item {\n border: 1px solid var(--accordion-item-border);\n border-radius: var(--accordion-item-radius);\n background-color: var(--accordion-item-bg);\n}\n\n.Root[data-variant=\"divided\"] > .Item:not(:last-child) {\n border-block-end-color: var(--lea-palette-border-subtle);\n}\n\n/*\n * The row lives on the header rather than the trigger so a control rendered\n * beside the trigger (a Switch, say) sits inside the same hover outline.\n */\n.Header {\n display: flex;\n align-items: center;\n gap: calc(var(--lea-spacing) * 2);\n padding: calc(var(--lea-spacing) * 1.7) calc(var(--lea-spacing) * 2);\n border: 1px solid transparent;\n border-radius: var(--accordion-header-radius);\n margin: 0;\n transition: border-color var(--lea-duration-emphasis)\n var(--lea-easing-standard),\n background-color var(--lea-duration-emphasis) var(--lea-easing-standard);\n}\n\n@media (hover: hover) {\n .Header:has(.Trigger:hover:not([data-disabled])) {\n border-color: var(--accordion-hover-border);\n background-color: var(--accordion-hover-bg);\n }\n}\n\n.Header:has(.Trigger:active:not([data-disabled])) {\n background-color: var(--accordion-active-bg);\n}\n\n.Trigger {\n display: flex;\n min-width: 0;\n flex: 1;\n align-items: center;\n justify-content: space-between;\n gap: var(--lea-spacing);\n padding: 0;\n border: none;\n margin: 0;\n background-color: transparent;\n color: var(--lea-palette-text-base);\n cursor: pointer;\n font-family: inherit;\n /* label-lg with room to wrap: the label styles have a tight line height. */\n font-size: var(--lea-font-step-0);\n font-weight: var(--lea-font-weight-medium);\n line-height: var(--lea-line-height-normal);\n text-align: left;\n}\n\n.Trigger:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n.Trigger[data-disabled] {\n color: var(--lea-palette-text-muted);\n cursor: not-allowed;\n}\n\n.Label {\n min-width: 0;\n}\n\n.Icon {\n flex-shrink: 0;\n transition: transform var(--lea-duration-emphasis) var(--lea-easing-standard);\n}\n\n.Trigger[data-panel-open] .Icon {\n transform: rotate(180deg);\n}\n\n/* Arrow before the title, so it stays next to the text it belongs to. */\n.Root[data-icon-position=\"start\"] .Trigger {\n justify-content: flex-start;\n}\n\n.Root[data-icon-position=\"start\"] .Icon {\n order: -1;\n}\n\n.Panel {\n height: var(--accordion-panel-height);\n overflow: hidden;\n transition: height var(--lea-duration-emphasis) var(--lea-easing-standard);\n}\n\n.Panel[data-starting-style],\n.Panel[data-ending-style] {\n height: 0;\n}\n\n.Content {\n padding-inline: calc(var(--lea-spacing) * 2);\n padding-block: var(--lea-spacing);\n color: var(--lea-palette-text-base);\n font-size: var(--lea-text-body-md-font-size);\n font-weight: var(--lea-text-body-md-font-weight);\n line-height: var(--lea-text-body-md-line-height);\n letter-spacing: var(--lea-text-body-md-letter-spacing);\n}\n\n/*\n * The card's header and answer are spaced as on leabank.no: 16px round the\n * title, 16px between title and arrow, 32px from the title to the answer and\n * under it. The title is 16px semibold on a phone and 18px from 768px, the\n * website's tablet width.\n */\n/* 16px from the card's edge to the title and to the answer: the card has no\n border, and the header's own 1px transparent border counts. */\n.Root[data-variant=\"card\"] .Item {\n border-width: 0;\n}\n\n.Root[data-variant=\"card\"] .Header {\n padding: calc(var(--lea-spacing-2) - 1px);\n}\n\n.Root[data-variant=\"card\"] .Trigger {\n gap: var(--lea-spacing-2);\n font-size: var(--lea-font-step-0);\n font-weight: var(--lea-font-weight-semibold);\n line-height: var(--lea-line-height-snug);\n}\n\n@media (min-width: 768px) {\n .Root[data-variant=\"card\"] .Trigger {\n font-size: var(--lea-font-step-1);\n }\n}\n\n.Root[data-variant=\"card\"] .Content {\n padding: var(--lea-spacing-2) var(--lea-spacing-2) var(--lea-spacing-4);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Header,\n .Icon,\n .Panel {\n transition-duration: 0.01ms;\n }\n}\n","\"use client\";\n\nimport { Accordion as BaseAccordion } from \"@base-ui/react/accordion\";\nimport { ChevronDown } from \"lucide-react\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport styles from \"./accordion.module.css\";\n\nexport type Variant = \"outline\" | \"solid\" | \"divided\" | \"ghost\" | \"card\";\n\nexport type IconPosition = \"start\" | \"end\";\n\nexport type RootProps = BaseAccordion.Root.Props & {\n /**\n * Surface treatment of each item.\n * - `\"outline\"` — transparent cards with a light `border.subtle` border\n * (default). Hovering outlines the header.\n * - `\"solid\"` — borderless cards filled light grey (`bg.subtle`). Hovering\n * darkens the header instead of outlining it.\n * - `\"divided\"` — a flush list with no cards, items separated by thin lines.\n * Suits long FAQ lists.\n * - `\"ghost\"` — no border or fill; only the hover tint shows. Use it on\n * tinted backgrounds.\n * - `\"card\"` — borderless white cards with larger corners, for a list of\n * questions on a grey (`bg.subtle`) section of the website. Spaced as the\n * website's FAQ: 16px apart and inside, with 18px semibold titles (16px\n * below 768px).\n * @default \"outline\"\n */\n variant?: Variant;\n /**\n * Which side of the title the expand arrow sits on. Use `\"start\"` when the\n * header also holds a control (such as a `Switch`), so the arrow stays next\n * to the title it belongs to.\n * @default \"end\"\n */\n iconPosition?: IconPosition;\n};\n\n/**\n * A set of collapsible panels built on Base UI's accessible `Accordion`\n * primitive, styled with Lea's design tokens.\n *\n * Heading semantics, `aria-expanded`, `aria-controls` and the unique ids\n * linking each trigger to its panel are generated by Base UI, so the\n * relationships can never dangle and consumers never supply ids by hand.\n * Only one panel opens at a time unless you pass `multiple`.\n *\n * ```tsx\n * <Accordion.Root defaultValue={[\"shipping\"]}>\n * <Accordion.Item value=\"shipping\">\n * <Accordion.Header>\n * <Accordion.Trigger>How long does shipping take?</Accordion.Trigger>\n * </Accordion.Header>\n * <Accordion.Panel>Three to five business days.</Accordion.Panel>\n * </Accordion.Item>\n * </Accordion.Root>\n * ```\n *\n * For the common title-and-content case, reach for `SimpleAccordion` instead.\n */\nexport function Root({\n variant = \"outline\",\n iconPosition = \"end\",\n className,\n ...rest\n}: RootProps) {\n return (\n <BaseAccordion.Root\n {...rest}\n data-variant={variant}\n data-icon-position={iconPosition}\n className={mergeClassName(styles.Root, className)}\n />\n );\n}\n\nexport type ItemProps = BaseAccordion.Item.Props;\n\n/**\n * One panel of the accordion, pairing a `Header` with a `Panel`. Give it a\n * `value` to open it initially or to control it programmatically.\n */\nexport function Item({ className, ...rest }: ItemProps) {\n return (\n <BaseAccordion.Item\n {...rest}\n className={mergeClassName(styles.Item, className)}\n />\n );\n}\n\nexport type HeaderProps = BaseAccordion.Header.Props;\n\n/**\n * The heading that labels the panel. Renders an `<h3>` and must wrap a\n * `Trigger` so the accordion stays navigable by heading.\n *\n * Anything rendered after the `Trigger` sits on the same row, which is where a\n * per-item control belongs. It must go beside the trigger rather than inside\n * it, because the trigger is a `<button>` and nesting controls is invalid.\n *\n * ```tsx\n * <Accordion.Header>\n * <Accordion.Trigger>Analyse</Accordion.Trigger>\n * <Switch aria-label=\"Analyse-informasjonskapsler\" />\n * </Accordion.Header>\n * ```\n */\nexport function Header({ className, ...rest }: HeaderProps) {\n return (\n <BaseAccordion.Header\n {...rest}\n className={mergeClassName(styles.Header, className)}\n />\n );\n}\n\nexport type TriggerProps = BaseAccordion.Trigger.Props;\n\n/**\n * The button that opens and closes its panel. Pass the question or label as\n * children; the chevron is rendered internally and hidden from assistive\n * technology.\n */\nexport function Trigger({ children, className, ...rest }: TriggerProps) {\n return (\n <BaseAccordion.Trigger\n {...rest}\n className={mergeClassName(styles.Trigger, className)}\n >\n <span className={styles.Label}>{children}</span>\n <ChevronDown className={styles.Icon} size={20} aria-hidden />\n </BaseAccordion.Trigger>\n );\n}\n\nexport type PanelProps = BaseAccordion.Panel.Props;\n\n/**\n * The collapsible content of an item. Padding sits on an inner element so the\n * open/close height animation stays smooth.\n */\nexport function Panel({ children, className, ...rest }: PanelProps) {\n return (\n <BaseAccordion.Panel\n {...rest}\n className={mergeClassName(styles.Panel, className)}\n >\n <div className={styles.Content}>{children}</div>\n </BaseAccordion.Panel>\n );\n}\n","\"use client\";\n\nimport type { ReactNode } from \"react\";\n\nimport * as Accordion from \"./accordion\";\n\nexport type SimpleAccordionItem = {\n /** The heading shown on the row. */\n title: ReactNode;\n /** The collapsible body revealed when the row is opened. */\n content: ReactNode;\n /**\n * A control rendered on the row beside the title, typically a `Switch`.\n * It sits outside the trigger, so operating it doesn't open the panel.\n */\n action?: ReactNode;\n /**\n * Identifies the item for `defaultValue` / `value`. Defaults to the item's\n * position, so only set it when you open or control items programmatically.\n * Only meaningful inside `items`.\n */\n value?: string;\n /** Whether this item can be opened. Only meaningful inside `items`. */\n disabled?: boolean;\n};\n\n/**\n * The single-item form. `value` and `disabled` are left to `Accordion.Root`\n * (they'd otherwise collide with the root's own props of the same name); the\n * lone item always has the value `\"0\"`, so `defaultValue={[\"0\"]}` opens it.\n */\ntype SingleItemProps = Omit<SimpleAccordionItem, \"value\" | \"disabled\"> & {\n items?: never;\n};\n\ntype MultipleItemsProps = {\n items: SimpleAccordionItem[];\n title?: never;\n content?: never;\n action?: never;\n};\n\n// The root props inherit every global HTML attribute, two of which (`title`\n// and the RDFa `content`) share a name with ours and would narrow our\n// `ReactNode` values to `string`. Drop them so our own types win.\nexport type SimpleAccordionProps = Omit<\n Accordion.RootProps,\n \"children\" | \"title\" | \"content\"\n> &\n (SingleItemProps | MultipleItemsProps);\n\n/**\n * The props-driven form of `Accordion` for the common case: a title and some\n * content, with an optional control on the row.\n *\n * Pass `title` and `content` for a single collapsible panel, or `items` for\n * several. Everything `Accordion.Root` accepts — `variant`, `iconPosition`,\n * `multiple`, `defaultValue` — is forwarded. Compose `Accordion.Root` by hand\n * when you need markup the `title` / `content` pair can't express.\n *\n * ```tsx\n * <SimpleAccordion\n * iconPosition=\"start\"\n * title=\"Nødvendige\"\n * content={<Text>Alltid påskrudd.</Text>}\n * action={<Switch aria-label=\"Nødvendige\" checked disabled />}\n * />\n *\n * <SimpleAccordion\n * multiple\n * items={[\n * { title: \"Levering\", content: \"Tre til fem virkedager.\" },\n * { title: \"Retur\", content: \"30 dagers angrerett.\" },\n * ]}\n * />\n * ```\n */\nexport function SimpleAccordion({\n items,\n title,\n content,\n action,\n ...rest\n}: SimpleAccordionProps) {\n const entries: SimpleAccordionItem[] = items ?? [\n { title, content, action } as SimpleAccordionItem,\n ];\n\n return (\n <Accordion.Root {...rest}>\n {entries.map((item, index) => {\n const itemValue = item.value ?? String(index);\n return (\n <Accordion.Item\n key={itemValue}\n value={itemValue}\n disabled={item.disabled}\n >\n <Accordion.Header>\n <Accordion.Trigger>{item.title}</Accordion.Trigger>\n {item.action}\n </Accordion.Header>\n <Accordion.Panel>{item.content}</Accordion.Panel>\n </Accordion.Item>\n );\n })}\n </Accordion.Root>\n );\n}\n","/*\n * The website's white box (FreeBlock, FlexibleContentBlock and SimpleTable\n * with \"Rounded\" on, the Hero card, FeaturePaymentBlock): `huge` corners and\n * 64px inside, `5xl` and 32px below 768px. Flat: no border, no shadow, as an\n * in-flow surface. Its content stacks 16px apart, as the Figma slot.\n */\n.Panel {\n --panel-padding: var(--lea-spacing-8);\n --panel-radius: var(--lea-radius-huge);\n\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n gap: var(--lea-spacing-2);\n padding: var(--panel-padding);\n border-radius: var(--panel-radius);\n background-color: var(--lea-palette-bg-base);\n color: var(--lea-palette-text-base);\n}\n\n.Panel[data-size=\"sm\"] {\n --panel-padding: var(--lea-spacing-4);\n --panel-radius: var(--lea-radius-5xl);\n}\n\n@media (max-width: 767.95px) {\n .Panel:not([data-size]) {\n --panel-padding: var(--lea-spacing-4);\n --panel-radius: var(--lea-radius-5xl);\n }\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport type { Ref } from \"react\";\n\nimport { mergeClassName } from \"../../../utils/merge-class-name\";\nimport styles from \"./panel.module.css\";\n\nexport type PanelSize = \"sm\" | \"lg\";\n\nexport type PanelProps = Omit<useRender.ComponentProps<\"div\">, \"ref\"> & {\n /** The rendered element: the `<div>`, or the element passed to `render`. */\n ref?: Ref<HTMLElement>;\n /**\n * `\"lg\"`: 64px corners and 64px inside. `\"sm\"`: 32px corners and 32px\n * inside, for a narrow column such as the content beside a `SideNav`. Left\n * out, the panel is `lg` and turns `sm` below 768px, as on the website.\n */\n size?: PanelSize;\n};\n\n/**\n * The website's white box: a white surface with large round corners and\n * generous room inside, around text, a table or a form on a grey, beige or\n * dark section. What is inside stacks with 16px between.\n *\n * ```tsx\n * <Panel>\n * <Text render={<h2 />} textStyle=\"heading-xl\">Priseksempler</Text>\n * <Table>…</Table>\n * </Panel>\n * ```\n *\n * It is always white, so it disappears on a white section: put it on a\n * coloured one. It takes the width it is given. Renders a `div`; pass\n * `render` for another element, e.g. `render={<section />}`.\n */\nexport function Panel({ render, ref, className, size, ...rest }: PanelProps) {\n return useRender({\n render,\n ref,\n defaultTagName: \"div\",\n props: {\n ...rest,\n \"data-size\": size,\n className: mergeClassName(styles.Panel, className)({}),\n },\n });\n}\n","import {\n createContext,\n useContext,\n useMemo,\n type ReactNode,\n} from \"react\";\n\nimport { COUNTRIES, type Country, type Currency } from \"./countries\";\n\nexport interface CountryContextValue {\n country: Country;\n locale: string;\n currency: Currency;\n}\n\nconst CountryContext = createContext<CountryContextValue | null>(null);\n\nexport interface CountryProviderProps {\n country: Country;\n children: ReactNode;\n}\n\n/**\n * Supplies the active Lea Bank country to locale-aware formatting hooks.\n */\nexport function CountryProvider({ country, children }: CountryProviderProps) {\n const value = useMemo<CountryContextValue>(() => {\n const config = COUNTRIES[country];\n\n return {\n country: config.country,\n locale: config.locale,\n currency: config.currency,\n };\n }, [country]);\n\n return (\n <CountryContext.Provider value={value}>{children}</CountryContext.Provider>\n );\n}\n\n/**\n * Reads the active country, locale, and default currency from the nearest\n * `CountryProvider`.\n */\nexport function useCountry(): CountryContextValue {\n const context = useContext(CountryContext);\n\n if (!context) {\n throw new Error(\n \"Country hooks must be used within a <CountryProvider>.\",\n );\n }\n\n return context;\n}\n","import { useCallback, useMemo } from \"react\";\n\nimport { type Currency } from \"./country/countries\";\nimport { useCountry } from \"./country/country-context\";\n\nexport type FormatCurrencyOptions = Omit<\n Intl.NumberFormatOptions,\n \"style\" | \"currency\"\n> & {\n currency?: Currency;\n};\n\n/**\n * Returns a locale-aware currency formatter using the active country's locale\n * and default currency. Pass `currency` in options to override per call.\n */\nexport function useFormatCurrency() {\n const { locale, currency: defaultCurrency } = useCountry();\n\n const defaultFormatter = useMemo(\n () =>\n new Intl.NumberFormat(locale, {\n style: \"currency\",\n currency: defaultCurrency,\n }),\n [locale, defaultCurrency],\n );\n\n return useCallback(\n (value: number, options?: FormatCurrencyOptions) => {\n const { currency = defaultCurrency, ...formatOptions } = options ?? {};\n\n if (\n currency === defaultCurrency &&\n Object.keys(formatOptions).length === 0\n ) {\n return defaultFormatter.format(value);\n }\n\n return new Intl.NumberFormat(locale, {\n style: \"currency\",\n currency,\n ...formatOptions,\n }).format(value);\n },\n [defaultCurrency, defaultFormatter, locale],\n );\n}\n","import { useCallback, useMemo } from \"react\";\n\nimport { useCountry } from \"./country/country-context\";\n\nconst DEFAULT_DATE_OPTIONS: Intl.DateTimeFormatOptions = {\n year: \"numeric\",\n month: \"short\",\n day: \"numeric\",\n};\n\nfunction toDate(value: Date | number | string): Date {\n return value instanceof Date ? value : new Date(value);\n}\n\n/**\n * Returns a locale-aware date formatter using the active country's locale.\n */\nexport function useFormatDate() {\n const { locale } = useCountry();\n\n const defaultFormatter = useMemo(\n () => new Intl.DateTimeFormat(locale, DEFAULT_DATE_OPTIONS),\n [locale],\n );\n\n return useCallback(\n (value: Date | number | string, options?: Intl.DateTimeFormatOptions) => {\n const date = toDate(value);\n\n if (!options) {\n return defaultFormatter.format(date);\n }\n\n return new Intl.DateTimeFormat(locale, options).format(date);\n },\n [defaultFormatter, locale],\n );\n}\n","import { useCallback, useMemo } from \"react\";\n\nimport { useCountry } from \"./country/country-context\";\n\n/**\n * Returns a locale-aware plain-number formatter using the active country's\n * locale.\n */\nexport function useFormatNumber() {\n const { locale } = useCountry();\n\n const defaultFormatter = useMemo(\n () => new Intl.NumberFormat(locale),\n [locale],\n );\n\n return useCallback(\n (value: number, options?: Intl.NumberFormatOptions) => {\n if (!options) {\n return defaultFormatter.format(value);\n }\n\n return new Intl.NumberFormat(locale, options).format(value);\n },\n [defaultFormatter, locale],\n );\n}\n\n/**\n * Returns a locale-aware percentage formatter using the active country's\n * locale.\n */\nexport function useFormatPercentage() {\n const { locale } = useCountry();\n\n const defaultFormatter = useMemo(\n () =>\n new Intl.NumberFormat(locale, {\n style: \"percent\",\n minimumFractionDigits: 2,\n maximumFractionDigits: 2,\n }),\n [locale],\n );\n\n return useCallback(\n (value: number, options?: Intl.NumberFormatOptions) => {\n if (!options) {\n return defaultFormatter.format(value);\n }\n\n return new Intl.NumberFormat(locale, {\n style: \"percent\",\n ...options,\n }).format(value);\n },\n [defaultFormatter, locale],\n );\n}\n","import { useCallback } from \"react\";\n\nimport { useCountry } from \"../country/country-context\";\nimport {\n isValidBankAccount,\n isValidNationalId,\n isValidPhoneNumber,\n validateBankAccount,\n validateNationalId,\n validatePhoneNumber,\n type ValidationResult,\n} from \"../../validation\";\n\n/**\n * Returns a national-id validator bound to the active country (from\n * `CountryProvider`). The returned function reports a boolean; use\n * {@link useValidateNationalId}'s `.validate` for the normalized value.\n */\nexport function useValidateNationalId() {\n const { country } = useCountry();\n\n const isValid = useCallback(\n (value: string): boolean => isValidNationalId(country, value),\n [country],\n );\n\n const validate = useCallback(\n (value: string): ValidationResult => validateNationalId(country, value),\n [country],\n );\n\n return Object.assign(isValid, { validate });\n}\n\n/**\n * Returns a phone-number validator bound to the active country.\n */\nexport function useValidatePhoneNumber() {\n const { country } = useCountry();\n\n const isValid = useCallback(\n (value: string): boolean => isValidPhoneNumber(country, value),\n [country],\n );\n\n const validate = useCallback(\n (value: string): ValidationResult => validatePhoneNumber(country, value),\n [country],\n );\n\n return Object.assign(isValid, { validate });\n}\n\n/**\n * Returns a bank-account validator bound to the active country.\n */\nexport function useValidateBankAccount() {\n const { country } = useCountry();\n\n const isValid = useCallback(\n (value: string): boolean => isValidBankAccount(country, value),\n [country],\n );\n\n const validate = useCallback(\n (value: string): ValidationResult => validateBankAccount(country, value),\n [country],\n );\n\n return Object.assign(isValid, { validate });\n}\n","\"use client\";\n\nimport { type ReactNode } from \"react\";\nimport { LanguageContext } from \"../i18n/language-context\";\nimport { DEFAULT_LANGUAGE, type Language } from \"../i18n/languages\";\n\nexport type LeaProviderProps = {\n /** Language for the library's built-in translations and date locales. @default \"no\" */\n language?: Language;\n children: ReactNode;\n};\n\n/**\n * Sets the language of the library's own texts (`createTranslation`) and of\n * the components that format by locale. That is all it does: no component\n * needs it to render, and without it the language is Norwegian.\n */\nexport function LeaProvider({\n language = DEFAULT_LANGUAGE,\n children,\n}: LeaProviderProps) {\n return (\n <LanguageContext.Provider value={language}>\n {children}\n </LanguageContext.Provider>\n );\n}\n","/**\n * Viewport widths in pixels where layouts may change. Mobile-first: each value\n * is the `min-width` at which the breakpoint starts. The values match MUI's\n * defaults, so the MUI wrappers and the CSS Modules switch at the same widths.\n *\n * CSS custom properties can't be used in `@media`, so CSS Modules write the\n * pixel value literally; tests/tokens.test.ts checks it against this scale.\n */\nexport const breakpoints = {\n xs: 0,\n sm: 600,\n md: 900,\n lg: 1200,\n xl: 1536,\n} as const;\n\nexport type Breakpoint = keyof typeof breakpoints;\n\n/**\n * The website's own widths, for the website components only, so a layout on\n * leabank.no switches where it does today. Named as in the website's theme.\n * There each is a `max-width` (`mobile` is up to 640px); here, as everywhere in\n * the library, a CSS Module writes them mobile-first: `@media (min-width:\n * 640px)` starts above `mobile`, and `max-width: 639.95px` ends below it. That\n * moves each switch by one pixel, at exactly the width itself.\n *\n * They are not part of `Breakpoint`, so `Container` sizes and the responsive\n * props of `Stack` and `Grid` keep their five steps.\n */\nexport const websiteBreakpoints = {\n mobile: 640,\n tablet: 768,\n desktop: 1024,\n /** The website's `mobileHeader`: below it the header is the mobile one. */\n header: 1312,\n wide: 1440,\n} as const;\n\nexport type WebsiteBreakpoint = keyof typeof websiteBreakpoints;\n","export type FormatDecimals = { value: number; decimalPlaces?: number };\n\n/**\n\n * Formats a number to a fixed number of decimal places (default is 2)\n * by using exponential notation\n *\n * ⚠️ Note: We avoid using `toFixed` for rounding because it can introduce rounding errors.\n * \n * @param value - The number to format.\n * @param decimalPlaces - Number of decimal places to show (default: 2).\n * @returns The number with fixed decimal places.\n */\nexport const fixDecimals = ({ value, decimalPlaces = 2 }: FormatDecimals) => {\n return Number(\n Math.round(parseFloat(value + \"e\" + decimalPlaces)) + \"e-\" + decimalPlaces\n );\n};\n","import { fixDecimals, FormatDecimals } from \"./format-decimal\";\n\n/**\n * Formats a number to a fixed number of decimal places (default is 2).\n *\n * Ensures that string representation of number always has the set number of decimals (even eg. .00)\n *\n * @param value - The number or string to format.\n * @param decimalPlaces - Number of decimal places to show (default: 2).\n * @returns A string representation of the number with fixed decimal places.\n */\nexport const fixDecimalsToString = ({\n value,\n decimalPlaces = 2,\n}: FormatDecimals) => {\n return fixDecimals({ value, decimalPlaces }).toFixed(decimalPlaces);\n};\nexport const formatDecimalString = ({\n value,\n decimalPlaces = 2,\n}: FormatDecimals): string => {\n return fixDecimals({ value, decimalPlaces }) % 1 === 0\n ? fixDecimalsToString({ value, decimalPlaces: 0 }).replace(\".\", \",\")\n : fixDecimalsToString({ value, decimalPlaces }).replace(\".\", \",\");\n};\n","import { formatDecimalString } from \"./format-decimal-string\";\n\nexport const formatPercentage = (value: number, decimalPlaces: number = 2) => {\n return formatDecimalString({ value, decimalPlaces }) + \"%\";\n};\n","export function parseExpirationDate(expirationDate: string): {\n month: string;\n year: string;\n} {\n const match = expirationDate.match(/^([0-9]{2})([0-9]{2})$/);\n if (!match) return { month: \"\", year: \"\" };\n return { month: match[1], year: match[2] };\n}\n"],"mappings":"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"}
|