@leabankab/component-library 8.12.1 → 8.13.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 +19 -0
- package/dist/component-library.es.js +1036 -911
- 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/buttons/action-button/action-button.d.ts +25 -2
- package/dist/components/data-display/featured-image/featured-image.d.ts +13 -2
- package/dist/components/form/index.d.ts +1 -0
- package/dist/components/form/search-input/search-input.d.ts +51 -0
- package/dist/components/form/slider/slider.d.ts +28 -3
- package/dist/components/form/slider-parts.d.ts +16 -8
- package/dist/index.css +1 -1
- package/dist/tailwind.css +127 -0
- package/dist/website/calculator/calculator.d.ts +2 -1
- package/dist/website/expanding-cards/expanding-cards-item.d.ts +34 -3
- package/dist/website/payment-feature/payment-feature.d.ts +5 -4
- package/dist/website.cjs +1 -1
- package/dist/website.cjs.map +1 -1
- package/dist/website.css +1 -1
- package/dist/website.mjs +313 -308
- package/dist/website.mjs.map +1 -1
- package/package.json +1 -1
package/dist/website.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"website.cjs","names":[],"sources":["../src/website/paragraphs.tsx","../src/website/alert-banner/alert-banner.module.css","../src/website/alert-banner/alert-banner.tsx","../src/utils/merge-class-name.ts","../src/website/brand-letters/brand-letters.module.css","../src/website/brand-letters/use-brand-letters-labels.ts","../src/website/brand-letters/brand-letters.tsx","../src/website/calculator/calculator.module.css","../src/website/calculator/calculator.tsx","../src/website/carousel/carousel.module.css","../src/theme/tokens/transitions.ts","../src/website/carousel/use-carousel.ts","../src/website/carousel/use-carousel-labels.ts","../src/website/carousel/carousel.tsx","../src/website/cta-banner/cta-banner.module.css","../src/website/cta-banner/cta-banner.tsx","../src/website/expanding-cards/expanding-cards-context.ts","../src/website/expanding-cards/expanding-cards.module.css","../src/website/expanding-cards/expanding-cards.tsx","../src/website/expanding-cards/expanding-cards-item.module.css","../src/website/expanding-cards/expanding-cards-item.tsx","../src/website/hero/hero.module.css","../src/website/hero/hero.tsx","../src/website/hero-service-grid/hero-service-grid.module.css","../src/website/hero-service-grid/hero-service-grid.tsx","../src/website/navigation-grid/navigation-grid.module.css","../src/website/navigation-grid/navigation-grid.tsx","../src/website/partner-list/partner-list.module.css","../src/website/partner-list/use-partner-list-labels.ts","../src/website/partner-list/partner-list.tsx","../src/website/payment-feature/payment-feature.module.css","../src/website/payment-feature/payment-feature.tsx","../src/website/promo-grid/promo-grid.module.css","../src/website/promo-grid/promo-grid.tsx","../src/website/section/section.module.css","../src/website/section/section.tsx","../src/website/section-header/section-header.module.css","../src/website/section-header/section-header.tsx","../src/website/service-card-grid/service-card-grid.module.css","../src/website/service-card-grid/service-card-grid.tsx","../src/website/arrow-reveal-link.module.css","../src/website/arrow-reveal-link.tsx","../src/website/side-nav/side-nav.module.css","../src/website/side-nav/use-side-nav-labels.ts","../src/website/side-nav/side-nav.tsx","../src/website/site-footer/flags.tsx","../src/website/site-header-link.tsx","../src/website/site-footer/site-footer.module.css","../src/website/site-footer/use-site-footer-labels.ts","../src/website/use-site-header-labels.ts","../src/website/site-footer/site-footer.tsx","../src/website/site-header/site-header.module.css","../src/website/site-header/site-header-menu.tsx","../src/website/site-header/site-header.tsx","../src/website/split-content/split-content.module.css","../src/website/split-content/split-content.tsx","../src/website/stats/stats.module.css","../src/website/stats/stats.tsx","../src/website/testimonials/testimonials.module.css","../src/website/testimonials/use-testimonials-labels.ts","../src/website/testimonials/testimonials.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\n\n/**\n * A text prop of the sections: a string or a number becomes one `<p>`, and\n * anything else (paragraphs the page passes, rich text from the CMS) goes in\n * a `<div>`, so a `<p>` never holds another. Internal.\n */\nexport function Paragraphs({\n className,\n id,\n children,\n}: {\n className?: string;\n id?: string;\n children: ReactNode;\n}) {\n return typeof children === \"string\" || typeof children === \"number\" ? (\n <p className={className} id={id}>\n {children}\n </p>\n ) : (\n <div className={className} id={id}>\n {children}\n </div>\n );\n}\n","/*\n * The website's `AlertBanner`: a 48px icon and a column of title and text,\n * 32 apart (16 below 768px), from the top. The title and text are the\n * `Section`'s text colour; the icon is the `color` prop's status colour.\n *\n * Each colour sets the icon for the light surfaces and for the dark ones\n * (`contrast`, `contrast-accent`): the status token where it reaches 3:1 on\n * every surface of that kind, else the nearest step of its scale that does.\n * The palette has no status step for a dark surface, hence the raw steps.\n */\n.AlertBanner {\n /* error: 4.2 to 5.0 on the light surfaces. */\n --alert-banner-icon-light: var(--lea-palette-status-error-base);\n /* `status.error.base` is 2.7 on `contrast-accent`; this is 3.7 and 5.3. */\n --alert-banner-icon-dark: var(--lea-color-red-400);\n --alert-banner-icon: var(--alert-banner-icon-light);\n\n display: flex;\n align-items: flex-start;\n gap: var(--lea-spacing-2);\n}\n\n.AlertBanner[data-color=\"warning\"] {\n /* `status.warning.base` is 2.0 on white; this is 3.2 to 3.9. */\n --alert-banner-icon-light: var(--lea-color-amber-600);\n --alert-banner-icon-dark: var(--lea-palette-status-warning-base);\n}\n\n.AlertBanner[data-color=\"info\"] {\n /* 3.3 to 4.8 on all five surfaces. */\n --alert-banner-icon-light: var(--lea-palette-status-info-base);\n --alert-banner-icon-dark: var(--lea-palette-status-info-base);\n}\n\n.AlertBanner[data-color=\"success\"] {\n /* `status.success.base` is 1.9 on white; this is 3.3 to 4.0. */\n --alert-banner-icon-light: var(--lea-color-teal-600);\n --alert-banner-icon-dark: var(--lea-palette-status-success-base);\n}\n\n.AlertBanner[data-color=\"inherit\"] {\n --alert-banner-icon-light: currentcolor;\n --alert-banner-icon-dark: currentcolor;\n}\n\n/*\n * On a dark `Section` or card, and back to the light colours on a light card\n * inside one. A white box that does not set `data-surface` (`Panel`) inside\n * a dark section keeps the dark step.\n */\n:is([data-surface=\"contrast\"], [data-surface=\"contrast-accent\"]) .AlertBanner {\n --alert-banner-icon: var(--alert-banner-icon-dark);\n}\n\n:is([data-surface=\"contrast\"], [data-surface=\"contrast-accent\"])\n :is([data-surface=\"base\"], [data-surface=\"subtle\"], [data-surface=\"muted\"])\n .AlertBanner {\n --alert-banner-icon: var(--alert-banner-icon-light);\n}\n\n@media (min-width: 768px) {\n .AlertBanner {\n gap: var(--lea-spacing-4);\n }\n}\n\n.Icon {\n display: flex;\n width: var(--lea-spacing-6);\n height: var(--lea-spacing-6);\n flex-shrink: 0;\n color: var(--alert-banner-icon);\n}\n\n.Icon > :is(svg, img) {\n width: 100%;\n height: 100%;\n}\n\n/* 2px between title and text, the website's: a hair, off the scale. */\n.Content {\n display: flex;\n min-width: 0;\n flex: 1;\n flex-direction: column;\n gap: calc(var(--lea-spacing) * 0.25);\n}\n\n/* 14px semibold, as `SiteHeader`'s group titles. */\n.Title {\n margin: 0;\n font-size: var(--lea-font-step-minus-1);\n font-weight: var(--lea-font-weight-semibold);\n line-height: var(--lea-line-height-tight);\n}\n\n.Text {\n margin: 0;\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.Text p {\n margin: 0;\n}\n\n.Text p + p {\n margin-block-start: var(--lea-spacing-1);\n}\n\n/* The CMS's bare links; a Paloma `Link` keeps its own look. */\n.Text a:not([class]) {\n color: inherit;\n text-decoration: underline;\n}\n\n.Text a:not([class]):focus-visible {\n border-radius: var(--lea-radius-sm);\n outline: 2px solid currentcolor;\n outline-offset: 0.125rem;\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { TriangleAlert } from \"lucide-react\";\nimport type { ComponentProps, ReactElement, ReactNode } from \"react\";\n\nimport type { ColorRoles } from \"../../components/color-roles\";\nimport { Paragraphs } from \"../paragraphs\";\nimport styles from \"./alert-banner.module.css\";\n\n/**\n * The colour of the icon: a status, or `inherit` for the text colour of the\n * section around it.\n */\nexport type AlertBannerColor =\n | ColorRoles<\"error\" | \"warning\" | \"info\" | \"success\">\n | \"inherit\";\n\n/** A title, a text or both: a banner with neither does not compile. */\ntype AlertBannerContent =\n | {\n /** The title, 14px semibold, a heading (`titleRender`). */\n title: ReactNode;\n /**\n * The notice. A string becomes a `<p>`; anything else (the CMS's\n * paragraphs, with links) goes in a `<div>`.\n */\n text?: ReactNode;\n }\n | { title?: ReactNode; text: ReactNode };\n\nexport type AlertBannerProps = Omit<\n ComponentProps<\"div\">,\n \"children\" | \"title\" | \"color\"\n> &\n AlertBannerContent & {\n /**\n * The element of the title.\n * @default <h2 />\n */\n titleRender?: ReactElement;\n /**\n * The picture before the text, 48px, decoration: the CMS's image with\n * `alt=\"\"`, or an icon. Left out, Lucide's `TriangleAlert`; `null` for\n * none.\n */\n icon?: ReactNode;\n /**\n * The colour of the icon: `\"error\"` (red), `\"warning\"` (amber), `\"info\"`\n * (blue), `\"success\"` (teal), or `\"inherit\"` for the section's text\n * colour. The title and the text keep the section's colour whichever it\n * is. On a dark section (`surface` `contrast` or `contrast-accent`) the\n * icon takes a lighter step of the same colour. An image passed as `icon`\n * keeps its own colours.\n * @default \"error\"\n */\n color?: AlertBannerColor;\n };\n\nfunction Title({\n render,\n children,\n}: {\n render?: ReactElement;\n children: ReactNode;\n}) {\n return useRender({\n render,\n defaultTagName: \"h2\",\n props: { className: styles.Title, children },\n });\n}\n\n/**\n * The website's notice (`AlertBanner`, the global one at the foot of every\n * page and the `sections.alert-banner` block): a 48px icon, red unless\n * `color` says otherwise, beside a short title and a text in the colour of\n * the `Section` it sits in.\n *\n * ```tsx\n * <Section surface=\"muted\" spacingTop=\"sm\" spacingBottom=\"sm\">\n * <AlertBanner\n * title=\"Viktig informasjon\"\n * text=\"Lea Bank ringer deg aldri for å be om BankID, passord eller kortopplysninger.\"\n * />\n * </Section>\n * ```\n *\n * A notice that stays on the page, not a message that has just appeared: it\n * has no `role=\"alert\"` and is not announced, and it has no close button.\n * For a status message over the page, use `GlobalAlert`. The icon and the\n * text are 32px apart (16 below 768px); title and text are 14px.\n */\nexport function AlertBanner({\n title,\n text,\n titleRender,\n icon,\n color = \"error\",\n className,\n ...rest\n}: AlertBannerProps) {\n const shownIcon = icon === undefined ? <TriangleAlert /> : icon;\n\n return (\n <div\n {...rest}\n className={[styles.AlertBanner, className].filter(Boolean).join(\" \")}\n data-color={color}\n >\n {shownIcon != null && shownIcon !== false && (\n <span aria-hidden className={styles.Icon}>\n {shownIcon}\n </span>\n )}\n <div className={styles.Content}>\n {title != null && <Title render={titleRender}>{title}</Title>}\n {text != null && (\n <Paragraphs className={styles.Text}>{text}</Paragraphs>\n )}\n </div>\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 LeaBrandVideoGrid: three cells side by side with no gap,\n * each 437 : 510 and cut to a letter of the logo (L, E, A) with a mask from\n * the website's letter shapes (lea-letter-paths.ts, viewBox 0 0 437 510; the\n * L has a hole, so its path is even-odd). A video fills each letter,\n * near-black until its first frame. The pause button sits centred 24px under\n * the letters, as on `VideoPlayer`.\n */\n.BrandLetters {\n display: flex;\n min-width: 0;\n flex-direction: column;\n align-items: center;\n gap: var(--lea-spacing-3);\n}\n\n.Letters {\n display: grid;\n width: 100%;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n}\n\n.Letter {\n aspect-ratio: 437 / 510;\n overflow: hidden;\n background-color: var(--lea-palette-bg-contrast);\n -webkit-mask-repeat: no-repeat;\n mask-repeat: no-repeat;\n -webkit-mask-size: 100% 100%;\n mask-size: 100% 100%;\n}\n\n.Letter[data-letter=\"L\"] {\n -webkit-mask-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 437 510' preserveAspectRatio='none'%3E%3Cpath fill-rule='evenodd' d='M0 410.002L2.00774e-05 91.8634C2.45127e-05 41.1287 41.1287 -4.43537e-06 91.8634 0C142.598 4.43538e-06 183.727 41.1286 183.727 91.8634V99.8089C183.831 44.6692 228.563 0.0019483 283.727 0.00195312L336.615 0.00195775C391.843 0.00196258 436.615 44.7735 436.615 100.002V221.524C436.615 276.688 391.948 321.419 336.808 321.523H342.376C394.423 321.523 436.615 363.716 436.615 415.763C436.615 467.809 394.423 510.002 342.376 510.002H99.9999C44.7714 510.002 -4.82822e-06 465.23 0 410.002ZM283.53 321.523C228.458 321.418 183.833 276.793 183.727 221.721V321.522L283.53 321.523Z'/%3E%3C/svg%3E\");\n mask-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 437 510' preserveAspectRatio='none'%3E%3Cpath fill-rule='evenodd' d='M0 410.002L2.00774e-05 91.8634C2.45127e-05 41.1287 41.1287 -4.43537e-06 91.8634 0C142.598 4.43538e-06 183.727 41.1286 183.727 91.8634V99.8089C183.831 44.6692 228.563 0.0019483 283.727 0.00195312L336.615 0.00195775C391.843 0.00196258 436.615 44.7735 436.615 100.002V221.524C436.615 276.688 391.948 321.419 336.808 321.523H342.376C394.423 321.523 436.615 363.716 436.615 415.763C436.615 467.809 394.423 510.002 342.376 510.002H99.9999C44.7714 510.002 -4.82822e-06 465.23 0 410.002ZM283.53 321.523C228.458 321.418 183.833 276.793 183.727 221.721V321.522L283.53 321.523Z'/%3E%3C/svg%3E\");\n}\n\n.Letter[data-letter=\"E\"] {\n -webkit-mask-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 437 510' preserveAspectRatio='none'%3E%3Cpath d='M100 0C44.7715 0 0 44.7715 0 100V170V310C0 326.569 13.4314 340 30 340H0V510H351.615C398.559 510 436.615 471.944 436.615 425C436.615 378.056 398.559 340 351.615 340C398.559 340 436.615 301.944 436.615 255C436.615 208.056 398.559 170 351.615 170C398.559 170 436.615 131.944 436.615 85C436.615 38.0558 398.559 0 351.615 0H100Z'/%3E%3C/svg%3E\");\n mask-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 437 510' preserveAspectRatio='none'%3E%3Cpath d='M100 0C44.7715 0 0 44.7715 0 100V170V310C0 326.569 13.4314 340 30 340H0V510H351.615C398.559 510 436.615 471.944 436.615 425C436.615 378.056 398.559 340 351.615 340C398.559 340 436.615 301.944 436.615 255C436.615 208.056 398.559 170 351.615 170C398.559 170 436.615 131.944 436.615 85C436.615 38.0558 398.559 0 351.615 0H100Z'/%3E%3C/svg%3E\");\n}\n\n.Letter[data-letter=\"A\"] {\n -webkit-mask-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 437 510' preserveAspectRatio='none'%3E%3Cpath d='M436.615 170V100C436.615 44.7715 391.843 0 336.615 0H85C38.0558 0 0 38.0558 0 85C0 131.944 38.0557 170 84.9999 170H100C44.7715 170 0 214.772 0 270V410C0 465.228 44.7714 510 99.9999 510H336.615C391.843 510 436.615 465.228 436.615 410V170Z'/%3E%3C/svg%3E\");\n mask-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 437 510' preserveAspectRatio='none'%3E%3Cpath d='M436.615 170V100C436.615 44.7715 391.843 0 336.615 0H85C38.0558 0 0 38.0558 0 85C0 131.944 38.0557 170 84.9999 170H100C44.7715 170 0 214.772 0 270V410C0 465.228 44.7714 510 99.9999 510H336.615C391.843 510 436.615 465.228 436.615 410V170Z'/%3E%3C/svg%3E\");\n}\n\n.Video {\n display: block;\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n","import { createTranslation } from \"../../index\";\n\n/** The names of `BrandLetters`' button, for paused and playing videos. */\nexport const useBrandLettersLabels = createTranslation({\n play: {\n no: \"Spill av videoene\",\n se: \"Spela upp videorna\",\n fi: \"Toista videot\",\n es: \"Reproducir los vídeos\",\n },\n pause: {\n no: \"Sett videoene på pause\",\n se: \"Pausa videorna\",\n fi: \"Keskeytä videot\",\n es: \"Pausar los vídeos\",\n },\n});\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { Pause, Play } from \"lucide-react\";\nimport { useEffect, useRef, useState, type Ref } from \"react\";\n\nimport { IconButton } from \"../../index\";\nimport { mergeClassName } from \"../../utils/merge-class-name\";\nimport styles from \"./brand-letters.module.css\";\nimport { useBrandLettersLabels } from \"./use-brand-letters-labels\";\n\nconst REDUCED_MOTION = \"(prefers-reduced-motion: reduce)\";\nconst LETTERS = [\"L\", \"E\", \"A\"] as const;\n\n/** The video in one letter. */\nexport type BrandLettersVideo = {\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\nexport type BrandLettersProps = 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 /** The three videos, in the L, the E and the A. */\n videos: readonly [BrandLettersVideo, BrandLettersVideo, BrandLettersVideo];\n};\n\n/**\n * The letters L, E and A of the logo side by side, each filled with a silent\n * video in a loop, as the brand block of leabank.no.\n *\n * ```tsx\n * <Section>\n * <BrandLetters\n * videos={[\n * { src: \"/video/l.mp4\", poster: \"/video/l.jpg\" },\n * { src: \"/video/e.mp4\", poster: \"/video/e.jpg\" },\n * { src: \"/video/a.mp4\", poster: \"/video/a.jpg\" },\n * ]}\n * />\n * </Section>\n * ```\n *\n * The letters share the width they are given, each 437 : 510. The videos\n * start on their own once the page has loaded, unless the visitor has asked\n * for reduced motion: then they wait for the button. One button under the\n * letters pauses and plays all three, named \"Sett videoene på pause\" or\n * \"Spill av videoene\" in the `LeaProvider` language, since moving content\n * that plays on its own needs a way to stop it. The videos are decoration\n * and hidden from screen readers.\n */\nexport function BrandLetters({\n videos,\n render,\n ref,\n className,\n ...rest\n}: BrandLettersProps) {\n const labels = useBrandLettersLabels();\n const videoRefs = useRef<(HTMLVideoElement | null)[]>([]);\n const [playing, setPlaying] = useState(false);\n\n const each = (action: (video: HTMLVideoElement) => void) => {\n for (const video of videoRefs.current) if (video) action(video);\n };\n\n const play = (video: HTMLVideoElement) => {\n video.muted = true;\n video.play().catch(() => {});\n };\n\n useEffect(() => {\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 videos paused, with the button to start them.\n if (!reducedMotion.matches) each(play);\n\n const stopOnReducedMotion = (event: MediaQueryListEvent) => {\n if (event.matches) each((video) => video.pause());\n };\n\n reducedMotion.addEventListener(\"change\", stopOnReducedMotion);\n\n return () =>\n reducedMotion.removeEventListener(\"change\", stopOnReducedMotion);\n }, []);\n\n const update = () =>\n setPlaying(\n videoRefs.current.some((video) => video != null && !video.paused),\n );\n\n const toggle = () => {\n if (playing) each((video) => video.pause());\n else each(play);\n };\n\n return useRender({\n render,\n ref,\n defaultTagName: \"div\",\n props: {\n ...rest,\n className: mergeClassName(styles.BrandLetters, className)({}),\n children: (\n <>\n <div className={styles.Letters}>\n {LETTERS.map((letter, index) => (\n <div key={letter} className={styles.Letter} data-letter={letter}>\n <video\n ref={(video) => {\n videoRefs.current[index] = video;\n }}\n className={styles.Video}\n src={videos[index].src}\n poster={videos[index].poster}\n muted\n loop\n playsInline\n aria-hidden\n onPlay={update}\n onPause={update}\n />\n </div>\n ))}\n </div>\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 </>\n ),\n },\n });\n}\n","/*\n * The website's Calculator: the header, then 80px (32px below 768px) to the\n * body. From 1024px the body is the panel and a 280px summary column, 64px\n * apart; below, one column 32px apart. The panel is black at 30 % over the\n * section (`color-mix` on `bg.contrast`; there is no scrim token), `5xl`\n * corners, 32px inside, the sliders 16px apart. The summary: the results\n * 16px apart (the label `purple-100`, the website's light purple, the value\n * semibold, 8px apart), right-aligned from 1024px; the action at the foot\n * of the column with the price example 20px under it. No colour of its\n * own: on leabank.no it sits on purple or black, where the text is white.\n */\n.Calculator {\n display: flex;\n min-width: 0;\n flex-direction: column;\n /* The website's 80px. */\n gap: calc(var(--lea-spacing) * 10);\n}\n\n@media (max-width: 767.95px) {\n .Calculator {\n gap: var(--lea-spacing-4);\n }\n}\n\n.Body {\n display: grid;\n gap: var(--lea-spacing-4);\n grid-template-columns: minmax(0, 1fr);\n}\n\n@media (min-width: 1024px) {\n .Body {\n align-items: start;\n gap: var(--lea-spacing-8);\n /* The website's 280px summary column. */\n grid-template-columns: minmax(0, 1fr) calc(var(--lea-spacing) * 35);\n }\n}\n\n.Panel {\n display: flex;\n min-width: 0;\n flex-direction: column;\n padding: var(--lea-spacing-4);\n border-radius: var(--lea-radius-5xl);\n background-color: color-mix(\n in srgb,\n var(--lea-palette-bg-contrast) 30%,\n transparent\n );\n gap: var(--lea-spacing-2);\n}\n\n.Summary {\n display: flex;\n min-width: 0;\n flex-direction: column;\n align-items: flex-start;\n align-self: stretch;\n gap: var(--lea-spacing-4);\n}\n\n.Results {\n display: flex;\n flex-direction: column;\n margin: 0;\n gap: var(--lea-spacing-2);\n}\n\n.Result {\n display: flex;\n flex-direction: column;\n gap: var(--lea-spacing-1);\n}\n\n.Label,\n.Value {\n margin: 0;\n font-size: var(--lea-text-body-md-font-size);\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 color: var(--lea-color-purple-100);\n font-weight: var(--lea-text-body-md-font-weight);\n}\n\n.Value {\n font-variant-numeric: tabular-nums;\n font-weight: var(--lea-font-weight-semibold);\n}\n\n.Actions {\n display: flex;\n flex-direction: column;\n align-items: inherit;\n margin-block-start: auto;\n gap: var(--lea-spacing-2-5);\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@media (min-width: 1024px) {\n .Summary {\n align-items: flex-end;\n text-align: end;\n }\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport type { ReactNode, Ref } from \"react\";\n\nimport { mergeClassName } from \"../../utils/merge-class-name\";\nimport styles from \"./calculator.module.css\";\n\n/** A figure the calculator works out, shown in the summary column. */\nexport type CalculatorResult = {\n /** What the figure is: \"Beregnet sparesaldo\". */\n label: ReactNode;\n /** The figure, written out: \"399 418 kr\". */\n value: ReactNode;\n};\n\nexport type CalculatorProps = Omit<\n useRender.ComponentProps<\"div\">,\n \"ref\" | \"children\" | \"results\"\n> & {\n /** The rendered element: the `<div>`, or the element passed to `render`. */\n ref?: Ref<HTMLElement>;\n /** The heading above the calculator: a `SectionHeader`. */\n header?: ReactNode;\n /**\n * The inputs, in the panel: `Slider`s with `color=\"contrast\"`, each with\n * its `label`, `min`, `max`, `step`, `format` and `locale`.\n */\n children: ReactNode;\n /**\n * The figures worked out from the inputs, at the top of the summary\n * column. The page does the sums: one result for savings, three for\n * refinancing on leabank.no. A change is announced to screen readers.\n */\n results: readonly CalculatorResult[];\n /** The call to action under the results: an `ActionButton`. */\n action?: ReactNode;\n /** The price example under the action, in the small print. */\n priceExample?: ReactNode;\n};\n\n/**\n * The website's savings and refinancing calculator: a heading, a panel of\n * sliders and a column with the results and a call to action, as on\n * leabank.no/sparing and leabank.no/lan. It lays the parts out; the inputs\n * are `Slider`s and the sums stay with the page.\n *\n * ```tsx\n * <Section surface=\"contrast-accent\">\n * <Calculator\n * header={<SectionHeader eyebrow=\"Sparekalkulator\" title=\"Se hvor mye sparingen din kan vokse\" />}\n * results={[{ label: \"Beregnet sparesaldo\", value: format(balance) }]}\n * action={<ActionButton render={<NextLink href=\"/sparing/apne-konto\" />}>Opprett konto</ActionButton>}\n * >\n * <Slider color=\"contrast\" label=\"Startbeløp\" min={0} max={1000000} step={1000} value={start} onValueChange={setStart} locale=\"nb-NO\" format={{ style: \"currency\", currency: \"NOK\", maximumFractionDigits: 0 }} />\n * …\n * </Calculator>\n * </Section>\n * ```\n *\n * The header is 80px above the rest (32px below 768px). From 1024px the\n * panel and a 280px column sit side by side, 64px apart: the results at the\n * top of the column, right-aligned, the action and the price example at its\n * foot; below 1024px one under the other, left-aligned, 32px apart. The\n * panel is black at 30 % over the section, with `5xl` corners and 32px\n * inside, the sliders 16px apart. It has no colour of its own and is made\n * for the dark surfaces, `contrast` and `contrast-accent`.\n */\nexport function Calculator({\n header,\n results,\n action,\n priceExample,\n render,\n ref,\n className,\n children,\n ...rest\n}: CalculatorProps) {\n return useRender({\n render,\n ref,\n defaultTagName: \"div\",\n props: {\n ...rest,\n className: mergeClassName(styles.Calculator, className)({}),\n children: (\n <>\n {header}\n <div className={styles.Body}>\n <div className={styles.Panel}>{children}</div>\n <div className={styles.Summary}>\n {results.length > 0 && (\n // Announced when a figure changes, as the sliders move.\n <dl className={styles.Results} aria-live=\"polite\">\n {results.map((result, index) => (\n <div key={index} className={styles.Result}>\n <dt className={styles.Label}>{result.label}</dt>\n <dd className={styles.Value}>{result.value}</dd>\n </div>\n ))}\n </dl>\n )}\n {(action != null || priceExample != null) && (\n <div className={styles.Actions}>\n {action}\n {priceExample != null && (\n <p className={styles.Price}>{priceExample}</p>\n )}\n </div>\n )}\n </div>\n </div>\n </>\n ),\n },\n });\n}\n","/*\n * The website's carousel of reviews (react-slick there, CSS scroll snap\n * here): 2.2 slides in view from 1024px and 1.15 below, 16px apart, each\n * `(view + gap) / per-view - gap` wide as react-slick sizes them (588px of\n * 1312, 310 of 358). The slides stop at their start and stretch to the\n * tallest. The controls sit 32px under the track, 32px apart: the elevated\n * buttons (40px) and the dots between them, as on `PartnerList`.\n *\n * The edges fade over 48px into the colour behind the carousel: the colour\n * of its `Section` (`--section-bg`), white outside one. A fade shows only\n * where there is more to scroll, so the first slide is clear at the start\n * (the website fades it too).\n */\n.Carousel {\n --carousel-gap: var(--lea-spacing-2);\n --carousel-per-view: 1.15;\n --carousel-fade-color: var(--section-bg, var(--lea-palette-bg-base));\n\n display: flex;\n min-width: 0;\n flex-direction: column;\n gap: var(--lea-spacing-4);\n}\n\n@media (min-width: 1024px) {\n .Carousel {\n --carousel-per-view: 2.2;\n }\n}\n\n.Viewport {\n position: relative;\n min-width: 0;\n}\n\n.Viewport::before,\n.Viewport::after {\n position: absolute;\n z-index: 1;\n width: var(--lea-spacing-6);\n content: \"\";\n inset-block: 0;\n pointer-events: none;\n transition: opacity var(--lea-duration-fast) var(--lea-easing-standard);\n}\n\n.Viewport::before {\n background: linear-gradient(\n to right,\n var(--carousel-fade-color),\n transparent\n );\n inset-inline-start: 0;\n}\n\n.Viewport::after {\n background: linear-gradient(to left, var(--carousel-fade-color), transparent);\n inset-inline-end: 0;\n}\n\n.Viewport[data-at-start]::before,\n.Viewport[data-at-end]::after {\n opacity: 0;\n}\n\n.Track {\n position: relative;\n display: grid;\n padding: 0;\n margin: 0;\n gap: var(--carousel-gap);\n grid-auto-columns: calc(\n (100% + var(--carousel-gap)) / var(--carousel-per-view) -\n var(--carousel-gap)\n );\n grid-auto-flow: column;\n list-style: none;\n overflow-x: auto;\n overscroll-behavior-x: contain;\n scroll-snap-type: x mandatory;\n scrollbar-width: none;\n}\n\n.Track::-webkit-scrollbar {\n display: none;\n}\n\n/* The ring in the section's text colour, so it shows on any surface. */\n.Track:focus-visible {\n outline: 2px solid currentColor;\n outline-offset: 0.125rem;\n}\n\n.Slide {\n display: grid;\n min-width: 0;\n scroll-snap-align: start;\n}\n\n.Controls {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--lea-spacing-4);\n}\n\n/* \"Side 2 av 3\", for screen readers when the carousel moves. */\n.VisuallyHidden {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Viewport::before,\n .Viewport::after {\n transition-duration: 0.01ms;\n }\n}\n","export const easings = {\n standard: \"ease-in-out\",\n spring: \"cubic-bezier(0.34, 1.56, 0.64, 1)\",\n} as const;\n\nexport const durations = {\n fast: \"0.15s\",\n normal: \"0.2s\",\n emphasis: \"0.3s\",\n} as const;\n\nexport const transition = (\n properties: string[],\n duration: string = durations.normal,\n easing: string = easings.standard,\n) => properties.map((p) => `${p} ${duration} ${easing}`).join(\", \");\n","\"use client\";\n\nimport {\n useCallback,\n useEffect,\n useRef,\n useState,\n type RefObject,\n} from \"react\";\n\nimport { durations } from \"../../theme/tokens/transitions\";\n\nconst REDUCED_MOTION = \"(prefers-reduced-motion: reduce)\";\n\n/** How long the track has to rest before a page counts as reached, in ms. */\nconst SETTLE_DELAY = 150;\n\n/**\n * How long the buttons and dots take to scroll to a place, in ms: twice\n * `emphasis`, as the expanding cards open. The browser's own smooth scroll\n * crossed most of a 604px place in a few frames, which read as a jump.\n */\nconst SCROLL_DURATION = parseFloat(durations.emphasis) * 1000;\n\n/**\n * The `standard` easing token, `ease-in-out`, which is\n * `cubic-bezier(0.42, 0, 0.58, 1)`: how far along the scroll is at a point in\n * its time, both from 0 to 1.\n */\nfunction easeInOut(time: number) {\n const bezier = (t: number, p1: number, p2: number) =>\n 3 * p1 * t * (1 - t) ** 2 + 3 * p2 * t ** 2 * (1 - t) + t ** 3;\n // The curve is given by a parameter, not by time: find the parameter at\n // this time, then the distance there.\n let low = 0;\n let high = 1;\n for (let step = 0; step < 20; step += 1) {\n const middle = (low + high) / 2;\n if (bezier(middle, 0.42, 0.58) < time) low = middle;\n else high = middle;\n }\n return bezier((low + high) / 2, 0, 1);\n}\n\nexport type CarouselState = {\n /** How many places the track can stop at: one per slide that can start it. */\n pageCount: number;\n /** The place the track is nearest to now, from 0. */\n page: number;\n /** The place the track came to rest at, from 0: what is announced. */\n settledPage: number;\n /** Whether the track is at its start, so nothing lies before the view. */\n atStart: boolean;\n /** Whether the track is at its end, so nothing lies after the view. */\n atEnd: boolean;\n /** Whether the slides are wider than the track, so it scrolls. */\n overflowing: boolean;\n /**\n * Whether the track has moved since it was shown, so that the place it\n * shows first is not announced on load.\n */\n moved: boolean;\n};\n\n/**\n * The state of a scroll-snap track, for `Carousel`: the places it can stop at\n * (a slide's start, or the end of the track for the last slides, which never\n * reach the start), the one it is at, and `goTo` to scroll to another. The\n * browser does the swiping and the scrolling by hand; `goTo` scrolls over\n * `SCROLL_DURATION` itself, since the browser's smooth scroll can't be timed.\n * Internal.\n *\n * Before it has measured (on the server, and in the first render), it\n * assumes the website's 2.2 slides from 1024px: one place per slide but the\n * last.\n */\nexport function useCarousel(\n trackRef: RefObject<HTMLElement | null>,\n slideCount: number,\n) {\n const positions = useRef<number[]>([0]);\n const settleTimer = useRef<ReturnType<typeof setTimeout>>(undefined);\n /** The scroll `goTo` is running: its next frame and where it ends. */\n const scroll = useRef<{ frame: number; to: number }>(undefined);\n const [state, setState] = useState<CarouselState>(() => ({\n pageCount: Math.max(1, slideCount - 1),\n page: 0,\n settledPage: 0,\n atStart: true,\n atEnd: slideCount <= 2,\n overflowing: slideCount > 2,\n moved: false,\n }));\n\n const read = useCallback(\n (settled: boolean, moved = false) => {\n const track = trackRef.current;\n if (!track) return;\n\n const left = track.scrollLeft;\n const max = Math.max(0, track.scrollWidth - track.clientWidth);\n const places = positions.current;\n let page = 0;\n for (let index = 1; index < places.length; index += 1) {\n if (Math.abs(places[index] - left) < Math.abs(places[page] - left)) {\n page = index;\n }\n }\n\n setState((previous) => {\n const next: CarouselState = {\n pageCount: places.length,\n page,\n settledPage: settled ? page : previous.settledPage,\n atStart: left <= 1,\n atEnd: left >= max - 1,\n overflowing: max > 1,\n moved: previous.moved || moved,\n };\n return (Object.keys(next) as (keyof CarouselState)[]).every(\n (key) => next[key] === previous[key],\n )\n ? previous\n : next;\n });\n },\n [trackRef],\n );\n\n const measure = useCallback(() => {\n const track = trackRef.current;\n if (!track) return;\n\n // A slide's start, measured from the first slide, as far as the track can\n // scroll: the last slides all stop at the end. Places less than a pixel\n // apart are one.\n const max = Math.max(0, track.scrollWidth - track.clientWidth);\n const slides = Array.from(track.children) as HTMLElement[];\n const origin = slides[0]?.offsetLeft ?? 0;\n const places: number[] = [];\n for (const slide of slides) {\n const left = Math.min(Math.max(0, slide.offsetLeft - origin), max);\n if (places.length === 0 || left - places[places.length - 1] > 1) {\n places.push(left);\n }\n }\n positions.current = places.length > 0 ? places : [0];\n read(true);\n }, [read, trackRef]);\n\n /** Ends the scroll `goTo` is running, where it is, and snaps again. */\n const stop = useCallback(() => {\n if (!scroll.current) return;\n cancelAnimationFrame(scroll.current.frame);\n scroll.current = undefined;\n trackRef.current?.style.removeProperty(\"scroll-snap-type\");\n }, [trackRef]);\n\n useEffect(() => {\n const track = trackRef.current;\n if (!track) return;\n\n measure();\n\n const onScroll = () => {\n read(false, true);\n clearTimeout(settleTimer.current);\n settleTimer.current = setTimeout(() => read(true), SETTLE_DELAY);\n };\n const resizeObserver = new ResizeObserver(measure);\n // A swipe, a wheel or a key takes over from a scroll the buttons started.\n const takeOver = [\"pointerdown\", \"touchstart\", \"wheel\", \"keydown\"];\n\n track.addEventListener(\"scroll\", onScroll, { passive: true });\n for (const type of takeOver) {\n track.addEventListener(type, stop, { passive: true });\n }\n resizeObserver.observe(track);\n\n return () => {\n track.removeEventListener(\"scroll\", onScroll);\n for (const type of takeOver) track.removeEventListener(type, stop);\n resizeObserver.disconnect();\n clearTimeout(settleTimer.current);\n stop();\n };\n }, [measure, read, slideCount, stop, trackRef]);\n\n const goTo = useCallback(\n (page: number) => {\n const track = trackRef.current;\n if (!track) return;\n\n const places = positions.current;\n const to = places[Math.min(places.length - 1, Math.max(0, page))];\n // Already on its way there: a second press doesn't start it again.\n if (scroll.current?.to === to) return;\n stop();\n\n const from = track.scrollLeft;\n if (\n window.matchMedia(REDUCED_MOTION).matches ||\n Math.abs(to - from) < 1\n ) {\n track.scrollTo({ left: to, behavior: \"instant\" });\n return;\n }\n\n // Each step would snap back to a slide, so snapping is off on the way.\n // \"instant\", so that an app's `scroll-behavior: smooth` doesn't smooth\n // every step.\n track.style.setProperty(\"scroll-snap-type\", \"none\");\n const start = performance.now();\n const step = (now: number) => {\n const time = Math.min(1, (now - start) / SCROLL_DURATION);\n track.scrollTo({\n left: from + (to - from) * easeInOut(time),\n behavior: \"instant\",\n });\n if (time < 1) {\n scroll.current = { frame: requestAnimationFrame(step), to };\n } else {\n scroll.current = undefined;\n track.style.removeProperty(\"scroll-snap-type\");\n }\n };\n scroll.current = { frame: requestAnimationFrame(step), to };\n },\n [stop, trackRef],\n );\n\n return { ...state, goTo };\n}\n","import { createTranslation } from \"../../index\";\n\n/**\n * The words of `Carousel`. `carousel` describes the region to screen readers\n * (`aria-roledescription`); `{page}` and `{count}` in `status` are replaced\n * with the page shown and the number of pages.\n */\nexport const useCarouselLabels = createTranslation({\n carousel: {\n no: \"karusell\",\n se: \"karusell\",\n fi: \"karuselli\",\n es: \"carrusel\",\n },\n previous: {\n no: \"Forrige\",\n se: \"Föregående\",\n fi: \"Edellinen\",\n es: \"Anterior\",\n },\n next: {\n no: \"Neste\",\n se: \"Nästa\",\n fi: \"Seuraava\",\n es: \"Siguiente\",\n },\n status: {\n no: \"Side {page} av {count}\",\n se: \"Sida {page} av {count}\",\n fi: \"Sivu {page}/{count}\",\n es: \"Página {page} de {count}\",\n },\n});\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { ChevronLeft, ChevronRight } from \"lucide-react\";\nimport { Children, useRef, type ReactNode, type Ref } from \"react\";\n\nimport { IconButton, PaginationDots } from \"../../index\";\nimport { mergeClassName } from \"../../utils/merge-class-name\";\nimport styles from \"./carousel.module.css\";\nimport { useCarousel } from \"./use-carousel\";\nimport { useCarouselLabels } from \"./use-carousel-labels\";\n\n/**\n * A carousel is a region, so it needs a name: the title of what it shows.\n * Provide exactly one of `aria-label` or `aria-labelledby`.\n */\ntype CarouselName =\n | { \"aria-label\": string; \"aria-labelledby\"?: never }\n | { \"aria-labelledby\": string; \"aria-label\"?: never };\n\nexport type CarouselProps = Omit<\n useRender.ComponentProps<\"div\">,\n \"ref\" | \"children\" | \"aria-label\" | \"aria-labelledby\"\n> &\n CarouselName & {\n /** The rendered element: the `<div>`, or the element passed to `render`. */\n ref?: Ref<HTMLElement>;\n /**\n * The slides, such as `TestimonialCard`s. Each one is put in an item of\n * a list and stretched to the height of the tallest.\n */\n children: ReactNode;\n /**\n * The name of each dot, when \"Gå til side 3\" does not say enough, as on\n * `PaginationDots`.\n */\n getItemLabel?: (page: number) => string;\n };\n\n/**\n * A row of slides that scrolls sideways, as the website's carousel of\n * reviews: 2.2 slides in view from 1024px and 1.15 below, 16px apart, with\n * previous and next buttons and a dot per place under them.\n *\n * ```tsx\n * <Carousel aria-labelledby={titleId}>\n * <TestimonialCard rating={5} quote=\"…\" name=\"Kari Nordmann\" meta=\"Kunde, Oslo\" />\n * <TestimonialCard rating={5} quote=\"…\" name=\"Ola Hansen\" meta=\"Kunde, Bergen\" />\n * <TestimonialCard rating={4} quote=\"…\" name=\"Ingrid Berg\" meta=\"Kunde, Trondheim\" />\n * </Carousel>\n * ```\n *\n * The track scrolls on its own, with scroll snap: swipe it, scroll it with a\n * trackpad, or focus it and use the arrow keys. It stops at the start of\n * each slide; the last slides, which can't reach the start, share the end.\n * The buttons and dots move it a place at a time, and once it has moved, the\n * place it comes to rest at is announced (\"Side 2 av 3\"). The edges fade into the section's colour where\n * there is more to scroll. Under reduced motion the buttons jump instead of\n * scrolling smoothly.\n *\n * It is a region named by `aria-label` or `aria-labelledby`, described as a\n * carousel in the `LeaProvider` language. The controls show only when the\n * slides don't fit. The buttons and dots are made for the light surfaces\n * (`base`, `subtle`, `muted`).\n */\nexport function Carousel({\n getItemLabel,\n render,\n ref,\n className,\n children,\n ...rest\n}: CarouselProps) {\n const labels = useCarouselLabels();\n const trackRef = useRef<HTMLUListElement>(null);\n const slides = Children.toArray(children);\n const {\n pageCount,\n page,\n settledPage,\n atStart,\n atEnd,\n overflowing,\n moved,\n goTo,\n } = useCarousel(trackRef, slides.length);\n\n return useRender({\n render,\n ref,\n defaultTagName: \"div\",\n props: {\n role: \"region\",\n \"aria-roledescription\": labels.carousel,\n ...rest,\n className: mergeClassName(styles.Carousel, className)({}),\n children: (\n <>\n <div\n className={styles.Viewport}\n data-at-start={atStart || undefined}\n data-at-end={atEnd || undefined}\n >\n <ul\n ref={trackRef}\n className={styles.Track}\n // A track that scrolls and holds nothing focusable has to take\n // the focus itself, so the arrow keys can scroll it.\n tabIndex={overflowing ? 0 : undefined}\n >\n {slides.map((slide, index) => (\n <li key={index} className={styles.Slide}>\n {slide}\n </li>\n ))}\n </ul>\n </div>\n {pageCount > 1 && (\n <div className={styles.Controls}>\n <IconButton\n variant=\"elevated\"\n color=\"secondary\"\n aria-label={labels.previous}\n disabled={page === 0}\n focusableWhenDisabled\n onClick={() => goTo(page - 1)}\n >\n <ChevronLeft aria-hidden />\n </IconButton>\n <PaginationDots\n count={pageCount}\n value={page + 1}\n onValueChange={(next) => goTo(next - 1)}\n getItemLabel={getItemLabel}\n />\n <IconButton\n variant=\"elevated\"\n color=\"secondary\"\n aria-label={labels.next}\n disabled={page === pageCount - 1}\n focusableWhenDisabled\n onClick={() => goTo(page + 1)}\n >\n <ChevronRight aria-hidden />\n </IconButton>\n <span role=\"status\" className={styles.VisuallyHidden}>\n {moved &&\n labels.status\n .replace(\"{page}\", String(settledPage + 1))\n .replace(\"{count}\", String(pageCount))}\n </span>\n </div>\n )}\n </>\n ),\n },\n });\n}\n","/*\n * The website's CTA block: from 1024px two equal columns 64px apart, the\n * title on `display-md` in the first and the text (`body-lg`), the action and\n * the price example 32px apart in the second. Below 1024px one column, 16px\n * apart, the title on `heading-xl`. No colour of its own.\n */\n.CtaBanner {\n display: grid;\n min-width: 0;\n grid-template-columns: minmax(0, 1fr);\n align-items: start;\n gap: var(--lea-spacing-2);\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.Content {\n display: flex;\n min-width: 0;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--lea-spacing-4);\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.Text > * {\n margin: 0;\n}\n\n.Text > * + * {\n margin-block-start: var(--lea-spacing-1);\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@media (min-width: 1024px) {\n .CtaBanner[data-has-content] {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: var(--lea-spacing-8);\n }\n\n .Title {\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","\"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 styles from \"./cta-banner.module.css\";\nimport { Paragraphs } from \"../paragraphs\";\n\nexport type CtaBannerProps = Omit<\n useRender.ComponentProps<\"div\">,\n \"ref\" | \"title\" | \"children\"\n> & {\n /** The rendered element: the `<div>`, or the element passed to `render`. */\n ref?: Ref<HTMLElement>;\n /** The big title. */\n title: ReactNode;\n /**\n * The element for the title, an `<h2>` by default: `titleRender={<h1 />}`\n * where the banner opens the page.\n */\n titleRender?: ReactElement;\n /**\n * The text beside the title. A string becomes one paragraph; pass `<p>`\n * elements for several.\n */\n text?: ReactNode;\n /** The call to action under the text: an `ActionButton`. */\n action?: ReactNode;\n /** The price example under the action, in the small print. */\n priceExample?: ReactNode;\n};\n\n/**\n * A closing call to action: a big title on the left, and a text, a button\n * and the price example on the right, as at the foot of leabank.no's home\n * page.\n *\n * ```tsx\n * <Section surface=\"muted\">\n * <CtaBanner\n * title=\"Klar for å komme i gang med Lea Bank?\"\n * text=\"Vi er her for deg og står klare til å hjelpe deg.\"\n * action={<ActionButton render={<NextLink href=\"/lan\" />}>Søk om forbrukslån</ActionButton>}\n * />\n * </Section>\n * ```\n *\n * Two equal columns 64px apart from 1024px, one column below with the title\n * at 41px. With only a title, the title takes the whole width. It takes the\n * text colour of its section.\n */\nexport function CtaBanner({\n title,\n titleRender,\n text,\n action,\n priceExample,\n render,\n ref,\n className,\n ...rest\n}: CtaBannerProps) {\n const titleElement = useRender({\n render: titleRender,\n defaultTagName: \"h2\",\n props: { className: styles.Title, children: title },\n });\n const hasContent = text != null || action != null || priceExample != null;\n\n return useRender({\n render,\n ref,\n defaultTagName: \"div\",\n props: {\n ...rest,\n \"data-has-content\": hasContent || undefined,\n className: mergeClassName(styles.CtaBanner, className)({}),\n children: (\n <>\n {titleElement}\n {hasContent && (\n <div className={styles.Content}>\n {text != null && (\n <Paragraphs className={styles.Text}>{text}</Paragraphs>\n )}\n {action != null && <div className={styles.Action}>{action}</div>}\n {priceExample != null && (\n <p className={styles.Price}>{priceExample}</p>\n )}\n </div>\n )}\n </>\n ),\n },\n });\n}\n","\"use client\";\n\nimport { createContext } from \"react\";\n\n/**\n * Whether an `ExpandingCardsItem` is open: the one card `ExpandingCards`\n * keeps open in its row from 1312px, or every card below that and outside\n * the row. Internal.\n */\nexport const ExpandingCardsContext = createContext<{ open: boolean }>({\n open: true,\n});\n","/*\n * The website's CardsWithButton: from 1312px the cards share a row 16px\n * apart, each `flex: 1` and the open one `flex: 2` (640 + 320 + 320 of\n * 1312). Below 1312px one under the other, 16px apart, every card open\n * (expanding-cards-item.module.css).\n *\n * In the row a card lays out its text and photo at the width of a closed\n * card, `--expanding-cards-column` (half the open one), whatever its own\n * width is on the way. The text never wraps anew while a card opens or\n * closes, so the row keeps its height and the page under it stays where it\n * is. The width changes over `--expanding-cards-duration`, 0.6s: twice\n * `emphasis`, off the duration scale. A card grows by 320px in the row of\n * three, and at `normal` that read as a jump (Klarna's cards, the model for\n * the motion, take 0.65s).\n *\n * `--expanding-cards-count`, the number of cards, is set on the list by\n * `ExpandingCards`.\n */\n.ExpandingCards {\n min-width: 0;\n}\n\n.List {\n display: flex;\n flex-direction: column;\n padding: 0;\n margin: 0;\n gap: var(--lea-spacing-2);\n list-style: none;\n}\n\n.Item {\n display: grid;\n min-width: 0;\n}\n\n@media (min-width: 1312px) {\n .List {\n /* `100cqw` in the cards is the width of the row. */\n container-type: inline-size;\n flex-direction: row;\n }\n\n .Item {\n /* The row less its gaps, in one part per card and one more for the\n open card's second half. */\n --expanding-cards-column: calc(\n (100cqw - (var(--expanding-cards-count) - 1) * var(--lea-spacing-2)) /\n (var(--expanding-cards-count) + 1)\n );\n --expanding-cards-duration: calc(var(--lea-duration-emphasis) * 2);\n\n flex: 1 1 0;\n transition: flex-grow var(--expanding-cards-duration)\n var(--lea-easing-standard);\n }\n\n .Item[data-open] {\n flex-grow: 2;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Item {\n transition-duration: 0.01ms;\n }\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport {\n Children,\n useState,\n useSyncExternalStore,\n type CSSProperties,\n type ReactNode,\n type Ref,\n} from \"react\";\n\nimport { mergeClassName } from \"../../utils/merge-class-name\";\nimport { ExpandingCardsContext } from \"./expanding-cards-context\";\nimport styles from \"./expanding-cards.module.css\";\n\n/** The width from which the cards sit in a row and one of them is open. */\nconst ROW = \"(min-width: 1312px)\";\n\nconst OPEN = { open: true };\nconst CLOSED = { open: false };\n\nconst subscribe = (onChange: () => void) => {\n const query = window.matchMedia(ROW);\n query.addEventListener(\"change\", onChange);\n return () => query.removeEventListener(\"change\", onChange);\n};\n\n/**\n * Whether the cards are in a row. On the server, and in the first render\n * after it, they are, as on the website: below 1312px the CSS opens every\n * card anyway, and only the buttons wait for the browser to tell.\n */\nconst useRow = () =>\n useSyncExternalStore(\n subscribe,\n () => window.matchMedia(ROW).matches,\n () => true,\n );\n\nexport type ExpandingCardsDefaultOpen = \"first\" | \"last\";\n\nexport type ExpandingCardsProps = 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 * The cards: `ExpandingCardsItem`s, usually three. Each one is put in an\n * item of a list.\n */\n children: ReactNode;\n /**\n * The card that is open before the visitor points at one: the website's\n * \"Main card\".\n * @default \"first\"\n */\n defaultOpen?: ExpandingCardsDefaultOpen;\n};\n\n/**\n * A row of photo cards in which one is open, as on the home page of\n * leabank.no: the open card is twice as wide as the others, with its title,\n * text and call to action beside its photo; the others show the photo and\n * the arrow.\n *\n * ```tsx\n * <Section>\n * <ExpandingCards>\n * <ExpandingCardsItem\n * surface=\"contrast\"\n * title=\"Forbrukslån\"\n * text=\"Lån til det du trenger, uten sikkerhet.\"\n * src=\"/bilder/forbrukslan.jpg\"\n * alt=\"\"\n * action={<ActionButton render={<NextLink href=\"/lan/forbrukslan\" />}>Søk nå</ActionButton>}\n * />\n * …\n * </ExpandingCards>\n * </Section>\n * ```\n *\n * From 1312px the cards share a row, 16px apart, and the card the pointer\n * or the keyboard focus moves into opens; the last one opened stays open.\n * The row keeps its height while a card opens, so the page under it does\n * not move. Below 1312px every card is open, one under the other, and below\n * 640px the photo sits on top of the text. The cards are a list. Under\n * reduced motion they open without moving.\n */\nexport function ExpandingCards({\n defaultOpen = \"first\",\n render,\n ref,\n className,\n children,\n ...rest\n}: ExpandingCardsProps) {\n const cards = Children.toArray(children);\n const row = useRow();\n const [open, setOpen] = useState(() =>\n defaultOpen === \"last\" ? Math.max(0, cards.length - 1) : 0,\n );\n\n return useRender({\n render,\n ref,\n defaultTagName: \"div\",\n props: {\n ...rest,\n className: mergeClassName(styles.ExpandingCards, className)({}),\n children: (\n <ul\n className={styles.List}\n // The stylesheet works out the width of a closed card from it.\n style={{ \"--expanding-cards-count\": cards.length } as CSSProperties}\n >\n {cards.map((card, index) => (\n <li\n key={index}\n className={styles.Item}\n data-open={index === open || undefined}\n onPointerEnter={() => setOpen(index)}\n onFocus={() => setOpen(index)}\n >\n <ExpandingCardsContext.Provider\n value={!row || index === open ? OPEN : CLOSED}\n >\n {card}\n </ExpandingCardsContext.Provider>\n </li>\n ))}\n </ul>\n ),\n },\n });\n}\n","/*\n * A card of the website's CardsWithButton: one of the five section colours,\n * `5xl` corners, at least 280px tall. Open, the text column takes the start\n * half (32px inside, its parts 16px apart at the bottom: the title\n * `heading-md` in capitals, the text `body-md`, the `ActionButton`, which\n * fits its label and may run over the photo) and the photo the end half,\n * 16px from the card's edges with `4xl` corners (320 × 248 in a 640 × 280\n * card). Closed (only from 1312px, in a row), the photo\n * fills the card, the title and text fade out (they stay for screen\n * readers) and the button is its arrow. In the row the text and the photo\n * keep the width the row gives them (`--expanding-cards-column`, from\n * expanding-cards.module.css) while the card opens and closes, so the card\n * never changes height. Below 640px the photo is on top,\n * 2 : 1 inside 16px, and the title 18px. The text is `text.contrast` on the\n * dark surfaces (the website paints it white on every card, code issue 1).\n */\n.ExpandingCardsItem {\n --item-bg: var(--lea-palette-bg-base);\n --item-text: var(--lea-palette-text-base);\n\n position: relative;\n display: flex;\n box-sizing: border-box;\n min-width: 0;\n /* The website's 280px. */\n min-height: calc(var(--lea-spacing) * 35);\n overflow: hidden;\n border-radius: var(--lea-radius-5xl);\n background-color: var(--item-bg);\n color: var(--item-text);\n}\n\n.ExpandingCardsItem[data-surface=\"subtle\"] {\n --item-bg: var(--lea-palette-bg-subtle);\n}\n\n.ExpandingCardsItem[data-surface=\"muted\"] {\n --item-bg: var(--lea-palette-bg-muted);\n}\n\n.ExpandingCardsItem[data-surface=\"contrast\"] {\n --item-bg: var(--lea-palette-bg-contrast);\n --item-text: var(--lea-palette-text-contrast);\n}\n\n.ExpandingCardsItem[data-surface=\"contrast-accent\"] {\n --item-bg: var(--lea-palette-bg-contrast-accent);\n --item-text: var(--lea-palette-text-contrast);\n}\n\n/* The photo lies over the title and text, so a card that closes covers them\n and one that opens uncovers them; only the button lies over the photo. */\n.Content {\n position: relative;\n display: flex;\n box-sizing: border-box;\n width: 100%;\n min-width: 0;\n flex-direction: column;\n align-items: flex-start;\n justify-content: flex-end;\n padding: var(--lea-spacing-4);\n gap: var(--lea-spacing-2);\n}\n\n.ExpandingCardsItem[data-image] .Content {\n width: 50%;\n}\n\n.Title {\n max-width: 100%;\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 text-transform: uppercase;\n}\n\n.Text {\n margin: 0;\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 > * {\n margin: 0;\n}\n\n/*\n * The button is as wide as its label, and runs over the photo rather than\n * wrap its label in the text column: at most to 32px from the card's end\n * edge, where it wraps. With the photo beside the text, the card's inside\n * is twice the column's plus the 64px of padding between the two halves.\n */\n.Action {\n position: relative;\n z-index: 1;\n width: max-content;\n max-width: 100%;\n}\n\n.ExpandingCardsItem[data-image] .Action {\n max-width: calc(200% + var(--lea-spacing-8));\n}\n\n.Photo {\n position: absolute;\n width: 50%;\n inset-block: var(--lea-spacing-2);\n inset-inline-end: var(--lea-spacing-2);\n}\n\n.Image {\n display: block;\n width: 100%;\n height: 100%;\n border-radius: var(--lea-radius-4xl);\n object-fit: cover;\n}\n\n@media (min-width: 1312px) {\n /*\n * In the row, the width of a closed card (half the open one), set by\n * `ExpandingCards`, whatever the card's own width is on the way; alone,\n * the card's halves. The photo stays that wide, anchored at the end, and\n * only its 16px inset changes: closed, it fills the card.\n */\n .Content {\n width: calc(var(--expanding-cards-column, 50%) * 2);\n }\n\n .ExpandingCardsItem[data-image] .Content,\n .Photo {\n width: var(--expanding-cards-column, 50%);\n }\n\n .Photo {\n transition-duration: var(--expanding-cards-duration);\n transition-property: inset-block, inset-inline-end;\n transition-timing-function: var(--lea-easing-standard);\n }\n\n .ExpandingCardsItem:not([data-open]) .Photo {\n inset-block: 0;\n inset-inline-end: 0;\n }\n\n /*\n * Opening, the title and text fade in over the second half of the card's\n * 0.6s, as the photo moves off them; closing, they fade out at once,\n * before it covers them.\n */\n .Title,\n .Text {\n transition: opacity var(--lea-duration-emphasis) var(--lea-easing-standard)\n var(--lea-duration-emphasis);\n }\n\n /* Out of sight but still in the page: the card is as tall closed as\n open, and screen readers still read them. */\n .ExpandingCardsItem:not([data-open]) .Title,\n .ExpandingCardsItem:not([data-open]) .Text {\n opacity: 0;\n transition-delay: 0s;\n transition-duration: var(--lea-duration-fast);\n }\n}\n\n@media (max-width: 639.95px) {\n .ExpandingCardsItem {\n flex-direction: column;\n }\n\n .ExpandingCardsItem[data-image] .Content {\n width: 100%;\n flex-grow: 1;\n }\n\n /* The photo is on top: the button keeps to the text. */\n .ExpandingCardsItem[data-image] .Action {\n max-width: 100%;\n }\n\n .Photo {\n position: static;\n width: auto;\n order: -1;\n padding: var(--lea-spacing-2);\n }\n\n .Image {\n height: auto;\n aspect-ratio: 2 / 1;\n }\n\n .Title {\n font-size: var(--lea-font-step-1);\n font-weight: var(--lea-font-weight-semibold);\n line-height: var(--lea-line-height-snug);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Photo,\n .Title,\n .Text,\n .ExpandingCardsItem:not([data-open]) .Title,\n .ExpandingCardsItem:not([data-open]) .Text {\n transition-duration: 0.01ms;\n transition-delay: 0s;\n }\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport {\n cloneElement,\n isValidElement,\n useContext,\n type ReactElement,\n type ReactNode,\n type Ref,\n} from \"react\";\n\nimport type { SurfaceRoles } from \"../../components/color-roles\";\nimport { ActionButton } from \"../../index\";\nimport { mergeClassName } from \"../../utils/merge-class-name\";\nimport { ExpandingCardsContext } from \"./expanding-cards-context\";\nimport styles from \"./expanding-cards-item.module.css\";\nimport { Paragraphs } from \"../paragraphs\";\n\nexport type ExpandingCardsItemSurface = SurfaceRoles<\n \"base\" | \"subtle\" | \"muted\" | \"contrast\" | \"contrast-accent\"\n>;\n\n/** `alt` is required with a photo, `\"\"` when it is decoration. */\ntype ExpandingCardsItemImage =\n | {\n /** The photo: the whole card while it is closed, half of it open. */\n src: string;\n /** What the photo shows, for screen readers; `\"\"` for decoration. */\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 ExpandingCardsItemProps = Omit<\n useRender.ComponentProps<\"div\">,\n \"ref\" | \"title\" | \"children\"\n> &\n ExpandingCardsItemImage & {\n /** The rendered element: the `<div>`, or the element passed to `render`. */\n ref?: Ref<HTMLElement>;\n /** The title, in capitals. */\n title: ReactNode;\n /**\n * The element for the title, an `<h2>` by default. Pass another level\n * where the page outline needs one: `titleRender={<h3 />}`.\n */\n titleRender?: ReactElement;\n /** The text under the title. A string becomes one paragraph. */\n text?: ReactNode;\n /**\n * The call to action: an `ActionButton`. While the card is closed it\n * shows only its arrow, with the label kept for screen readers.\n */\n action?: ReactNode;\n /**\n * The colour of the card, after its `bg` token, as on `Section`. On\n * `\"contrast\"` and `\"contrast-accent\"` the text is white.\n * @default \"base\"\n */\n surface?: ExpandingCardsItemSurface;\n };\n\n/**\n * A card of `ExpandingCards`: a photo, a title in capitals, a text and a\n * call to action, on one of the five section colours.\n *\n * ```tsx\n * <ExpandingCardsItem\n * surface=\"contrast\"\n * title=\"Forbrukslån\"\n * text=\"Lån til det du trenger, uten sikkerhet. Søk på få minutter.\"\n * src=\"/bilder/forbrukslan.jpg\"\n * alt=\"\"\n * action={<ActionButton render={<NextLink href=\"/lan/forbrukslan\" />}>Søk nå</ActionButton>}\n * />\n * ```\n *\n * Open, the text takes the start half (32px inside, at the bottom) and the\n * photo the end half, 16px from the card's edges. Closed (in the row of\n * `ExpandingCards` from 1312px), the photo fills the card and only the\n * `ActionButton`'s arrow shows; the title and text are still there for\n * screen readers, and focusing the button opens the card. Below 640px the\n * photo is on top at 2 : 1. Outside `ExpandingCards` the card is always\n * open. At least 280px tall; it grows with its text.\n */\nexport function ExpandingCardsItem({\n title,\n titleRender,\n text,\n action,\n src,\n alt,\n imageRender,\n surface = \"base\",\n render,\n ref,\n className,\n ...rest\n}: ExpandingCardsItemProps) {\n const { open } = useContext(ExpandingCardsContext);\n\n const titleElement = useRender({\n render: titleRender,\n defaultTagName: \"h2\",\n props: { className: styles.Title, 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 // A closed card has room for the arrow only.\n const actionElement =\n isValidElement<{ iconOnly?: boolean }>(action) &&\n action.type === ActionButton\n ? cloneElement(action, { iconOnly: !open })\n : action;\n\n return useRender({\n render,\n ref,\n defaultTagName: \"div\",\n props: {\n ...rest,\n \"data-surface\": surface,\n \"data-open\": open || undefined,\n \"data-image\": src != null || undefined,\n className: mergeClassName(styles.ExpandingCardsItem, className)({}),\n children: (\n <>\n <div className={styles.Content}>\n {titleElement}\n {text != null && (\n <Paragraphs className={styles.Text}>{text}</Paragraphs>\n )}\n {action != null && (\n <div className={styles.Action}>{actionElement}</div>\n )}\n </div>\n {image != null && <div className={styles.Photo}>{image}</div>}\n </>\n ),\n },\n });\n}\n","/*\n * The website's HeroWithBackgroundImage: a band at least 700px tall (600 below\n * 768px, 500 below 640px), 112px / 64px inside (80 / 16, then 64 / 16), with\n * the photo filling it behind the content and a `Panel` at most 600px wide\n * (500 below 768px, the full column below 640px) at the start of the 1312px\n * column, centred vertically. The panel's parts are 32px apart.\n */\n.Hero {\n position: relative;\n isolation: isolate;\n display: flex;\n box-sizing: border-box;\n width: 100%;\n /* The website's min-height: 700px. */\n min-height: calc(var(--lea-spacing) * 87.5);\n align-items: center;\n overflow: hidden;\n /* 112px, the website's `huge`: off the spacing scale. */\n padding: calc(var(--lea-spacing) * 14) var(--lea-spacing-8);\n /* Behind the photo while it loads. */\n background-color: var(--lea-palette-bg-muted);\n}\n\n.Background {\n position: absolute;\n z-index: -1;\n inset: 0;\n}\n\n.Image {\n display: block;\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n\n.Container {\n width: 100%;\n max-width: var(--lea-width-section);\n margin-inline: auto;\n}\n\n/* Two classes, to win over the Panel's own gap whichever stylesheet loads\n last. */\n.Container .Box {\n max-width: calc(var(--lea-spacing) * 75);\n gap: var(--lea-spacing-4);\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-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 > * {\n margin: 0;\n}\n\n.Text > * + * {\n margin-block-start: var(--lea-spacing-2);\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@media (max-width: 767.95px) {\n .Hero {\n /* The website's 600px. */\n min-height: calc(var(--lea-spacing) * 75);\n /* 80px, the website's `xxl`. */\n padding: calc(var(--lea-spacing) * 10) var(--lea-spacing-2);\n }\n\n .Container .Box {\n /* The website's 500px. */\n max-width: calc(var(--lea-spacing) * 62.5);\n }\n\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\n@media (max-width: 639.95px) {\n .Hero {\n /* The website's 500px. */\n min-height: calc(var(--lea-spacing) * 62.5);\n padding: var(--lea-spacing-8) var(--lea-spacing-2);\n }\n\n .Container .Box {\n max-width: none;\n }\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport type { ReactElement, ReactNode, Ref } from \"react\";\n\nimport { Panel } from \"../../index\";\nimport { mergeClassName } from \"../../utils/merge-class-name\";\nimport styles from \"./hero.module.css\";\nimport { Paragraphs } from \"../paragraphs\";\n\nexport type HeroProps = Omit<\n useRender.ComponentProps<\"section\">,\n \"ref\" | \"title\" | \"children\"\n> & {\n /** The rendered element: the `<section>`, or the element passed to `render`. */\n ref?: Ref<HTMLElement>;\n /** The photo behind the band, cropped to fill it. */\n src: string;\n /**\n * What the photo shows, for screen readers; `\"\"` when it is decoration,\n * as a photo behind a title usually is.\n */\n alt: string;\n /**\n * The image element to render instead of the plain `<img>`, such as\n * `next/image` with `fill` and `priority` (the hero photo is usually the\n * largest thing on the page). Its own props win over `src` and `alt`.\n */\n imageRender?: ReactElement;\n /** The title of the page. */\n title: ReactNode;\n /**\n * The element for the title, an `<h1>` by default, as a hero opens the\n * page: `titleRender={<h2 />}` where it does not.\n */\n titleRender?: ReactElement;\n /**\n * The text under the title. A string becomes one paragraph; pass `<p>`\n * elements for several.\n */\n text?: ReactNode;\n /** The call to action under the text: an `ActionButton`. */\n action?: ReactNode;\n /** The price example under the action, in the small print. */\n priceExample?: ReactNode;\n};\n\n/**\n * The top of a product page: a photo from edge to edge, and on it a white\n * `Panel` with the page title, a text, a call to action and the price\n * example, as on leabank.no's loan pages.\n *\n * ```tsx\n * <Hero\n * src=\"/bilder/kjokken.jpg\"\n * alt=\"\"\n * imageRender={<NextImage src={kitchen} alt=\"\" fill priority />}\n * title=\"Forbrukslån med rente fra 7,90 %\"\n * text=\"Søk om lån uten sikkerhet på få minutter og få svar med én gang.\"\n * action={<ActionButton render={<NextLink href=\"/sok\" />}>Søk om lån</ActionButton>}\n * />\n * ```\n *\n * It is a band of its own, not inside a `Section`: at least 700px tall\n * (600px below 768px, 500px below 640px), with the panel at most 600px wide\n * at the start of the 1312px column. In the reading order the panel comes\n * first and the photo after it.\n */\nexport function Hero({\n src,\n alt,\n imageRender,\n title,\n titleRender,\n text,\n action,\n priceExample,\n render,\n ref,\n className,\n ...rest\n}: HeroProps) {\n const titleElement = useRender({\n render: titleRender,\n defaultTagName: \"h1\",\n props: { className: styles.Title, children: title },\n });\n\n const image = useRender({\n render: imageRender,\n defaultTagName: \"img\",\n props: { src, alt, className: styles.Image },\n });\n\n return useRender({\n render,\n ref,\n defaultTagName: \"section\",\n props: {\n ...rest,\n className: mergeClassName(styles.Hero, className)({}),\n children: (\n <>\n <div className={styles.Container}>\n <Panel className={styles.Box}>\n {titleElement}\n {text != null && (\n <Paragraphs className={styles.Text}>{text}</Paragraphs>\n )}\n {action != null && <div className={styles.Action}>{action}</div>}\n {priceExample != null && (\n <p className={styles.Price}>{priceExample}</p>\n )}\n </Panel>\n </div>\n <div className={styles.Background}>{image}</div>\n </>\n ),\n },\n });\n}\n","/*\n * The website's HeroServiceGrid. From 1024px two equal columns 16px apart,\n * at least 600px tall: a `FeaturedImage` filling one, and the cards two by\n * two in the other, in rows of one height (292px each at 600). Below 1024px the\n * photo is on top at `FeaturedImage`'s own 1 : 0.7, the cards two by two\n * under it from 768px and one under the other below.\n */\n.HeroServiceGrid {\n display: grid;\n min-width: 0;\n grid-template-columns: minmax(0, 1fr);\n gap: var(--lea-spacing-2);\n}\n\n.Cards {\n display: grid;\n min-width: 0;\n grid-template-columns: minmax(0, 1fr);\n gap: var(--lea-spacing-2);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n/* The card fills its cell. */\n.Item {\n display: grid;\n min-width: 0;\n grid-template-columns: minmax(0, 1fr);\n}\n\n@media (min-width: 768px) {\n .Cards {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n /* Rows of one height, so a title that wraps further doesn't make its\n row taller than the other. */\n grid-auto-rows: 1fr;\n }\n}\n\n@media (min-width: 1024px) {\n .HeroServiceGrid {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n /* The website's min-height: 600px. */\n grid-template-rows: minmax(calc(var(--lea-spacing) * 75), auto);\n }\n\n /* Two classes, to win over FeaturedImage's own rules whichever stylesheet\n loads last. The photo takes the height the row is given and never sets\n it, so a tall photo can't stretch the block. */\n .HeroServiceGrid .Featured {\n contain: size;\n }\n\n .HeroServiceGrid[data-image-position=\"end\"] .Featured {\n order: 1;\n }\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { Children, type ReactElement, type ReactNode, type Ref } from \"react\";\n\nimport { FeaturedImage } from \"../../index\";\nimport { mergeClassName } from \"../../utils/merge-class-name\";\nimport styles from \"./hero-service-grid.module.css\";\n\nexport type HeroServiceGridImagePosition = \"start\" | \"end\";\n\nexport type HeroServiceGridProps = 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 /** The large photo. */\n src: string;\n /** What the photo shows, for screen readers; `\"\"` when it is decoration. */\n alt: string;\n /**\n * The image element to render instead of the plain `<img>`, as on\n * `FeaturedImage`: `next/image` with `fill`, or an `<img loading=\"lazy\">`.\n */\n imageRender?: ReactElement;\n /** A call to action on the photo: an `ActionButton`. */\n action?: ReactNode;\n /**\n * Where the photo sits from 1024px: `\"start\"` (on the left) or `\"end\"`.\n * Below 1024px it is always on top.\n * @default \"start\"\n */\n imagePosition?: HeroServiceGridImagePosition;\n /**\n * The cards beside the photo: up to four `LinkCard`s, two by two. Each one\n * is put in an item of a list.\n */\n children?: ReactNode;\n};\n\n/**\n * A large photo beside a block of link cards, as on leabank.no's home page:\n * a `FeaturedImage` with a call to action, and up to four `LinkCard`s.\n *\n * ```tsx\n * <Section>\n * <HeroServiceGrid\n * src=\"/bilder/familie.jpg\"\n * alt=\"\"\n * action={<ActionButton render={<NextLink href=\"/lan\" />}>Se våre lån</ActionButton>}\n * >\n * <LinkCard render={<NextLink href=\"/lan\" />} title=\"Lån etter dine behov.\" surface=\"contrast\" />\n * <LinkCard render={<NextLink href=\"/sparing\" />} title=\"Se hvor mye du kan spare.\" surface=\"muted\" />\n * </HeroServiceGrid>\n * </Section>\n * ```\n *\n * From 1024px two equal columns 16px apart, at least 600px tall: the photo\n * fills one, the cards share the other two by two. Below 1024px the photo is\n * on top at 1 : 0.7, the cards two by two under it, and one under the other\n * below 768px. The cards are a list.\n */\nexport function HeroServiceGrid({\n src,\n alt,\n imageRender,\n action,\n imagePosition = \"start\",\n render,\n ref,\n className,\n children,\n ...rest\n}: HeroServiceGridProps) {\n const cards = Children.toArray(children);\n\n return useRender({\n render,\n ref,\n defaultTagName: \"div\",\n props: {\n ...rest,\n \"data-image-position\": imagePosition,\n className: mergeClassName(styles.HeroServiceGrid, className)({}),\n children: (\n <>\n <FeaturedImage\n className={styles.Featured}\n src={src}\n alt={alt}\n imageRender={imageRender}\n action={action}\n />\n {cards.length > 0 && (\n <ul className={styles.Cards}>\n {cards.map((card, index) => (\n <li key={index} className={styles.Item}>\n {card}\n </li>\n ))}\n </ul>\n )}\n </>\n ),\n },\n });\n}\n","/*\n * The website's NavigationGrid: the title on `heading-xl` (`heading-md` below\n * 768px), 32px over a grid of cards 16px apart. From 1024px four columns\n * with rows of 300px; every card is two columns wide, except the first two\n * of an odd number, which are one (1 + 1 + 2, then 2 + 2). Two columns from\n * 768px, where the wide cards take a row of their own; one column below.\n * The title is the section's text colour.\n */\n.NavigationGrid {\n display: flex;\n min-width: 0;\n flex-direction: column;\n gap: var(--lea-spacing-4);\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.List {\n display: grid;\n grid-template-columns: minmax(0, 1fr);\n gap: var(--lea-spacing-2);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n/* The card fills its cell. */\n.Item {\n display: grid;\n min-width: 0;\n grid-template-columns: minmax(0, 1fr);\n}\n\n@media (min-width: 768px) {\n .List {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n }\n\n .Item {\n grid-column: span 2;\n }\n\n .List[data-odd] > .Item:nth-child(-n + 2) {\n grid-column: span 1;\n }\n}\n\n@media (min-width: 1024px) {\n .List {\n grid-template-columns: repeat(4, minmax(0, 1fr));\n /* The website's 300px rows; a long title makes its row taller. */\n grid-auto-rows: minmax(calc(var(--lea-spacing) * 37.5), auto);\n }\n}\n\n@media (max-width: 767.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 { Children, type ReactElement, type ReactNode, type Ref } from \"react\";\n\nimport { mergeClassName } from \"../../utils/merge-class-name\";\nimport styles from \"./navigation-grid.module.css\";\n\nexport type NavigationGridProps = Omit<\n useRender.ComponentProps<\"div\">,\n \"ref\" | \"title\" | \"children\"\n> & {\n /** The rendered element: the `<div>`, or the element passed to `render`. */\n ref?: Ref<HTMLElement>;\n /**\n * The heading over the cards. Left out, there is no heading and no room\n * kept for one.\n */\n title?: ReactNode;\n /** The element for the title, an `<h2>` by default: `titleRender={<h3 />}`. */\n titleRender?: ReactElement;\n /**\n * The cards: `LinkCard`s, three to five as on the website. Each one is put\n * in an item of a list.\n */\n children?: ReactNode;\n};\n\n/**\n * A grid of `LinkCard`s under a title, as the \"Hva kan vi hjelpe deg med?\"\n * block on leabank.no's customer service page.\n *\n * ```tsx\n * <Section surface=\"subtle\">\n * <NavigationGrid title=\"Hva kan vi hjelpe deg med?\">\n * <LinkCard render={<NextLink href=\"/lan\" />} title=\"Lån\" surface=\"muted\" />\n * <LinkCard render={<NextLink href=\"/sparing\" />} title=\"Sparing\" surface=\"contrast\" />\n * <LinkCard render={<NextLink href=\"/forsikring\" />} title=\"Forsikring\" surface=\"muted\" />\n * </NavigationGrid>\n * </Section>\n * ```\n *\n * From 1024px the cards are two to a row of four columns, each two columns\n * wide; with an odd number of cards the first row holds three, the first two\n * one column wide (1 + 1 + 2). Rows are 300px, or taller for a long title.\n * Between 768 and 1024px two columns, the wide cards taking a row of their\n * own; below 768px one column. The cards are a list.\n */\nexport function NavigationGrid({\n title,\n titleRender,\n render,\n ref,\n className,\n children,\n ...rest\n}: NavigationGridProps) {\n const cards = Children.toArray(children);\n const odd = cards.length >= 3 && cards.length % 2 === 1;\n\n const titleElement = useRender({\n enabled: title != null,\n render: titleRender,\n defaultTagName: \"h2\",\n props: { className: styles.Title, children: title },\n });\n\n return useRender({\n render,\n ref,\n defaultTagName: \"div\",\n props: {\n ...rest,\n className: mergeClassName(styles.NavigationGrid, className)({}),\n children: (\n <>\n {titleElement}\n <ul className={styles.List} data-odd={odd || undefined}>\n {cards.map((card, index) => (\n <li key={index} className={styles.Item}>\n {card}\n </li>\n ))}\n </ul>\n </>\n ),\n },\n });\n}\n","/*\n * The website's PartnersBlock: the rows one under the other (16px apart\n * below 768px, where each row is one column), then the controls centred,\n * 32px apart, with 32px above them and 64px below: two elevated buttons\n * (40px) and the dots between.\n */\n.PartnerList {\n min-width: 0;\n}\n\n.List {\n display: flex;\n flex-direction: column;\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.Controls {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--lea-spacing-4);\n padding-block: var(--lea-spacing-4) var(--lea-spacing-8);\n}\n\n/* \"Side 2 av 3\", for screen readers when the page turns. */\n.VisuallyHidden {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n}\n\n@media (max-width: 767.95px) {\n .List {\n gap: var(--lea-spacing-2);\n }\n}\n","import { createTranslation } from \"../../index\";\n\n/**\n * The words of `PartnerList`'s controls. `{page}` and `{count}` are replaced\n * with the page number and the number of pages. `collation` is the language\n * the partners are sorted in, as a BCP 47 tag for `Intl.Collator`.\n */\nexport const usePartnerListLabels = createTranslation({\n previous: {\n no: \"Forrige side\",\n se: \"Föregående sida\",\n fi: \"Edellinen sivu\",\n es: \"Página anterior\",\n },\n next: {\n no: \"Neste side\",\n se: \"Nästa sida\",\n fi: \"Seuraava sivu\",\n es: \"Página siguiente\",\n },\n status: {\n no: \"Side {page} av {count}\",\n se: \"Sida {page} av {count}\",\n fi: \"Sivu {page}/{count}\",\n es: \"Página {page} de {count}\",\n },\n collation: { no: \"nb\", se: \"sv\", fi: \"fi\", es: \"es\" },\n});\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { ChevronLeft, ChevronRight } from \"lucide-react\";\nimport {\n useMemo,\n useState,\n type ReactElement,\n type ReactNode,\n type Ref,\n} from \"react\";\n\nimport { IconButton, PaginationDots, PartnerRow } from \"../../index\";\nimport { mergeClassName } from \"../../utils/merge-class-name\";\nimport styles from \"./partner-list.module.css\";\nimport { usePartnerListLabels } from \"./use-partner-list-labels\";\n\n/** `alt` is required with a logo. */\ntype PartnerListLogo =\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>`, as on\n * `PartnerRow`: `next/image`, or an `<img loading=\"lazy\">`.\n */\n imageRender?: ReactElement;\n }\n | { src?: undefined; alt?: undefined; imageRender?: undefined };\n\n/** One partner in the list. */\nexport type PartnerListItem = PartnerListLogo & {\n /** The partner's name. The list is sorted by it. */\n name: string;\n /**\n * The text about the partner, beside the logo: usually `RichText` with the\n * CMS content, whose heading is the name.\n */\n content: ReactNode;\n};\n\nexport type PartnerListProps = Omit<\n useRender.ComponentProps<\"div\">,\n \"ref\" | \"children\" | \"defaultValue\" | \"onChange\"\n> & {\n /** The rendered element: the `<div>`, or the element passed to `render`. */\n ref?: Ref<HTMLElement>;\n /** The partners, in any order. */\n items: readonly PartnerListItem[];\n /**\n * How many partners a page shows.\n * @default 9\n */\n pageSize?: number;\n /** The page shown, counted from 1. Pass it to control the list. */\n value?: number;\n /**\n * The page shown first when the list is not controlled.\n * @default 1\n */\n defaultValue?: number;\n /** Called with the page number when the visitor turns the page. */\n onValueChange?: (page: number) => void;\n};\n\n/**\n * The website's list of partners, as on leabank.no/om-oss/samarbeidspartnere:\n * a `PartnerRow` per partner, sorted by name, nine to a page, with previous\n * and next buttons and a dot per page under them.\n *\n * ```tsx\n * <Section surface=\"subtle\" width=\"wide\">\n * <PartnerList\n * items={partners.map((partner) => ({\n * name: partner.name,\n * content: <RichText>…</RichText>,\n * src: partner.logo,\n * alt: \"\",\n * }))}\n * />\n * </Section>\n * ```\n *\n * The partners are sorted in the `LeaProvider` language (Å after Z in\n * Norwegian and Swedish, Ä and Ö in Swedish and Finnish order), so the\n * order follows the market. The controls show from two pages: elevated\n * buttons named \"Forrige side\" and \"Neste side\", which stay focusable at the\n * first and last page, and `PaginationDots`; the new page is announced as\n * \"Side 2 av 3\". The list takes the width it is given: on the website a\n * `Section` with `width=\"wide\"`.\n */\nexport function PartnerList({\n items,\n pageSize = 9,\n value: valueProp,\n defaultValue = 1,\n onValueChange,\n render,\n ref,\n className,\n ...rest\n}: PartnerListProps) {\n const labels = usePartnerListLabels();\n const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);\n\n const sorted = useMemo(() => {\n const collator = new Intl.Collator(labels.collation, {\n sensitivity: \"base\",\n });\n return [...items].sort((a, b) => collator.compare(a.name, b.name));\n }, [items, labels.collation]);\n\n const size = Math.max(1, Math.floor(pageSize));\n const pageCount = Math.max(1, Math.ceil(sorted.length / size));\n const page = Math.min(pageCount, Math.max(1, valueProp ?? uncontrolledValue));\n const start = (page - 1) * size;\n\n const select = (next: number) => {\n const clamped = Math.min(pageCount, Math.max(1, next));\n if (valueProp === undefined) setUncontrolledValue(clamped);\n if (clamped !== page) onValueChange?.(clamped);\n };\n\n return useRender({\n enabled: sorted.length > 0,\n render,\n ref,\n defaultTagName: \"div\",\n props: {\n ...rest,\n className: mergeClassName(styles.PartnerList, className)({}),\n children: (\n <>\n <ul className={styles.List}>\n {sorted\n .slice(start, start + size)\n .map(({ name, content, ...logo }, index) => (\n <PartnerRow\n key={`${start + index}-${name}`}\n {...logo}\n render={<li />}\n >\n {content}\n </PartnerRow>\n ))}\n </ul>\n {pageCount > 1 && (\n <div className={styles.Controls}>\n <IconButton\n variant=\"elevated\"\n color=\"secondary\"\n aria-label={labels.previous}\n disabled={page === 1}\n focusableWhenDisabled\n onClick={() => select(page - 1)}\n >\n <ChevronLeft aria-hidden />\n </IconButton>\n <PaginationDots\n count={pageCount}\n value={page}\n onValueChange={select}\n />\n <IconButton\n variant=\"elevated\"\n color=\"secondary\"\n aria-label={labels.next}\n disabled={page === pageCount}\n focusableWhenDisabled\n onClick={() => select(page + 1)}\n >\n <ChevronRight aria-hidden />\n </IconButton>\n <span role=\"status\" className={styles.VisuallyHidden}>\n {labels.status\n .replace(\"{page}\", String(page))\n .replace(\"{count}\", String(pageCount))}\n </span>\n </div>\n )}\n </>\n ),\n },\n });\n}\n","/*\n * The website's FeaturePaymentBlock on a `Panel` (64px inside, 32px below\n * 768px). From 1024px a text column of at most 560px and a photo column 64px\n * apart, centred on each other; the photo at most 435px wide at the end of\n * its column. In the text column, 64px apart (32px below 1024px): the logos,\n * the title (`heading-xl`, `heading-md` below 768px) and the text (`body-lg`)\n * 32px apart, the actions 16px apart, the price example. Below 1024px one\n * column, 32px between the text and the photo.\n */\n.Grid {\n display: grid;\n grid-template-columns: minmax(0, 1fr);\n align-items: center;\n gap: var(--lea-spacing-4);\n}\n\n.Content {\n display: flex;\n min-width: 0;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--lea-spacing-4);\n}\n\n/* A line between two logos, 32px from each. It hangs after a logo, so when\n the row wraps on a phone it ends the first row, as in Figma, rather than\n opening the next. */\n.Logos {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--lea-spacing-2) var(--lea-spacing-4);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.LogoItem {\n display: flex;\n align-items: center;\n gap: var(--lea-spacing-4);\n}\n\n.LogoItem:not(:last-child)::after {\n content: \"\";\n flex: none;\n width: 1px;\n height: var(--lea-spacing-5);\n background-color: var(--lea-palette-border-base);\n}\n\n.Logo {\n display: block;\n width: auto;\n max-width: 100%;\n height: auto;\n max-height: var(--lea-spacing-6);\n}\n\n.Text {\n display: flex;\n flex-direction: column;\n gap: var(--lea-spacing-4);\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.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.Description > * {\n margin: 0;\n}\n\n.Description > * + * {\n margin-block-start: var(--lea-spacing-1);\n}\n\n.Actions {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--lea-spacing-2);\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.Media {\n display: flex;\n min-width: 0;\n}\n\n.PaymentFeature[data-image-position=\"start\"] .Media {\n order: -1;\n}\n\n.Image {\n display: block;\n width: 100%;\n /* The website's max-width: 435px. */\n max-width: calc(var(--lea-spacing) * 54.375);\n height: auto;\n}\n\n@media (min-width: 1024px) {\n .PaymentFeature[data-has-image] .Grid {\n /* The website's minmax(auto, 560px): the text column stops at 560px. */\n grid-template-columns: minmax(0, calc(var(--lea-spacing) * 70)) minmax(\n 0,\n 1fr\n );\n }\n\n .PaymentFeature[data-image-position=\"start\"][data-has-image] .Grid {\n grid-template-columns: minmax(0, 1fr) minmax(\n 0,\n calc(var(--lea-spacing) * 70)\n );\n }\n\n .Grid,\n .Content {\n gap: var(--lea-spacing-8);\n }\n\n .Media {\n justify-content: flex-end;\n }\n}\n\n@media (max-width: 767.95px) {\n .Logo {\n max-height: var(--lea-spacing-4);\n }\n\n .LogoItem:not(:last-child)::after {\n height: var(--lea-spacing-4);\n }\n\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 type { ReactElement, ReactNode } from \"react\";\n\nimport { Panel, type PanelProps } from \"../../index\";\nimport { Paragraphs } from \"../paragraphs\";\nimport styles from \"./payment-feature.module.css\";\n\nexport type PaymentFeatureImagePosition = \"start\" | \"end\";\n\n/** A logo in the row at the top. */\nexport type PaymentFeatureLogo = {\n /** The logo file. */\n src: string;\n /** The name of the brand, for screen readers. */\n alt: string;\n /**\n * The image element to render instead of the plain `<img>`, such as\n * `next/image` with the logo's own `width` and `height`.\n */\n imageRender?: ReactElement;\n};\n\n/** `alt` is required with a photo, `\"\"` when it is decoration. */\ntype PaymentFeatureImage =\n | {\n /** The photo beside the text. Without one, the text takes the card. */\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`. Its own props win over `src` and `alt`.\n */\n imageRender?: ReactElement;\n }\n | { src?: undefined; alt?: undefined; imageRender?: undefined };\n\nexport type PaymentFeatureProps = Omit<\n PanelProps,\n \"title\" | \"children\" | \"size\"\n> &\n PaymentFeatureImage & {\n /** The logos of the payment brands, in a row over the text. */\n logos?: readonly PaymentFeatureLogo[];\n /** The title. */\n title: ReactNode;\n /** The element for the title, an `<h2>` by default: `titleRender={<h3 />}`. */\n titleRender?: ReactElement;\n /**\n * The text under the title. A string becomes one paragraph; pass `<p>`\n * elements for several.\n */\n text?: ReactNode;\n /**\n * The calls to action, in a row that wraps: an `ActionButton`, a `Link`\n * with `arrow`, or both.\n */\n actions?: ReactNode;\n /** The price example under the actions, in the small print. */\n priceExample?: ReactNode;\n /**\n * Where the photo sits: `\"start\"` before the text (on the left, or on\n * top below 1024px), `\"end\"` after it.\n * @default \"end\"\n */\n imagePosition?: PaymentFeatureImagePosition;\n };\n\n/**\n * A payment feature on a white `Panel`: a row of brand logos, a title, a\n * text and the calls to action in one column, and a photo in the other.\n *\n * ```tsx\n * <Section surface=\"subtle\">\n * <PaymentFeature\n * logos={[{ src: \"/logoer/vipps.svg\", alt: \"Vipps\" }, { src: \"/logoer/visa.svg\", alt: \"Visa\" }]}\n * title=\"Betal med Lea Bank i nettbutikken\"\n * text=\"Del opp betalingen eller betal senere når du handler hos våre partnere.\"\n * actions={<ActionButton render={<NextLink href=\"/betaling\" />}>Les mer</ActionButton>}\n * src=\"/bilder/betaling.jpg\"\n * alt=\"\"\n * />\n * </Section>\n * ```\n *\n * From 1024px the text column is at most 560px and the photo, at most 435px\n * wide, sits at the end of the other column; below 1024px they stack 32px\n * apart. The logos are up to 48px tall (32px below 768px) with a line between\n * them, and wrap on a narrow screen. Put it on a coloured section: the panel\n * is white.\n */\nexport function PaymentFeature({\n logos,\n title,\n titleRender,\n text,\n actions,\n priceExample,\n src,\n alt,\n imageRender,\n imagePosition = \"end\",\n className,\n ...rest\n}: PaymentFeatureProps) {\n const titleElement = useRender({\n render: titleRender,\n defaultTagName: \"h2\",\n props: { className: styles.Title, 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 (\n <Panel\n {...rest}\n className={[styles.PaymentFeature, className].filter(Boolean).join(\" \")}\n data-image-position={imagePosition}\n data-has-image={image != null || undefined}\n >\n <div className={styles.Grid}>\n <div className={styles.Content}>\n {logos != null && logos.length > 0 && (\n <ul className={styles.Logos}>\n {logos.map((logo, index) => (\n <PaymentFeatureLogoItem key={index} {...logo} />\n ))}\n </ul>\n )}\n <div className={styles.Text}>\n {titleElement}\n {text != null && (\n <Paragraphs className={styles.Description}>{text}</Paragraphs>\n )}\n </div>\n {actions != null && <div className={styles.Actions}>{actions}</div>}\n {priceExample != null && (\n <p className={styles.Price}>{priceExample}</p>\n )}\n </div>\n {image != null && <div className={styles.Media}>{image}</div>}\n </div>\n </Panel>\n );\n}\n\nfunction PaymentFeatureLogoItem({ src, alt, imageRender }: PaymentFeatureLogo) {\n const image = useRender({\n render: imageRender,\n defaultTagName: \"img\",\n props: { src, alt, className: styles.Logo },\n });\n\n return <li className={styles.LogoItem}>{image}</li>;\n}\n","/*\n * The website's FlexiblePromoBlock. From 1024px two equal columns 600px tall\n * and 16px apart: a `FeaturedImage` filling one, and in the other one or two\n * photos (`huge` corners) over a beige block (32px inside, `huge` corners)\n * with the title on `title-xl` in capitals and the subtitle on `body-lg`, 8px\n * apart. The photos take the room the block leaves. Below 1024px everything\n * stacks 16px apart, the photos at 1 : 0.5 and the title on `title-md`; the\n * two smaller photos stack below 768px.\n */\n.PromoGrid {\n display: grid;\n min-width: 0;\n grid-template-columns: minmax(0, 1fr);\n gap: var(--lea-spacing-2);\n}\n\n/* Two classes, to win over FeaturedImage's own 1 : 0.7 whichever stylesheet\n loads last. */\n.PromoGrid .Featured {\n aspect-ratio: 1 / 0.5;\n}\n\n.Content {\n display: flex;\n min-width: 0;\n min-height: 0;\n flex-direction: column;\n gap: var(--lea-spacing-2);\n}\n\n.Photos {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: var(--lea-spacing-2);\n}\n\n.Photos[data-count=\"1\"] {\n grid-template-columns: minmax(0, 1fr);\n}\n\n.Photo {\n display: block;\n width: 100%;\n aspect-ratio: 1 / 0.5;\n border-radius: var(--lea-radius-huge);\n /* Behind the photo while it loads. */\n background-color: var(--lea-palette-bg-muted);\n object-fit: cover;\n}\n\n.Block {\n display: flex;\n box-sizing: border-box;\n flex-direction: column;\n justify-content: center;\n gap: var(--lea-spacing-1);\n padding: var(--lea-spacing-4);\n border-radius: var(--lea-radius-huge);\n background-color: var(--lea-palette-bg-muted);\n color: var(--lea-palette-text-base);\n}\n\n.Title {\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 text-transform: uppercase;\n overflow-wrap: break-word;\n}\n\n.Subtitle {\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.Subtitle > * {\n margin: 0;\n}\n\n.Subtitle > * + * {\n margin-block-start: var(--lea-spacing-1);\n}\n\n@media (min-width: 1024px) {\n .PromoGrid {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n grid-template-rows: minmax(0, 1fr);\n /* The website's height: 600px. */\n height: calc(var(--lea-spacing) * 75);\n }\n\n .PromoGrid .Featured {\n aspect-ratio: auto;\n }\n\n .PromoGrid[data-image-position=\"end\"] .Featured {\n order: 1;\n }\n\n .Photos {\n flex: 1 1 0;\n min-height: 0;\n grid-template-rows: minmax(0, 1fr);\n }\n\n .Photo {\n height: 100%;\n aspect-ratio: auto;\n }\n\n /* Without photos the block takes the whole column. */\n .Block:only-child {\n flex: 1 1 auto;\n }\n\n .Title {\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\n@media (max-width: 767.95px) {\n .Photos {\n grid-template-columns: minmax(0, 1fr);\n }\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport type { ReactElement, ReactNode, Ref } from \"react\";\n\nimport { FeaturedImage } from \"../../index\";\nimport { mergeClassName } from \"../../utils/merge-class-name\";\nimport { Paragraphs } from \"../paragraphs\";\nimport styles from \"./promo-grid.module.css\";\n\nexport type PromoGridImagePosition = \"start\" | \"end\";\n\n/** One of the smaller photos over the text. */\nexport type PromoGridImage = {\n /** The photo. */\n src: string;\n /** What the photo shows, for screen readers; `\"\"` when it is decoration. */\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\nexport type PromoGridProps = Omit<\n useRender.ComponentProps<\"div\">,\n \"ref\" | \"title\" | \"children\"\n> & {\n /** The rendered element: the `<div>`, or the element passed to `render`. */\n ref?: Ref<HTMLElement>;\n /** The large photo. */\n src: string;\n /** What the large photo shows, for screen readers; `\"\"` when it is decoration. */\n alt: string;\n /**\n * The image element to render for the large photo instead of the plain\n * `<img>`, as on `FeaturedImage`.\n */\n imageRender?: ReactElement;\n /** A call to action on the large photo: an `ActionButton`. */\n action?: ReactNode;\n /** One or two smaller photos over the text, side by side. */\n images?:\n | readonly [PromoGridImage]\n | readonly [PromoGridImage, PromoGridImage];\n /** The title on the beige block, in capitals. */\n title: ReactNode;\n /** The element for the title, an `<h2>` by default: `titleRender={<h3 />}`. */\n titleRender?: ReactElement;\n /**\n * The text under the title. A string becomes one paragraph; pass `<p>`\n * elements for several.\n */\n subtitle?: ReactNode;\n /**\n * Where the large photo sits from 1024px: `\"start\"` (on the left) or\n * `\"end\"`. Below 1024px it is always on top.\n * @default \"start\"\n */\n imagePosition?: PromoGridImagePosition;\n};\n\n/**\n * A promotion of photos and a title: a large `FeaturedImage` on one side, and\n * on the other one or two photos over a beige block with the title in\n * capitals, as on leabank.no's about page.\n *\n * ```tsx\n * <Section>\n * <PromoGrid\n * src=\"/bilder/kontor.jpg\"\n * alt=\"\"\n * action={<ActionButton render={<NextLink href=\"/lan\" />}>Se våre lån</ActionButton>}\n * images={[{ src: \"/bilder/team.jpg\", alt: \"\" }]}\n * title=\"Lån til det du trenger\"\n * subtitle=\"Vi hjelper deg med lån uten sikkerhet til det du har lyst på.\"\n * />\n * </Section>\n * ```\n *\n * From 1024px two equal columns 600px tall, 16px apart, the smaller photos\n * taking the room the block leaves. Below 1024px everything stacks, the\n * photos at 1 : 0.5, the two smaller ones side by side down to 768px. The\n * block is beige with dark text on any section.\n */\nexport function PromoGrid({\n src,\n alt,\n imageRender,\n action,\n images,\n title,\n titleRender,\n subtitle,\n imagePosition = \"start\",\n render,\n ref,\n className,\n ...rest\n}: PromoGridProps) {\n const titleElement = useRender({\n render: titleRender,\n defaultTagName: \"h2\",\n props: { className: styles.Title, children: title },\n });\n\n return useRender({\n render,\n ref,\n defaultTagName: \"div\",\n props: {\n ...rest,\n \"data-image-position\": imagePosition,\n className: mergeClassName(styles.PromoGrid, className)({}),\n children: (\n <>\n <FeaturedImage\n className={styles.Featured}\n src={src}\n alt={alt}\n imageRender={imageRender}\n action={action}\n />\n <div className={styles.Content}>\n {images != null && images.length > 0 && (\n <div className={styles.Photos} data-count={images.length}>\n {images.map((image, index) => (\n <PromoGridPhoto key={index} {...image} />\n ))}\n </div>\n )}\n <div className={styles.Block}>\n {titleElement}\n {subtitle != null && (\n <Paragraphs className={styles.Subtitle}>{subtitle}</Paragraphs>\n )}\n </div>\n </div>\n </>\n ),\n },\n });\n}\n\nfunction PromoGridPhoto({ src, alt, imageRender }: PromoGridImage) {\n return useRender({\n render: imageRender,\n defaultTagName: \"img\",\n props: { src, alt, className: styles.Photo },\n });\n}\n","/*\n * The website's SectionContainer: a band in one of the five section colours,\n * 112px or 64px above and below (64 / 32 below 768px), 64px at the sides (16\n * below 768px), and the content in a column of at most `--lea-width-section`\n * (1312px), centred. Wide and narrow content is 75 % and 60 % of that column,\n * centred, from 1024px.\n *\n * `--section-bg` is inherited by what is inside: `Carousel` fades its edges\n * into it.\n */\n.Section {\n --section-bg: var(--lea-palette-bg-base);\n --section-text: var(--lea-palette-text-base);\n /* 112px, the website's `huge`: off the spacing scale. */\n --section-space-lg: calc(var(--lea-spacing) * 14);\n --section-space-sm: var(--lea-spacing-8);\n --section-side: var(--lea-spacing-8);\n\n box-sizing: border-box;\n width: 100%;\n padding-inline: var(--section-side);\n background-color: var(--section-bg);\n color: var(--section-text);\n}\n\n.Section[data-surface=\"subtle\"] {\n --section-bg: var(--lea-palette-bg-subtle);\n}\n\n.Section[data-surface=\"muted\"] {\n --section-bg: var(--lea-palette-bg-muted);\n}\n\n.Section[data-surface=\"contrast\"] {\n --section-bg: var(--lea-palette-bg-contrast);\n --section-text: var(--lea-palette-text-contrast);\n}\n\n.Section[data-surface=\"contrast-accent\"] {\n --section-bg: var(--lea-palette-bg-contrast-accent);\n --section-text: var(--lea-palette-text-contrast);\n}\n\n.Section[data-spacing-top=\"lg\"] {\n padding-block-start: var(--section-space-lg);\n}\n\n.Section[data-spacing-top=\"sm\"] {\n padding-block-start: var(--section-space-sm);\n}\n\n.Section[data-spacing-bottom=\"lg\"] {\n padding-block-end: var(--section-space-lg);\n}\n\n.Section[data-spacing-bottom=\"sm\"] {\n padding-block-end: var(--section-space-sm);\n}\n\n.Content {\n box-sizing: border-box;\n width: 100%;\n max-width: var(--lea-width-section);\n margin-inline: auto;\n}\n\n@media (min-width: 1024px) {\n .Content[data-width=\"wide\"] {\n max-width: calc(var(--lea-width-section) * 0.75);\n width: 75%;\n }\n\n .Content[data-width=\"narrow\"] {\n max-width: calc(var(--lea-width-section) * 0.6);\n width: 60%;\n }\n}\n\n@media (max-width: 767.95px) {\n .Section {\n --section-space-lg: var(--lea-spacing-8);\n --section-space-sm: var(--lea-spacing-4);\n --section-side: var(--lea-spacing-2);\n }\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport type { Ref } from \"react\";\n\nimport type { SurfaceRoles } from \"../../components/color-roles\";\nimport { mergeClassName } from \"../../utils/merge-class-name\";\nimport styles from \"./section.module.css\";\n\nexport type SectionSurface = SurfaceRoles<\n \"base\" | \"subtle\" | \"muted\" | \"contrast\" | \"contrast-accent\"\n>;\nexport type SectionSpacing = \"none\" | \"sm\" | \"lg\";\nexport type SectionWidth = \"full\" | \"wide\" | \"narrow\";\n\nexport type SectionProps = Omit<useRender.ComponentProps<\"section\">, \"ref\"> & {\n /** The rendered element: the `<section>`, or the element passed to `render`. */\n ref?: Ref<HTMLElement>;\n /**\n * The colour of the band, 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 is white;\n * give a `Link` inside `color=\"contrast\"` there.\n * @default \"base\"\n */\n surface?: SectionSurface;\n /**\n * The room above the content: `\"lg\"` 112px (64px below 768px), `\"sm\"` 64px\n * (32px), `\"none\"`.\n * @default \"lg\"\n */\n spacingTop?: SectionSpacing;\n /**\n * The room below the content, in the same steps as `spacingTop`.\n * @default \"lg\"\n */\n spacingBottom?: SectionSpacing;\n /**\n * How much of the content column the content takes, centred: `\"full\"`,\n * `\"wide\"` (75 %) or `\"narrow\"` (60 %). Below 1024px it is always the full\n * column.\n * @default \"full\"\n */\n width?: SectionWidth;\n};\n\n/**\n * A band of a website page: the colour from edge to edge, the content in a\n * column of at most 1312px in the middle, 64px from the sides (16px below\n * 768px). Every block on leabank.no sits in one.\n *\n * ```tsx\n * <Section surface=\"subtle\" spacingTop=\"none\">\n * <SectionHeader title=\"Vi gjør det enkelt å ta smarte økonomiske valg\" />\n * </Section>\n * ```\n *\n * The section sets the text colour for what is inside it, white on the dark\n * surfaces, so text, a vertical `MediaCard` and the sections that have no\n * colour of their own follow it. Renders a `section`; pass `render` for\n * another element.\n */\nexport function Section({\n surface = \"base\",\n spacingTop = \"lg\",\n spacingBottom = \"lg\",\n width = \"full\",\n render,\n ref,\n className,\n children,\n ...rest\n}: SectionProps) {\n return useRender({\n render,\n ref,\n defaultTagName: \"section\",\n props: {\n ...rest,\n \"data-surface\": surface,\n \"data-spacing-top\": spacingTop,\n \"data-spacing-bottom\": spacingBottom,\n className: mergeClassName(styles.Section, className)({}),\n children: (\n <div className={styles.Content} data-width={width}>\n {children}\n </div>\n ),\n },\n });\n}\n","/*\n * The website's SimpleHeaderBlock: everything centred, the text in a column\n * 16px apart (the eyebrow `heading-xs`, the title `heading-xl` or\n * `heading-md`, the subtitle `body-lg` at most 720px wide), the actions 32px\n * under it in a row that wraps, and the price example 32px under that. No\n * colour of its own: the text is the section's.\n */\n.SectionHeader {\n display: flex;\n min-width: 0;\n flex-direction: column;\n align-items: center;\n gap: var(--lea-spacing-4);\n text-align: center;\n}\n\n.Text {\n display: flex;\n max-width: 100%;\n flex-direction: column;\n align-items: center;\n gap: var(--lea-spacing-2);\n}\n\n.Eyebrow {\n margin: 0;\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.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.SectionHeader[data-title-size=\"sm\"] .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.Subtitle {\n /* The website's max-width: 720px. */\n max-width: calc(var(--lea-spacing) * 90);\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.Subtitle > * {\n margin: 0;\n}\n\n.Subtitle > * + * {\n margin-block-start: var(--lea-spacing-1);\n}\n\n.Actions {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: center;\n gap: var(--lea-spacing-4);\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@media (max-width: 767.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 type { ReactElement, ReactNode, Ref } from \"react\";\n\nimport { mergeClassName } from \"../../utils/merge-class-name\";\nimport { Paragraphs } from \"../paragraphs\";\nimport styles from \"./section-header.module.css\";\n\nexport type SectionHeaderTitleSize = \"sm\" | \"lg\";\n\nexport type SectionHeaderProps = Omit<\n useRender.ComponentProps<\"div\">,\n \"ref\" | \"title\" | \"children\"\n> & {\n /** The rendered element: the `<div>`, or the element passed to `render`. */\n ref?: Ref<HTMLElement>;\n /** The short line above the title. */\n eyebrow?: ReactNode;\n /** The title of the section. */\n title: ReactNode;\n /**\n * The element for the title, an `<h2>` by default. Pass another level where\n * the page outline needs one: `titleRender={<h1 />}`.\n */\n titleRender?: ReactElement;\n /**\n * `\"lg\"`: 41px (26px below 768px). `\"sm\"`: 26px at every width.\n * @default \"lg\"\n */\n titleSize?: SectionHeaderTitleSize;\n /**\n * The text under the title, at most 720px wide. A string becomes one\n * paragraph; pass `<p>` elements for several.\n */\n subtitle?: ReactNode;\n /**\n * The calls to action, in a centred row that wraps: an `ActionButton`,\n * a `Link` with `arrow`, or both. The row is only there when this is.\n */\n actions?: ReactNode;\n /** The price example under the actions, in the small print. */\n priceExample?: ReactNode;\n};\n\n/**\n * The centred heading of a website section: a short line, the title, a text\n * of at most 720px and a row of calls to action, as at the top of the\n * sections on leabank.no.\n *\n * ```tsx\n * <Section>\n * <SectionHeader\n * eyebrow=\"Om Lea Bank\"\n * title=\"Vi gjør det enkelt å ta smarte økonomiske valg\"\n * subtitle=\"Lea Bank er en digital bank …\"\n * actions={<ActionButton render={<NextLink href=\"/om-oss\" />}>Les mer om oss</ActionButton>}\n * />\n * </Section>\n * ```\n *\n * It takes the text colour of its section. The parts are 16px apart and the\n * actions 32px under them; the actions are 32px apart.\n */\nexport function SectionHeader({\n eyebrow,\n title,\n titleRender,\n titleSize = \"lg\",\n subtitle,\n actions,\n priceExample,\n render,\n ref,\n className,\n ...rest\n}: SectionHeaderProps) {\n const titleElement = useRender({\n render: titleRender,\n defaultTagName: \"h2\",\n props: { className: styles.Title, children: title },\n });\n\n return useRender({\n render,\n ref,\n defaultTagName: \"div\",\n props: {\n ...rest,\n \"data-title-size\": titleSize,\n className: mergeClassName(styles.SectionHeader, className)({}),\n children: (\n <>\n <div className={styles.Text}>\n {eyebrow != null && <p className={styles.Eyebrow}>{eyebrow}</p>}\n {titleElement}\n {subtitle != null && (\n <Paragraphs className={styles.Subtitle}>{subtitle}</Paragraphs>\n )}\n </div>\n {actions != null && <div className={styles.Actions}>{actions}</div>}\n {priceExample != null && (\n <p className={styles.Price}>{priceExample}</p>\n )}\n </>\n ),\n },\n });\n}\n","/*\n * The website's ServiceCardsBlock (Display = Grid): cards 16px apart. From\n * 1024px three columns with rows of 280, 280 and 320px: the first card takes\n * two columns and two rows (576px tall), the second the third column of the\n * same rows; a third card the whole row under them, or with a fourth, the\n * first column of it (the fourth the other two). Below 1024px one column:\n * a card with a photo 440px tall, one without as tall as its text.\n */\n.ServiceCardGrid {\n min-width: 0;\n}\n\n.List {\n display: grid;\n grid-template-columns: minmax(0, 1fr);\n gap: var(--lea-spacing-2);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n/* The card fills its cell, and its photo shrinks to fit it. */\n.Item {\n display: grid;\n min-width: 0;\n min-height: 0;\n grid-template-columns: minmax(0, 1fr);\n grid-template-rows: minmax(0, 1fr);\n}\n\n@media (max-width: 1023.95px) {\n /* A card with a photo (an <img> straight inside the card) is 440px tall,\n the photo taking what the text leaves, as the website's\n minmax(auto, 440px) rows; a card without one is as tall as its text. */\n .Item:has(> * > img) {\n height: calc(var(--lea-spacing) * 55);\n }\n}\n\n@media (min-width: 1024px) {\n .List {\n grid-template-columns: repeat(3, minmax(0, 1fr));\n /* The website's rows: 280 + 280 for the first two cards, 320 for the\n row under them. Fixed, so a photo's own height never sets them: the\n photo takes what the text leaves. */\n grid-template-rows: repeat(2, calc(var(--lea-spacing) * 35));\n grid-auto-rows: calc(var(--lea-spacing) * 40);\n }\n\n .Item:nth-child(1) {\n grid-column: 1 / 3;\n grid-row: 1 / 3;\n }\n\n .Item:nth-child(2) {\n grid-column: 3 / 4;\n grid-row: 1 / 3;\n }\n\n .List[data-count=\"3\"] > .Item:nth-child(3) {\n grid-column: 1 / 4;\n }\n\n .List[data-count=\"4\"] > .Item:nth-child(3) {\n grid-column: 1 / 2;\n }\n\n .List[data-count=\"4\"] > .Item:nth-child(4) {\n grid-column: 2 / 4;\n }\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { Children, type ReactNode, type Ref } from \"react\";\n\nimport { mergeClassName } from \"../../utils/merge-class-name\";\nimport styles from \"./service-card-grid.module.css\";\n\nexport type ServiceCardGridProps = 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 * The cards: one to four `ServiceCard`s, in the order of the website's\n * grid. The first two are `layout=\"stacked\"` (the default); give the third\n * and fourth `layout=\"row\"`. Each one is put in an item of a list.\n */\n children?: ReactNode;\n};\n\n/**\n * The website's grid of service cards, as on leabank.no/lan: a large card\n * and a narrow one side by side, then a wide row under them.\n *\n * ```tsx\n * <Section surface=\"muted\">\n * <ServiceCardGrid>\n * <ServiceCard render={<NextLink href=\"/lan/forbrukslan\" />} title=\"Forbrukslån\" … />\n * <ServiceCard render={<NextLink href=\"/lan/refinansiering\" />} title=\"Refinansiering\" … />\n * <ServiceCard render={<NextLink href=\"/sparing\" />} layout=\"row\" title=\"Sparing\" … />\n * <ServiceCard render={<NextLink href=\"/betalingsforsikring\" />} layout=\"row\" title=\"Betalingsforsikring\" … />\n * </ServiceCardGrid>\n * </Section>\n * ```\n *\n * From 1024px three columns: the first card two columns wide and the second\n * one, both 576px tall; a third card takes the row under them (320px) on its\n * own, or the first column of it with a fourth in the other two. Below\n * 1024px one column, each card at most 440px tall. The cards are a list.\n */\nexport function ServiceCardGrid({\n render,\n ref,\n className,\n children,\n ...rest\n}: ServiceCardGridProps) {\n const cards = Children.toArray(children);\n\n return useRender({\n render,\n ref,\n defaultTagName: \"div\",\n props: {\n ...rest,\n className: mergeClassName(styles.ServiceCardGrid, className)({}),\n children: (\n <ul className={styles.List} data-count={cards.length}>\n {cards.map((card, index) => (\n <li key={index} className={styles.Item}>\n {card}\n </li>\n ))}\n </ul>\n ),\n },\n });\n}\n","/*\n * Figma's `.ArrowRevealLink` at `md`, the website's `DropdownLink`: 16px\n * text 4px in from the start, and a purple arrow that grows from nothing to\n * 24px on hover while the text turns purple and moves 4px further in. The\n * arrow's room is its width, so the line keeps its height.\n */\n.ArrowRevealLink {\n display: inline-flex;\n max-width: 100%;\n align-items: center;\n gap: var(--lea-spacing-0-5);\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 letter-spacing: var(--lea-text-body-md-letter-spacing);\n line-height: var(--lea-text-body-md-line-height);\n text-decoration: none;\n}\n\n.Arrow {\n display: flex;\n overflow: hidden;\n width: 0;\n height: var(--lea-spacing-3);\n flex-shrink: 0;\n align-items: center;\n color: var(--lea-palette-primary-base);\n opacity: 0;\n transition:\n width var(--lea-duration-normal) var(--lea-easing-standard),\n opacity var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n.Arrow svg {\n width: var(--lea-spacing-3);\n height: var(--lea-spacing-3);\n flex-shrink: 0;\n}\n\n.Text {\n min-width: 0;\n transition:\n padding-inline-start var(--lea-duration-normal) var(--lea-easing-standard),\n color var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n@media (hover: hover) {\n .ArrowRevealLink:not([data-size]):hover .Arrow {\n width: var(--lea-spacing-3);\n opacity: 1;\n }\n\n .ArrowRevealLink:not([data-size]):hover .Text {\n padding-inline-start: var(--lea-spacing-0-5);\n color: var(--lea-palette-primary-base);\n }\n}\n\n/*\n * `lg`, the website's `SidebarLink`: 18px text from the start; the marked\n * link (`data-marked`, the current page) has the 24px arrow before it (4px\n * apart), and the others none. When the mark moves to another link, its\n * arrow grows in while the old one shrinks away, as the `md` arrow does on\n * hover. The arrow sits on the first line, so a long label wraps under its\n * text. Hover underlines the text: no movement.\n */\n.ArrowRevealLink[data-size=\"lg\"] {\n align-items: flex-start;\n /* The arrow's room holds the 4px to the text: an unmarked link starts at the edge. */\n gap: 0;\n font-size: var(--lea-text-body-lg-font-size);\n font-weight: var(--lea-text-body-lg-font-weight);\n letter-spacing: var(--lea-text-body-lg-letter-spacing);\n line-height: var(--lea-text-body-lg-line-height);\n}\n\n.ArrowRevealLink[data-size=\"lg\"] .Arrow {\n height: calc(var(--lea-text-body-lg-line-height) * 1em);\n}\n\n.ArrowRevealLink[data-size=\"lg\"][data-marked] .Arrow {\n width: calc(var(--lea-spacing-3) + var(--lea-spacing-0-5));\n opacity: 1;\n}\n\n@media (hover: hover) {\n .ArrowRevealLink[data-size=\"lg\"]:hover .Text {\n text-decoration: underline;\n }\n}\n\n.ArrowRevealLink:focus-visible {\n border-radius: var(--lea-radius-sm);\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Arrow,\n .Text {\n transition-duration: 0.01ms;\n }\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { ArrowRight } from \"lucide-react\";\n\nimport { mergeClassName } from \"../utils/merge-class-name\";\nimport styles from \"./arrow-reveal-link.module.css\";\n\nexport type ArrowRevealLinkProps = useRender.ComponentProps<\"a\"> & {\n /**\n * `\"md\"`: the header's menus, 16px, the arrow growing in on hover. `\"lg\"`:\n * the `SideNav`, 18px, underlined on hover, the arrow before the\n * `marked` link only.\n * @default \"md\"\n */\n size?: \"md\" | \"lg\";\n /**\n * `lg` only: the arrow shows before this link, and grows in when it is\n * set. `SideNav` marks the current page's link, and a link as soon as it\n * is clicked.\n */\n marked?: boolean;\n};\n\n/**\n * Internal: Figma's `.ArrowRevealLink`. At `md`, the links of the header's\n * menus: a plain 16px link whose purple arrow grows in on hover while the\n * text turns purple and steps aside, as the website's `DropdownLink`. At\n * `lg`, the links of `SideNav`: 18px, underlined on hover, and the arrow\n * marks the current page (`marked`), as the website's `SidebarLink`. Not\n * exported.\n *\n * Renders an `<a>`, or the element passed to `render`: a router link, or the\n * `NavigationMenu.Link` of the desktop menu, which renders the page's own\n * link in turn.\n */\nexport function ArrowRevealLink({\n render,\n className,\n children,\n size = \"md\",\n marked = false,\n ...rest\n}: ArrowRevealLinkProps) {\n return useRender({\n render,\n defaultTagName: \"a\",\n props: {\n ...rest,\n \"data-size\": size === \"md\" ? undefined : size,\n \"data-marked\": marked ? \"\" : undefined,\n className: mergeClassName(styles.ArrowRevealLink, className)({}),\n children: (\n <>\n <span aria-hidden className={styles.Arrow}>\n <ArrowRight />\n </span>\n <span className={styles.Text}>{children}</span>\n </>\n ),\n },\n });\n}\n","/*\n * The website's `TwoColumnLayout`: from 1024px a 280px sidebar (360 from\n * 1312px) 16 from the panel, both from the top; below 1024px one column, 32\n * apart.\n */\n.SideNav {\n display: grid;\n align-items: start;\n gap: var(--lea-spacing-4);\n grid-template-columns: minmax(0, 1fr);\n}\n\n@media (min-width: 1024px) {\n .SideNav {\n gap: var(--lea-spacing-2);\n /* 280px, the website's sidebar between 1024 and 1312px. */\n grid-template-columns: calc(var(--lea-spacing) * 35) minmax(0, 1fr);\n }\n}\n\n@media (min-width: 1312px) {\n .SideNav {\n /* 360px. */\n grid-template-columns: calc(var(--lea-spacing) * 45) minmax(0, 1fr);\n }\n}\n\n/* Beige, `5xl` corners, 32 inside; from 1024px it stays in view, 32 from the top. */\n.Sidebar {\n box-sizing: border-box;\n padding: var(--lea-spacing-4);\n border-radius: var(--lea-radius-5xl);\n background-color: var(--lea-palette-bg-muted);\n color: var(--lea-palette-text-base);\n}\n\n@media (min-width: 1024px) {\n .Sidebar {\n position: sticky;\n top: var(--lea-spacing-4);\n }\n}\n\n.List {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n padding: 0;\n margin: 0;\n gap: var(--lea-spacing-2);\n list-style: none;\n}\n\n/* A row is as tall as its link, with or without the arrow. */\n.List > li {\n display: flex;\n max-width: 100%;\n}\n\n/* The panel can hold a table wider than its column: it scrolls, not the page. */\n.SideNav .Content {\n min-width: 0;\n}\n","import { createTranslation } from \"../../index\";\n\n/**\n * The words of `SideNav`: the name of its navigation when the page gives\n * none, told apart from the header's \"Hovedmeny\".\n */\nexport const useSideNavLabels = createTranslation({\n navigation: {\n no: \"Sidemeny\",\n se: \"Sidomeny\",\n fi: \"Sivuvalikko\",\n es: \"Menú lateral\",\n },\n});\n","\"use client\";\n\nimport {\n type ComponentProps,\n type MouseEvent,\n type ReactNode,\n useEffect,\n useState,\n} from \"react\";\n\nimport { Panel } from \"../../index\";\nimport { ArrowRevealLink } from \"../arrow-reveal-link\";\nimport type { SiteHeaderLink } from \"../site-header/site-header\";\nimport styles from \"./side-nav.module.css\";\nimport { useSideNavLabels } from \"./use-side-nav-labels\";\n\n/**\n * A link in the side navigation: `{ label, href }`, or `{ label, render }`\n * with a router link carrying its own `href`. A link without either does not\n * compile.\n */\nexport type SideNavItem = SiteHeaderLink & {\n /**\n * This page: the link is marked `aria-current=\"page\"` and has the arrow\n * before it.\n */\n current?: boolean;\n};\n\nexport type SideNavProps = Omit<ComponentProps<\"div\">, \"children\"> & {\n /** The pages beside this one (the website's child pages), in order. */\n items: SideNavItem[];\n /**\n * The name of the navigation, read out with it: the part of the site it\n * covers (\"Investor Relations\"). Left out, \"Sidemeny\".\n */\n label?: string;\n /** The `Breadcrumbs` at the top of the content panel. */\n breadcrumbs?: ReactNode;\n /** The page's content, in the white panel. */\n children: ReactNode;\n};\n\n/**\n * The website's two-column page (`TwoColumnLayout`, the Investor Relations\n * pages of leabank.no): a beige sidebar of links to the pages of one part of\n * the site, beside a white panel with the breadcrumbs and the content. Put it\n * in a `Section` (grey, `spacingTop` and `spacingBottom` `sm`, as on the\n * website).\n *\n * ```tsx\n * <Section surface=\"subtle\" spacingTop=\"sm\" spacingBottom=\"sm\">\n * <SideNav\n * label=\"Investor Relations\"\n * items={[\n * { label: \"About Lea Bank\", render: <NextLink href=\"/om-oss/investor-relations/about-lea-bank\" />, current: true },\n * { label: \"Dividend policy\", render: <NextLink href=\"/om-oss/investor-relations/dividend-policy\" /> },\n * ]}\n * breadcrumbs={<Breadcrumbs items={crumbs} />}\n * >\n * <RichText>…</RichText>\n * </SideNav>\n * </Section>\n * ```\n *\n * From 1024px the sidebar is 280px wide (360 from 1312px), 16 from the\n * panel, and stays in view while the page scrolls (32 from the top); below\n * 1024px it is a block over the panel, 32 apart. The sidebar is a `nav`\n * named by `label`; the link to this page is marked `aria-current=\"page\"`\n * and has a purple arrow before it, the others underline on hover. A click\n * on another link moves the arrow there at once (it grows in over 0.2s),\n * before the next page arrives; `aria-current` stays on this page until\n * `items` says otherwise. The panel is a `Panel size=\"sm\"`: what is in it\n * stacks 16 apart.\n */\nexport function SideNav({\n items,\n label,\n breadcrumbs,\n children,\n className,\n ...rest\n}: SideNavProps) {\n const labels = useSideNavLabels();\n const currentIndex = items.findIndex((item) => item.current);\n\n // The link clicked on this page, which has the arrow until `current` moves.\n const [clicked, setClicked] = useState<{\n index: number;\n from: number;\n } | null>(null);\n if (clicked && clicked.from !== currentIndex) setClicked(null);\n const markedIndex = clicked?.index ?? currentIndex;\n\n // A page the browser brings back from its cache (the back button after a\n // full page load) still has the clicked link's arrow: return it.\n useEffect(() => {\n const reset = (event: PageTransitionEvent) => {\n if (event.persisted) setClicked(null);\n };\n window.addEventListener(\"pageshow\", reset);\n return () => window.removeEventListener(\"pageshow\", reset);\n }, []);\n\n return (\n <div\n {...rest}\n className={[styles.SideNav, className].filter(Boolean).join(\" \")}\n >\n <nav className={styles.Sidebar} aria-label={label ?? labels.navigation}>\n <ul className={styles.List}>\n {items.map((item, index) => (\n <li key={index}>\n <ArrowRevealLink\n size=\"lg\"\n href={item.href}\n render={item.render}\n aria-current={item.current ? \"page\" : undefined}\n marked={index === markedIndex}\n onClick={(event) => {\n if (opensHere(event))\n setClicked({ index, from: currentIndex });\n }}\n >\n {item.label}\n </ArrowRevealLink>\n </li>\n ))}\n </ul>\n </nav>\n <Panel size=\"sm\" className={styles.Content}>\n {breadcrumbs}\n {children}\n </Panel>\n </div>\n );\n}\n\n/**\n * Whether a click follows the link in this tab: not one that opens a new tab\n * or window (a modifier key, `target`, `download`), or one the page stopped.\n */\nfunction opensHere(event: MouseEvent<HTMLAnchorElement>) {\n const link = event.currentTarget;\n const target = link.getAttribute(\"target\");\n return (\n !event.defaultPrevented &&\n event.button === 0 &&\n !event.metaKey &&\n !event.ctrlKey &&\n !event.shiftKey &&\n !event.altKey &&\n (!target || target === \"_self\") &&\n !link.hasAttribute(\"download\")\n );\n}\n","import type { ComponentProps, ReactNode } from \"react\";\n\n/*\n * Internal to `SiteFooter`: the flags of the four markets, round, 20px, from\n * the website's `assets/icons/countries/*.svg` (also in Figma's\n * `.SiteFooter/Flag`). Their colours are the flags', not tokens. The\n * website's files clip to a 20px square by an id; every path already sits\n * inside the circle, so the clip is left out and two flags on a page cannot\n * clash. Decoration: the market's name is the text beside them.\n */\n\ntype FlagProps = Omit<ComponentProps<\"svg\">, \"children\">;\n\nconst circle =\n \"M10 20C15.5228 20 20 15.5228 20 10C20 4.47715 15.5228 0 10 0C4.47715 0 0 4.47715 0 10C0 15.5228 4.47715 20 10 20Z\";\n\n/** The Nordic cross, as Finland and Norway draw it. */\nconst cross =\n \"M19.9154 8.69625H7.82613H7.82609V0.238281C6.90195 0.443203 6.02582 0.775352 5.21738 1.21648V8.69617V8.69621H0.0846484C0.0289844 9.1232 0 9.55852 0 10.0006C0 10.4427 0.0289844 10.878 0.0846484 11.3049H5.21734H5.21738V18.7846C6.02582 19.2257 6.90195 19.558 7.82609 19.7629V11.305V11.305H19.9154C19.9709 10.878 20 10.4427 20 10.0006C20 9.55852 19.9709 9.1232 19.9154 8.69625V8.69625Z\";\n\nfunction Flag({ children, ...rest }: FlagProps & { children: ReactNode }) {\n return (\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n aria-hidden\n focusable=\"false\"\n {...rest}\n >\n {children}\n </svg>\n );\n}\n\nexport function FlagNorway(props: FlagProps) {\n return (\n <Flag {...props}>\n <path d={circle} fill=\"#F0F0F0\" />\n <path\n d=\"M0.34375 12.6094C0.922344 14.7562 2.19754 16.6169 3.9123 17.9345V12.6094H0.34375Z\"\n fill=\"#D80027\"\n />\n <path\n d=\"M9.12891 19.9626C9.41555 19.9873 9.70543 20.0007 9.99848 20.0007C14.6188 20.0007 18.5065 16.867 19.654 12.6094H9.12891V19.9626Z\"\n fill=\"#D80027\"\n />\n <path\n d=\"M19.654 7.39129C18.5065 3.13363 14.6188 0 9.99848 0C9.70543 0 9.41555 0.0133594 9.12891 0.0380469V7.39129H19.654Z\"\n fill=\"#D80027\"\n />\n <path\n d=\"M3.9123 2.06641C2.19754 3.38398 0.922344 5.24465 0.34375 7.39148H3.9123V2.06641Z\"\n fill=\"#D80027\"\n />\n <path d={cross} fill=\"#0052B4\" />\n </Flag>\n );\n}\n\nexport function FlagSweden(props: FlagProps) {\n return (\n <Flag {...props}>\n <path d={circle} fill=\"#FFDA44\" />\n <path\n d=\"M7.82816 8.69566H19.9174C19.2782 3.78914 15.0828 0 10.002 0C9.25539 0 8.52809 0.0825391 7.82812 0.237695V8.69566H7.82816Z\"\n fill=\"#0052B4\"\n />\n <path\n d=\"M5.21867 8.69848V1.21875C2.47281 2.71695 0.507148 5.46504 0.0859375 8.69852H5.21867V8.69848Z\"\n fill=\"#0052B4\"\n />\n <path\n d=\"M5.21863 11.3047H0.0859375C0.507148 14.5382 2.47281 17.2863 5.21867 18.7844L5.21863 11.3047Z\"\n fill=\"#0052B4\"\n />\n <path\n d=\"M7.82812 11.3047V19.7627C8.52809 19.9178 9.25539 20.0004 10.002 20.0004C15.0828 20.0004 19.2782 16.2112 19.9174 11.3047H7.82812V11.3047Z\"\n fill=\"#0052B4\"\n />\n </Flag>\n );\n}\n\nexport function FlagFinland(props: FlagProps) {\n return (\n <Flag {...props}>\n <path d={circle} fill=\"#F0F0F0\" />\n <path d={cross} fill=\"#0052B4\" />\n </Flag>\n );\n}\n\nexport function FlagSpain(props: FlagProps) {\n return (\n <Flag {...props}>\n <path\n d=\"M0 10.0002C0 11.2234 0.220039 12.3951 0.621992 13.4784L10 14.348L19.378 13.4784C19.78 12.3951 20 11.2234 20 10.0002C20 8.77695 19.78 7.60523 19.378 6.52191L10 5.65234L0.621992 6.52191C0.220039 7.60523 0 8.77695 0 10.0002H0Z\"\n fill=\"#FFDA44\"\n />\n <path\n d=\"M19.3771 6.52176C17.9642 2.71379 14.2987 0 9.9991 0C5.69945 0 2.03402 2.71379 0.621094 6.52176H19.3771Z\"\n fill=\"#D80027\"\n />\n <path\n d=\"M0.621094 13.4766C2.03402 17.2845 5.69945 19.9983 9.9991 19.9983C14.2987 19.9983 17.9642 17.2845 19.3771 13.4766H0.621094Z\"\n fill=\"#D80027\"\n />\n </Flag>\n );\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport type { ComponentProps, ReactElement } from \"react\";\n\nimport type {\n SiteHeaderGroup,\n SiteHeaderItem,\n} from \"./site-header/site-header\";\n\n/**\n * Internal to `SiteHeader`: a link to `href`, or the router link in\n * `render`, which carries its own.\n */\nexport function SiteLink({\n href,\n render,\n ...rest\n}: Omit<ComponentProps<\"a\">, \"href\"> & {\n href?: string;\n render?: ReactElement;\n}) {\n return useRender({\n render,\n defaultTagName: \"a\",\n props: href == null ? rest : { ...rest, href },\n });\n}\n\n/** Whether an item opens a menu: it has a group to show. */\nexport const hasMenu = (\n item: SiteHeaderItem,\n): item is Extract<SiteHeaderItem, { groups: SiteHeaderGroup[] }> =>\n item.groups != null && item.groups.length > 0;\n\n/** Whether an item has a page of its own to link to. */\nexport const hasPage = (item: SiteHeaderItem) =>\n item.href != null || item.render != null;\n","/*\n * The website's `Footer`: a near-black band, 80px above and below and 64 at\n * the sides (64 and 16 below 768px), its content at most 1312px wide in a\n * column 32 apart: the logo and the columns, a white line 32 off each side,\n * the cookie settings and the copyright. Everything in `text.contrast`.\n */\n.SiteFooter {\n box-sizing: border-box;\n padding-block: var(--lea-spacing-8);\n padding-inline: var(--lea-spacing-2);\n background-color: var(--lea-palette-bg-contrast);\n color: var(--lea-palette-text-contrast);\n}\n\n@media (min-width: 768px) {\n .SiteFooter {\n /* 80px, the website's `xxl`. */\n padding-block: calc(var(--lea-spacing) * 10);\n padding-inline: var(--lea-spacing-8);\n }\n}\n\n.Content {\n display: flex;\n max-width: var(--lea-width-section);\n flex-direction: column;\n margin-inline: auto;\n gap: var(--lea-spacing-4);\n}\n\n/* The logo and the columns: side by side from 1024px, 64 apart. */\n.Main {\n display: grid;\n align-items: start;\n gap: var(--lea-spacing-4);\n grid-template-columns: minmax(0, 1fr);\n}\n\n@media (min-width: 1024px) {\n .Main {\n gap: var(--lea-spacing-8);\n grid-template-columns: auto minmax(0, 1fr);\n }\n}\n\n.Home {\n display: inline-flex;\n justify-self: start;\n border-radius: var(--lea-radius-sm);\n color: inherit;\n}\n\n/* 140px, the website's logo. */\n.Home > :is(img, svg) {\n display: block;\n width: calc(var(--lea-spacing) * 17.5);\n height: auto;\n}\n\n/*\n * As many columns of at least 140px as fit, sharing the width (five of\n * 170.4 beside the logo at 1440px); two below 768px, one below 640px.\n */\n.Columns {\n display: grid;\n gap: var(--lea-spacing-4);\n grid-template-columns: minmax(0, 1fr);\n}\n\n@media (min-width: 640px) {\n .Columns {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n }\n}\n\n@media (min-width: 768px) {\n .Columns {\n gap: var(--lea-spacing-8);\n grid-template-columns: repeat(\n auto-fit,\n minmax(calc(var(--lea-spacing) * 17.5), 1fr)\n );\n }\n}\n\n.Column {\n display: flex;\n min-width: 0;\n flex-direction: column;\n gap: var(--lea-spacing-1);\n}\n\n/* 24px to what follows: 16 of its own and the column's 8. */\n.Title {\n margin: 0;\n margin-block-end: var(--lea-spacing-2);\n color: inherit;\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-text-heading-xs-line-height);\n}\n\n/*\n * 12px apart: with `body-sm`'s 21px lines that is the website's 33px from\n * link to link (its 14px text sits in 17px lines, 16 apart).\n */\n.Links,\n.Social {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n padding: 0;\n margin: 0;\n gap: var(--lea-spacing-1-5);\n list-style: none;\n}\n\n/* A row is as tall as its link, not the 16px line of the page's text. */\n.Links > li,\n.Social > li {\n display: flex;\n max-width: 100%;\n}\n\n/* The rows are as tall as their 24px icons, 16 apart. */\n.Social {\n gap: var(--lea-spacing-2);\n}\n\n.Link {\n border-radius: var(--lea-radius-sm);\n color: inherit;\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 text-decoration: none;\n}\n\n.Social .Link {\n display: inline-flex;\n align-items: center;\n gap: var(--lea-spacing-1);\n}\n\n.SocialIcon {\n display: flex;\n width: var(--lea-spacing-3);\n height: var(--lea-spacing-3);\n flex-shrink: 0;\n}\n\n.SocialIcon > :is(img, svg) {\n width: 100%;\n height: 100%;\n object-fit: contain;\n}\n\n/* 32px to the social links' title, as the website's. */\n.Market:not(:last-child) {\n margin-block-end: var(--lea-spacing-4);\n}\n\n.Divider {\n display: block;\n height: 1px;\n margin-block: var(--lea-spacing-4);\n background-color: var(--lea-palette-border-contrast);\n}\n\n/*\n * Centred, as the website's. At least 24px tall, the smallest target\n * (WCAG 2.5.8), where the text alone is 21.\n */\n.CookieSettings {\n min-height: var(--lea-spacing-3);\n align-self: center;\n padding: 0;\n border: 0;\n border-radius: var(--lea-radius-sm);\n background: none;\n color: inherit;\n cursor: pointer;\n font: inherit;\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.Copyright {\n margin: 0;\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.Copyright > p {\n margin: 0;\n}\n\n@media (hover: hover) {\n .Link:hover,\n .CookieSettings:hover {\n text-decoration: underline;\n }\n}\n\n/* White on the near-black band (`text.base` would not show). */\n.Home:focus-visible,\n.Link:focus-visible,\n.CookieSettings:focus-visible {\n outline: 2px solid var(--lea-palette-text-contrast);\n outline-offset: 0.125rem;\n}\n","import { createTranslation } from \"../../index\";\n\n/**\n * The words of `SiteFooter`: the titles of the market and social columns\n * when the page gives none, and the button that opens the cookie settings.\n * `collation` is the language the markets are sorted in, as a BCP 47 tag\n * for `Intl.Collator`. The link round the logo takes `SiteHeader`'s name.\n */\nexport const useSiteFooterLabels = createTranslation({\n market: {\n no: \"Marked\",\n se: \"Marknad\",\n fi: \"Markkina\",\n es: \"Mercado\",\n },\n social: {\n no: \"Sosiale medier\",\n se: \"Sociala medier\",\n fi: \"Sosiaalinen media\",\n es: \"Redes sociales\",\n },\n cookieSettings: {\n no: \"Cookie-innstillinger\",\n se: \"Cookieinställningar\",\n fi: \"Evästeasetukset\",\n es: \"Configuración de cookies\",\n },\n collation: { no: \"nb\", se: \"sv\", fi: \"fi\", es: \"es\" },\n});\n","import { createTranslation } from \"../index\";\n\n/**\n * The words of `SiteHeader`: the name of the navigation, the menu button on\n * a phone and the button that closes the menu, and the name of the link\n * around the logo. The link's name starts with the words in the logo, so\n * what a voice user reads out matches what they see.\n */\nexport const useSiteHeaderLabels = createTranslation({\n navigation: {\n no: \"Hovedmeny\",\n se: \"Huvudmeny\",\n fi: \"Päävalikko\",\n es: \"Menú principal\",\n },\n menu: {\n no: \"Meny\",\n se: \"Meny\",\n fi: \"Valikko\",\n es: \"Menú\",\n },\n closeMenu: {\n no: \"Lukk meny\",\n se: \"Stäng menyn\",\n fi: \"Sulje valikko\",\n es: \"Cerrar menú\",\n },\n home: {\n no: \"Lea Bank, forside\",\n se: \"Lea Bank, startsida\",\n fi: \"Lea Bank, etusivu\",\n es: \"Lea Bank, página de inicio\",\n },\n});\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport {\n useId,\n useMemo,\n type ComponentProps,\n type ReactElement,\n type ReactNode,\n} from \"react\";\n\nimport { Select } from \"../../index\";\nimport { FlagFinland, FlagNorway, FlagSpain, FlagSweden } from \"./flags\";\nimport { Paragraphs } from \"../paragraphs\";\nimport type { SiteHeaderLink } from \"../site-header/site-header\";\nimport { SiteLink } from \"../site-header-link\";\nimport styles from \"./site-footer.module.css\";\nimport { useSiteFooterLabels } from \"./use-site-footer-labels\";\nimport { useSiteHeaderLabels } from \"../use-site-header-labels\";\n\n/**\n * A link in the footer: `{ label, href }`, or `{ label, render }` with a\n * router link carrying its own `href`. A link without either does not\n * compile. A link that opens in a new tab is a `render` with `target`.\n */\nexport type SiteFooterLink = SiteHeaderLink;\n\n/** A column of links under a title (\"Tjenester\", \"Support\", \"Juridisk\"). */\nexport type SiteFooterColumn = {\n /** The title over the links, a heading (`titleRender`). */\n title: ReactNode;\n links: SiteFooterLink[];\n};\n\n/** A link to one of the bank's pages on a social network. */\nexport type SiteFooterSocialLink = SiteFooterLink & {\n /**\n * The network's logo, 24px: an `<img>` or `next/image` with `alt=\"\"`, as\n * the label names the link.\n */\n icon: ReactNode;\n};\n\n/** The four markets, by the codes of the languages they speak. */\nexport type SiteFooterMarketCode = \"no\" | \"se\" | \"fi\" | \"es\";\n\n/** The list of markets: the select that takes the visitor to another one. */\nexport type SiteFooterMarket = {\n /** The market of this site, shown with its flag. */\n value: SiteFooterMarketCode;\n /**\n * Called with the market the visitor picks. The website goes to that\n * market's site (`https://leabank.se/`).\n */\n onValueChange: (value: SiteFooterMarketCode) => void;\n /**\n * The markets to choose from, sorted by name in the page's language.\n * @default [\"no\", \"se\", \"fi\", \"es\"]\n */\n options?: readonly SiteFooterMarketCode[];\n /** The title over the select, which names it. Left out, \"Marked\". */\n title?: ReactNode;\n};\n\n/** The links to the bank on social networks, under a title. */\nexport type SiteFooterSocial = {\n /** The title over the links. Left out, \"Sosiale medier\". */\n title?: ReactNode;\n links: SiteFooterSocialLink[];\n};\n\nexport type SiteFooterProps = Omit<ComponentProps<\"footer\">, \"children\"> & {\n /**\n * The white logo, an `<img>` or `next/image`, 140px wide. It is the\n * content of the link to the start page, named \"Lea Bank, forside\" as in\n * `SiteHeader`, so the image can take `alt=\"\"`.\n */\n logo: ReactNode;\n /**\n * The link to the start page, a router link carrying its own `href`:\n * `<NextLink href=\"/\" />`.\n * @default <a href=\"/\" />\n */\n homeRender?: ReactElement;\n /** The columns of links, in order. */\n columns?: SiteFooterColumn[];\n /** The market select, at the top of the last column. */\n market?: SiteFooterMarket;\n /** The social links, at the foot of the last column. */\n social?: SiteFooterSocial;\n /**\n * Called by the button that opens the cookie settings, which shows only\n * with it: `openCookieSettings` from\n * `@leabankab/component-library/cookie-consent`, or the website's own.\n */\n onOpenCookieSettings?: () => void;\n /**\n * The line at the very end. A string becomes a `<p>`; anything else (the\n * CMS's paragraphs) goes in a `<div>`.\n */\n copyright?: ReactNode;\n /**\n * The element of every title in the footer.\n * @default <h2 />\n */\n titleRender?: ReactElement;\n};\n\nconst MARKETS: Record<SiteFooterMarketCode, { name: string; flag: ReactNode }> =\n {\n no: { name: \"Norge\", flag: <FlagNorway /> },\n se: { name: \"Sverige\", flag: <FlagSweden /> },\n fi: { name: \"Suomi\", flag: <FlagFinland /> },\n es: { name: \"España\", flag: <FlagSpain /> },\n };\n\nconst ALL_MARKETS: readonly SiteFooterMarketCode[] = [\"no\", \"se\", \"fi\", \"es\"];\n\nfunction Title({\n render,\n id,\n children,\n}: {\n render?: ReactElement;\n id?: string;\n children: ReactNode;\n}) {\n return useRender({\n render,\n defaultTagName: \"h2\",\n props: { id, className: styles.Title, children },\n });\n}\n\nfunction MarketSelect({\n market,\n render,\n}: {\n market: SiteFooterMarket;\n render?: ReactElement;\n}) {\n const labels = useSiteFooterLabels();\n const titleId = useId();\n const markets = market.options ?? ALL_MARKETS;\n\n const options = useMemo(() => {\n const collator = new Intl.Collator(labels.collation, {\n sensitivity: \"base\",\n });\n return [...markets]\n .sort((a, b) => collator.compare(MARKETS[a].name, MARKETS[b].name))\n .map((code) => ({\n value: code,\n label: MARKETS[code].name,\n icon: MARKETS[code].flag,\n }));\n }, [markets, labels.collation]);\n\n return (\n <>\n <Title render={render} id={titleId}>\n {market.title ?? labels.market}\n </Title>\n <Select\n size=\"sm\"\n aria-labelledby={titleId}\n options={options}\n value={market.value}\n onValueChange={market.onValueChange}\n className={styles.Market}\n />\n </>\n );\n}\n\n/**\n * The website's footer, as at the end of every page of leabank.no: the\n * white logo, the columns of links, the market select and the social links\n * on a near-black band, then a line, the cookie settings and the copyright.\n *\n * ```tsx\n * <SiteFooter\n * logo={<img src=\"/lea-bank-white.svg\" alt=\"\" />}\n * homeRender={<NextLink href=\"/\" />}\n * columns={[\n * {\n * title: \"Tjenester\",\n * links: [{ label: \"Lån\", render: <NextLink href=\"/lan\" /> }],\n * },\n * ]}\n * market={{ value: \"no\", onValueChange: (market) => location.assign(sites[market]) }}\n * social={{\n * links: [\n * {\n * label: \"Facebook\",\n * icon: <img src=\"/facebook.svg\" alt=\"\" />,\n * render: <a href=\"https://www.facebook.com/leabank\" target=\"_blank\" rel=\"noopener noreferrer\" />,\n * },\n * ],\n * }}\n * onOpenCookieSettings={openCookieSettings}\n * copyright=\"Copyright © 2005–2026 Lea Bank AB NUF (publ).\"\n * />\n * ```\n *\n * The columns share the width, as many 140px columns as fit beside the logo\n * from 1024px (five at 1440px), under it below; two columns below 768px and\n * one below 640px. The market select is Paloma's `Select` at `size=\"sm\"`,\n * each market with its flag, named by the title over it; picking a market\n * calls `onValueChange` and goes nowhere by itself. `market.onValueChange`\n * and `onOpenCookieSettings` are functions, so a server component renders\n * the footer through a client component of its own.\n */\nexport function SiteFooter({\n logo,\n homeRender,\n columns = [],\n market,\n social,\n onOpenCookieSettings,\n copyright,\n titleRender,\n className,\n ...rest\n}: SiteFooterProps) {\n const labels = useSiteFooterLabels();\n const headerLabels = useSiteHeaderLabels();\n const hasEnd = onOpenCookieSettings != null || copyright != null;\n\n return (\n <footer\n {...rest}\n className={[styles.SiteFooter, className].filter(Boolean).join(\" \")}\n >\n <div className={styles.Content}>\n <div className={styles.Main}>\n <SiteLink\n href={homeRender == null ? \"/\" : undefined}\n render={homeRender}\n className={styles.Home}\n aria-label={headerLabels.home}\n >\n {logo}\n </SiteLink>\n <div className={styles.Columns}>\n {columns.map((column, index) => (\n <div key={index} className={styles.Column}>\n <Title render={titleRender}>{column.title}</Title>\n {column.links.length > 0 && (\n <ul className={styles.Links}>\n {column.links.map((link, linkIndex) => (\n <li key={linkIndex}>\n <SiteLink\n href={link.href}\n render={link.render}\n className={styles.Link}\n >\n {link.label}\n </SiteLink>\n </li>\n ))}\n </ul>\n )}\n </div>\n ))}\n {(market != null || social != null) && (\n <div className={styles.Column}>\n {market != null && (\n <MarketSelect market={market} render={titleRender} />\n )}\n {social != null && (\n <>\n <Title render={titleRender}>\n {social.title ?? labels.social}\n </Title>\n <ul className={styles.Social}>\n {social.links.map((link, index) => (\n <li key={index}>\n <SiteLink\n href={link.href}\n render={link.render}\n className={styles.Link}\n >\n <span aria-hidden className={styles.SocialIcon}>\n {link.icon}\n </span>\n {link.label}\n </SiteLink>\n </li>\n ))}\n </ul>\n </>\n )}\n </div>\n )}\n </div>\n </div>\n {hasEnd && (\n <>\n <span aria-hidden className={styles.Divider} />\n {onOpenCookieSettings != null && (\n <button\n type=\"button\"\n className={styles.CookieSettings}\n onClick={onOpenCookieSettings}\n >\n {labels.cookieSettings}\n </button>\n )}\n {copyright != null && (\n <Paragraphs className={styles.Copyright}>{copyright}</Paragraphs>\n )}\n </>\n )}\n </div>\n </footer>\n );\n}\n","/*\n * The website's header (Figma `SiteHeader`, leabank.no's Header, MobileMenu\n * and DropdownShared): a white bar over the brand stripe. From 1312px, the\n * website's header width, the bar is 72px tall with the row of items; below\n * it, 55px with the menu button, the logo and the account icon. Both\n * heights are off the spacing grid (9 and 6.875 steps): they are the\n * website's.\n */\n.SiteHeader {\n --site-header-bar: calc(var(--lea-spacing) * 6.875);\n\n display: flex;\n width: 100%;\n flex-direction: column;\n background-color: var(--lea-palette-bg-base);\n color: var(--lea-palette-text-base);\n}\n\n.Bar {\n padding-inline: var(--lea-spacing-2);\n background-color: var(--lea-palette-bg-base);\n}\n\n.BarContent {\n display: flex;\n width: 100%;\n max-width: var(--lea-width-section);\n min-height: var(--site-header-bar, calc(var(--lea-spacing) * 6.875));\n align-items: center;\n justify-content: space-between;\n margin-inline: auto;\n}\n\n/*\n * The logo, as the content of the link to the start page. On the narrow bar\n * it takes the free room on both sides, which puts it where the website's\n * `space-between` does, and keeps it off the end when there is no account\n * link.\n */\n.Home {\n display: flex;\n height: var(--lea-spacing-4);\n flex-shrink: 0;\n align-items: center;\n margin-inline: auto;\n border-radius: var(--lea-radius-sm);\n color: inherit;\n}\n\n.Home img,\n.Home svg {\n display: block;\n width: auto;\n height: 100%;\n}\n\n.Actions {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n gap: var(--lea-spacing-2);\n}\n\n.Account {\n position: relative;\n display: flex;\n width: var(--lea-spacing-4);\n height: var(--lea-spacing-4);\n flex-shrink: 0;\n border-radius: var(--lea-radius-full);\n color: inherit;\n}\n\n.Account > * {\n width: 100%;\n height: 100%;\n}\n\n/* The menu button: a 24px icon at the start of the narrow bar. */\n.MenuButton {\n position: relative;\n display: flex;\n width: var(--lea-spacing-3);\n height: var(--lea-spacing-3);\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 background: transparent;\n color: inherit;\n cursor: pointer;\n}\n\n.MenuButton svg {\n width: var(--lea-spacing-3);\n height: var(--lea-spacing-3);\n}\n\n/*\n * The icon buttons of the narrow bar keep the website's size, and answer to\n * 44px around it, the size of a primary control: 10px round the 24px menu\n * icon, 6px round the 32px account icon.\n */\n.MenuButton::before,\n.Account::before {\n position: absolute;\n content: \"\";\n inset: calc(var(--lea-spacing) * -1.25);\n}\n\n.Account::before {\n inset: calc(var(--lea-spacing) * -0.75);\n}\n\n.Action {\n display: none;\n}\n\n.Nav {\n display: none;\n}\n\n.Home:focus-visible,\n.Account:focus-visible,\n.MenuButton:focus-visible,\n.Pill:focus-visible,\n.Cta:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n/* ---- The row of items, from 1312px ---- */\n\n.List {\n display: flex;\n min-width: 0;\n flex: 1 1 auto;\n align-items: center;\n gap: var(--lea-spacing-1);\n padding: 0;\n margin: 0;\n list-style: none;\n}\n\n/* The end items (Kundeservice, Om oss) sit at the end of the row. */\n.Item[data-end] {\n margin-inline-start: auto;\n}\n\n/*\n * A 40px pill: 16px text 16px from its sides, and the chevron of a menu\n * 4px after it. Grey on hover and while its menu is open, as the website.\n */\n.Pill {\n display: inline-flex;\n min-height: var(--lea-spacing-5);\n box-sizing: border-box;\n align-items: center;\n gap: var(--lea-spacing-0-5);\n padding: var(--lea-spacing-0-5) var(--lea-spacing-1-5);\n border: 0;\n border-radius: var(--lea-radius-5xl);\n background-color: transparent;\n color: var(--lea-palette-text-base);\n cursor: pointer;\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 text-decoration: none;\n transition: background-color var(--lea-duration-fast)\n var(--lea-easing-standard);\n white-space: nowrap;\n}\n\n@media (hover: hover) {\n .Pill:hover {\n background-color: var(--lea-palette-bg-subtle);\n }\n}\n\n.Pill[data-popup-open] {\n background-color: var(--lea-palette-bg-subtle);\n}\n\n.Chevron {\n display: flex;\n transition: rotate var(--lea-duration-fast) var(--lea-easing-standard);\n}\n\n.Chevron svg {\n width: var(--lea-spacing-3);\n height: var(--lea-spacing-3);\n}\n\n.Chevron[data-popup-open] {\n rotate: 180deg;\n}\n\n@media (min-width: 1312px) {\n .Bar {\n padding-inline: var(--lea-spacing-8);\n }\n\n .BarContent {\n --site-header-bar: calc(var(--lea-spacing) * 9);\n\n justify-content: flex-start;\n }\n\n /* 38px, the website's logo. */\n .Home {\n height: calc(var(--lea-spacing) * 4.75);\n margin-inline: 0;\n }\n\n .Account {\n width: var(--lea-spacing-5);\n height: var(--lea-spacing-5);\n }\n\n .Account::before {\n content: none;\n }\n\n .MenuButton {\n display: none;\n }\n\n .Action {\n display: flex;\n }\n\n /*\n * The items take the room between the logo and the actions. Below the\n * website's widest step the pills and the gaps round them are 4px\n * tighter on each side, so the longest row (leabank.es, with its 249px\n * call to action) fits at 1312px with room to spare.\n */\n .Nav {\n display: flex;\n min-width: 0;\n flex: 1 1 auto;\n padding-inline: var(--lea-spacing-2);\n }\n}\n\n@media (min-width: 1440px) {\n .Nav {\n padding-inline: var(--lea-spacing-4);\n }\n\n .Pill {\n padding-inline: var(--lea-spacing-2);\n }\n}\n\n/* ---- The menu of an item, from 1312px ---- */\n\n.Positioner {\n z-index: 1300;\n width: var(--positioner-width);\n height: var(--positioner-height);\n max-width: var(--available-width);\n transition-duration: var(--lea-duration-normal);\n transition-property: top, left, right, bottom;\n transition-timing-function: var(--lea-easing-standard);\n}\n\n/* Keeps the pointer over the menu on its way down from the pill. */\n.Positioner::before {\n position: absolute;\n top: calc(var(--lea-spacing) * -1);\n height: var(--lea-spacing);\n content: \"\";\n inset-inline: 0;\n}\n\n.Positioner[data-instant] {\n transition: none;\n}\n\n/*\n * The website's dropdown: a white panel 400px wide with 32px corners and\n * the soft shadow, 8px under the pill. It drops in from 16px above, and\n * moves and resizes when the pointer goes to the next item.\n */\n.Popup {\n position: relative;\n width: var(--popup-width);\n height: var(--popup-height);\n border-radius: var(--lea-radius-5xl);\n background-color: var(--lea-palette-bg-base);\n box-shadow: var(--lea-shadow-soft);\n color: var(--lea-palette-text-base);\n transform-origin: var(--transform-origin);\n transition-duration: var(--lea-duration-normal);\n transition-property: opacity, translate, width, height;\n transition-timing-function: var(--lea-easing-standard);\n}\n\n.Popup[data-starting-style],\n.Popup[data-ending-style] {\n opacity: 0;\n translate: 0 calc(var(--lea-spacing) * -2);\n}\n\n.Viewport {\n position: relative;\n overflow: hidden;\n width: 100%;\n height: 100%;\n border-radius: inherit;\n}\n\n.Content {\n display: flex;\n width: max-content;\n box-sizing: border-box;\n gap: var(--lea-spacing-4);\n padding: var(--lea-spacing-4);\n transition: opacity var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n/* In the server's markup a closed menu is there, hidden. */\n.Content[hidden] {\n display: none;\n}\n\n.Content[data-starting-style],\n.Content[data-ending-style] {\n opacity: 0;\n}\n\n/* One group makes the website's 400px panel: 336 + 2 × 32. */\n.Group {\n display: flex;\n width: calc(var(--lea-spacing) * 42);\n flex-direction: column;\n align-items: flex-start;\n gap: var(--lea-spacing-3);\n}\n\n/* 14px semibold, the website's category title. No text style has it. */\n.GroupTitle {\n margin: 0;\n font-size: var(--lea-font-step-minus-1);\n font-weight: var(--lea-font-weight-semibold);\n line-height: var(--lea-line-height-tight);\n}\n\n.Links {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--lea-spacing-2);\n padding: 0;\n margin: 0;\n list-style: none;\n}\n\n/* A list item would add a line box of its own round the inline link. */\n.Links > li {\n display: flex;\n}\n\n.Separator {\n display: block;\n width: var(--lea-spacing-3);\n height: 1px;\n background-color: var(--lea-palette-primary-base);\n}\n\n/* The last link: purple and underlined on hover, as the website's. */\n.Cta {\n display: inline-flex;\n align-items: center;\n gap: var(--lea-spacing-0-5);\n border-radius: var(--lea-radius-sm);\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 letter-spacing: var(--lea-text-body-md-letter-spacing);\n line-height: var(--lea-text-body-md-line-height);\n text-decoration: none;\n transition: color var(--lea-duration-fast) var(--lea-easing-standard);\n}\n\n@media (hover: hover) {\n .Cta:hover {\n color: var(--lea-palette-primary-base);\n }\n\n .Cta:hover > span {\n text-decoration: underline;\n }\n}\n\n.CtaArrow {\n width: var(--lea-spacing-3);\n height: var(--lea-spacing-3);\n flex-shrink: 0;\n}\n\n/* ---- The menu below 1312px ---- */\n\n.Backdrop {\n position: fixed;\n z-index: 1300;\n background-color: var(--lea-palette-bg-overlay);\n inset: 0;\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.DrawerViewport {\n position: fixed;\n z-index: 1300;\n inset: 0;\n pointer-events: none;\n}\n\n/*\n * The menu covers the header from the top, with a bar of its own that\n * looks like the header's (the close button where the menu button was), so\n * nothing seems to move but the icon. The list then slides down from under\n * that bar, as on the website: the panel is revealed downwards and the list\n * moves with it. On a phone it fills the screen; from 640px it is as tall as\n * its rows, over a dimmed page.\n */\n.MenuPopup {\n --site-header-bar: calc(var(--lea-spacing) * 6.875);\n\n position: absolute;\n top: 0;\n left: 0;\n display: flex;\n width: 100%;\n min-height: 100dvh;\n max-height: 100dvh;\n flex-direction: column;\n background-color: var(--lea-palette-bg-base);\n clip-path: inset(0);\n color: var(--lea-palette-text-base);\n outline: 0;\n pointer-events: auto;\n transform: translateY(var(--drawer-swipe-movement-y, 0px));\n transition-duration: var(--lea-duration-normal);\n transition-property: clip-path, transform;\n transition-timing-function: var(--lea-easing-standard);\n}\n\n@media (min-width: 640px) {\n .MenuPopup {\n min-height: 0;\n }\n}\n\n/* The bar and the stripe: 55px and 4px. */\n.MenuPopup[data-starting-style],\n.MenuPopup[data-ending-style] {\n clip-path: inset(\n 0 0 calc(100% - var(--site-header-bar) - var(--lea-spacing-0-5)) 0\n );\n}\n\n.MenuPopup[data-swiping] {\n transition-duration: 0ms;\n}\n\n.MenuTop {\n position: relative;\n z-index: 1;\n flex-shrink: 0;\n background-color: var(--lea-palette-bg-base);\n}\n\n.MenuContent {\n min-height: 0;\n flex: 1 1 auto;\n overflow-y: auto;\n overscroll-behavior: contain;\n transition: translate var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n.MenuPopup[data-starting-style] .MenuContent,\n.MenuPopup[data-ending-style] .MenuContent {\n translate: 0 -100%;\n}\n\n.MenuList {\n padding: 0;\n margin: 0;\n list-style: none;\n}\n\n/* A 72px row: a link, or the button of a submenu. */\n.Row {\n display: flex;\n width: 100%;\n min-height: calc(var(--lea-spacing) * 9);\n box-sizing: border-box;\n align-items: center;\n gap: var(--lea-spacing-0-5);\n padding: var(--lea-spacing-1) var(--lea-spacing-2);\n border: 0;\n background-color: transparent;\n color: var(--lea-palette-text-base);\n cursor: pointer;\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 text-align: start;\n text-decoration: none;\n transition: background-color var(--lea-duration-fast)\n var(--lea-easing-standard);\n}\n\n@media (hover: hover) {\n .Row:hover {\n background-color: var(--lea-palette-bg-subtle);\n }\n}\n\n.Row:active {\n background-color: var(--lea-palette-bg-subtle);\n}\n\n/* The rows span the menu edge to edge: the ring sits inside them. */\n.Row:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: -0.125rem;\n}\n\n.RowChevron {\n width: var(--lea-spacing-3);\n height: var(--lea-spacing-3);\n flex-shrink: 0;\n transition: rotate var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n.Row[data-panel-open] .RowChevron {\n rotate: 180deg;\n}\n\n.SubmenuPanel {\n overflow: hidden;\n height: var(--collapsible-panel-height);\n transition: height var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n.SubmenuPanel[data-starting-style],\n.SubmenuPanel[data-ending-style] {\n height: 0;\n}\n\n.Submenu {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--lea-spacing-2);\n padding: var(--lea-spacing-2);\n background-color: var(--lea-palette-bg-subtle);\n}\n\n/* The item's own page: 14px semibold, spaced out 4 %, as the website's. */\n.Submenu .Parent {\n font-size: var(--lea-font-step-minus-1);\n font-weight: var(--lea-font-weight-semibold);\n letter-spacing: 0.04em;\n}\n\n.Submenu .CtaArrow {\n width: var(--lea-spacing-2-5);\n height: var(--lea-spacing-2-5);\n}\n\n@media (min-width: 1312px) {\n /* The menu belongs to the narrow header. */\n .Backdrop,\n .DrawerViewport {\n display: none;\n }\n}\n\n@media (max-width: 1311.95px) {\n .Positioner {\n display: none;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Pill,\n .Chevron,\n .Positioner,\n .Popup,\n .Content,\n .Cta,\n .Backdrop,\n .MenuPopup,\n .MenuContent,\n .Row,\n .RowChevron,\n .SubmenuPanel {\n transition-duration: 0.01ms;\n }\n}\n","\"use client\";\n\nimport { Collapsible } from \"@base-ui/react/collapsible\";\nimport { Drawer } from \"@base-ui/react/drawer\";\nimport { ArrowUpRight, ChevronDown, Menu, X } from \"lucide-react\";\nimport { useEffect, useState, type MouseEvent, type ReactNode } from \"react\";\n\nimport { BrandDivider } from \"../../index\";\nimport { ArrowRevealLink } from \"../arrow-reveal-link\";\nimport type { SiteHeaderItem } from \"./site-header\";\nimport styles from \"./site-header.module.css\";\nimport { hasMenu, hasPage, SiteLink } from \"../site-header-link\";\nimport { useSiteHeaderLabels } from \"../use-site-header-labels\";\n\n/** The width from which the header shows its row of items instead. */\nconst WIDE = \"(min-width: 1312px)\";\n\ntype SubmenuProps = {\n item: Extract<SiteHeaderItem, { groups: unknown[] }>;\n open: boolean;\n onOpenChange: (open: boolean) => void;\n};\n\n/** One item with a menu: a 72px row that opens its links under it. */\nfunction Submenu({ item, open, onOpenChange }: SubmenuProps) {\n const links = item.groups.flatMap((group) => group.links);\n const ctas = item.groups.flatMap((group) =>\n group.cta == null ? [] : [group.cta],\n );\n\n return (\n <Collapsible.Root open={open} onOpenChange={onOpenChange}>\n <Collapsible.Trigger className={styles.Row}>\n {item.label}\n <ChevronDown aria-hidden className={styles.RowChevron} />\n </Collapsible.Trigger>\n <Collapsible.Panel className={styles.SubmenuPanel}>\n <div className={styles.Submenu}>\n {(hasPage(item) || links.length > 0) && (\n <ul className={styles.Links}>\n {hasPage(item) && (\n <li>\n <ArrowRevealLink\n className={styles.Parent}\n href={item.href}\n render={item.render}\n >\n {item.label}\n </ArrowRevealLink>\n </li>\n )}\n {links.map((link, index) => (\n <li key={index}>\n <ArrowRevealLink href={link.href} render={link.render}>\n {link.label}\n </ArrowRevealLink>\n </li>\n ))}\n </ul>\n )}\n {ctas.length > 0 && links.length > 0 && (\n <span aria-hidden className={styles.Separator} />\n )}\n {ctas.map((cta, index) => (\n <SiteLink\n key={index}\n className={styles.Cta}\n href={cta.href}\n render={cta.render}\n >\n <span>{cta.label}</span>\n <ArrowUpRight aria-hidden className={styles.CtaArrow} />\n </SiteLink>\n ))}\n </div>\n </Collapsible.Panel>\n </Collapsible.Root>\n );\n}\n\n/**\n * The items as a list of 72px rows: a link, or a button that opens the\n * item's links under it. One submenu is open at a time, as on the website.\n */\nfunction MenuList({ items }: { items: SiteHeaderItem[] }) {\n const labels = useSiteHeaderLabels();\n const [openIndex, setOpenIndex] = useState<number | null>(null);\n\n return (\n <nav aria-label={labels.navigation}>\n <ul className={styles.MenuList}>\n {items.map((item, index) => (\n <li key={index}>\n {hasMenu(item) ? (\n <Submenu\n item={item}\n open={openIndex === index}\n onOpenChange={(open) => setOpenIndex(open ? index : null)}\n />\n ) : (\n <SiteLink\n className={styles.Row}\n href={item.href}\n render={item.render}\n >\n {item.label}\n </SiteLink>\n )}\n </li>\n ))}\n </ul>\n </nav>\n );\n}\n\ntype SiteHeaderMenuProps = {\n items: SiteHeaderItem[];\n /** The link around the logo, shown in the menu's own bar too. */\n home: ReactNode;\n /** The account link, shown in the menu's own bar too. */\n account: ReactNode;\n};\n\n/**\n * Internal to `SiteHeader`: the menu below 1312px. The button in the bar\n * opens a dialog that drops in from the top (Base UI's `Drawer`): focus\n * moves into it and stays there, the page behind it does not scroll, and\n * Escape, the close button, a swipe up or a press outside closes it. The\n * dialog starts with a bar of its own, the bar of the header with a close\n * button where the menu button was, so the visitor sees the same bar and\n * always has a visible way out. Its links are not in the page while it is\n * closed, and following one closes it.\n */\nexport function SiteHeaderMenu({ items, home, account }: SiteHeaderMenuProps) {\n const labels = useSiteHeaderLabels();\n const [open, setOpen] = useState(false);\n\n // The menu belongs to the narrow header: close it if the window widens.\n useEffect(() => {\n if (!open) return;\n const query = window.matchMedia(WIDE);\n const close = () => {\n if (query.matches) setOpen(false);\n };\n query.addEventListener(\"change\", close);\n return () => query.removeEventListener(\"change\", close);\n }, [open]);\n\n // A client-side route change keeps the header mounted: close the menu on\n // any link followed inside it.\n const closeOnLink = (event: MouseEvent) => {\n if (event.target instanceof Element && event.target.closest(\"a\")) {\n setOpen(false);\n }\n };\n\n return (\n <Drawer.Root open={open} onOpenChange={setOpen} swipeDirection=\"up\">\n <Drawer.Trigger className={styles.MenuButton} aria-label={labels.menu}>\n <Menu aria-hidden />\n </Drawer.Trigger>\n <Drawer.Portal>\n <Drawer.Backdrop className={styles.Backdrop} />\n <Drawer.Viewport className={styles.DrawerViewport}>\n <Drawer.Popup\n className={styles.MenuPopup}\n aria-label={labels.menu}\n onClick={closeOnLink}\n >\n <div className={styles.MenuTop}>\n <div className={styles.Bar}>\n <div className={styles.BarContent}>\n <Drawer.Close\n className={styles.MenuButton}\n aria-label={labels.closeMenu}\n >\n <X aria-hidden />\n </Drawer.Close>\n {home}\n {account && <div className={styles.Actions}>{account}</div>}\n </div>\n </div>\n <BrandDivider size=\"sm\" />\n </div>\n <Drawer.Content className={styles.MenuContent}>\n <MenuList items={items} />\n </Drawer.Content>\n </Drawer.Popup>\n </Drawer.Viewport>\n </Drawer.Portal>\n </Drawer.Root>\n );\n}\n","\"use client\";\n\nimport { NavigationMenu } from \"@base-ui/react/navigation-menu\";\nimport { ArrowUpRight, ChevronDown, CircleUserRound } from \"lucide-react\";\nimport {\n useId,\n useState,\n type ComponentProps,\n type ReactElement,\n type ReactNode,\n} from \"react\";\n\nimport { BrandDivider } from \"../../index\";\nimport { ArrowRevealLink } from \"../arrow-reveal-link\";\nimport styles from \"./site-header.module.css\";\nimport { hasMenu, SiteLink } from \"../site-header-link\";\nimport { SiteHeaderMenu } from \"./site-header-menu\";\nimport { useSiteHeaderLabels } from \"../use-site-header-labels\";\n\n/**\n * Where a link goes: `href`, or a router link in `render` that carries its\n * own (`<NextLink href=\"/lan\" />`). A link without either does not compile.\n */\nexport type SiteHeaderLinkTarget =\n | {\n /** Where the link goes. */\n href: string;\n /**\n * A router link to render instead of an `<a>`, carrying its own\n * `href`, and any `target` or `data-` attribute the page wants on it.\n */\n render?: ReactElement;\n }\n | { href?: undefined; render: ReactElement };\n\n/** A link in the header or in one of its menus. */\nexport type SiteHeaderLink = SiteHeaderLinkTarget & {\n /** The text of the link. */\n label: ReactNode;\n};\n\n/**\n * A group of links in a menu: the website's \"Sublink\", a column of the\n * dropdown. The menus on leabank.no have one each.\n */\nexport type SiteHeaderGroup = {\n /**\n * The small title over the links on a wide screen (\"Låneprodukter\"). It\n * names the list; the phone menu leaves it out, as the website's.\n */\n title?: ReactNode;\n /** The links, each with the arrow that grows in on hover. */\n links: SiteHeaderLink[];\n /**\n * The last link, under a short purple line, with an arrow pointing up and\n * to the right (\"Se våre lån\").\n */\n cta?: SiteHeaderLink;\n};\n\n/**\n * A top-level item: a link, or a menu of `groups`. With a menu the item is a\n * button that opens it, so its own page is not a click on the item any more:\n * link to it in the menu (the website's menus do, with the `cta` or a link).\n * Given `href` or `render`, a menu item's own page is the first link of its\n * submenu on a phone, in small bold type, as on the website.\n */\nexport type SiteHeaderItem =\n | (SiteHeaderLink & { groups?: undefined })\n | {\n /** The text of the item. */\n label: ReactNode;\n /** The item's own page, the first link of its submenu on a phone. */\n href?: string;\n /** A router link to the item's own page, instead of `href`. */\n render?: ReactElement;\n /** The menu that opens from the item. */\n groups: SiteHeaderGroup[];\n };\n\n/** The account link, a round icon beside the call to action. */\nexport type SiteHeaderAccount = SiteHeaderLinkTarget & {\n /**\n * The link's name, read out in place of the icon (\"Logg inn\", \"Min side\").\n */\n label: string;\n /**\n * The icon, 40px (32px below 1312px). Left out, Lucide's\n * `CircleUserRound`; the website passes its own image with `alt=\"\"`.\n */\n icon?: ReactNode;\n};\n\nexport type SiteHeaderProps = Omit<ComponentProps<\"header\">, \"children\"> & {\n /**\n * The logo, an `<img>` or `next/image`, 38px tall (32px below 1312px). It\n * is the content of the link to the start page, which is named \"Lea Bank,\n * forside\" by the header, so the image can take `alt=\"\"`.\n */\n logo: ReactNode;\n /**\n * The link to the start page, a router link carrying its own `href`:\n * `<NextLink href=\"/\" />`.\n * @default <a href=\"/\" />\n */\n homeRender?: ReactElement;\n /** The items at the start of the row (Lån, Sparing, Forsikring …). */\n items: SiteHeaderItem[];\n /**\n * The items pushed to the end of the row (Kundeservice, Om oss). On a\n * phone they follow `items` in the menu.\n */\n endItems?: SiteHeaderItem[];\n /** The account link, an icon at the end of the bar at every width. */\n account?: SiteHeaderAccount;\n /**\n * The call to action at the end of the bar from 1312px, an\n * `ActionButton size=\"sm\"` (\"Min side\"). Below 1312px it is left out, as\n * on the website.\n */\n action?: ReactNode;\n};\n\nfunction MenuGroup({ group }: { group: SiteHeaderGroup }) {\n const titleId = useId();\n const hasTitle = group.title != null;\n const hasLinks = group.links.length > 0;\n\n return (\n <div className={styles.Group}>\n {hasTitle && (\n <p id={titleId} className={styles.GroupTitle}>\n {group.title}\n </p>\n )}\n {hasLinks && (\n <ul\n className={styles.Links}\n aria-labelledby={hasTitle ? titleId : undefined}\n >\n {group.links.map((link, index) => (\n <li key={index}>\n <ArrowRevealLink\n render={\n <NavigationMenu.Link\n closeOnClick\n href={link.href}\n render={link.render}\n />\n }\n >\n {link.label}\n </ArrowRevealLink>\n </li>\n ))}\n </ul>\n )}\n {group.cta != null && hasLinks && (\n <span aria-hidden className={styles.Separator} />\n )}\n {group.cta != null && (\n <NavigationMenu.Link\n closeOnClick\n className={styles.Cta}\n href={group.cta.href}\n render={group.cta.render}\n >\n <span>{group.cta.label}</span>\n <ArrowUpRight aria-hidden className={styles.CtaArrow} />\n </NavigationMenu.Link>\n )}\n </div>\n );\n}\n\ntype NavItemProps = {\n item: SiteHeaderItem;\n value: string;\n end: boolean;\n /** Called when the focus comes to the item, its menu included. */\n onFocus: (value: string) => void;\n};\n\nfunction NavItem({ item, value, end, onFocus }: NavItemProps) {\n return (\n <NavigationMenu.Item\n value={value}\n className={styles.Item}\n data-end={end || undefined}\n // React's focus events bubble out of the menu's portal, so focus in\n // the open menu counts as focus in its item.\n onFocus={() => onFocus(value)}\n >\n {hasMenu(item) ? (\n <>\n <NavigationMenu.Trigger className={styles.Pill}>\n {item.label}\n <NavigationMenu.Icon className={styles.Chevron}>\n <ChevronDown />\n </NavigationMenu.Icon>\n </NavigationMenu.Trigger>\n {/* Mounted, hidden, in the server's markup too, so a crawler finds\n the links the menus hold. */}\n <NavigationMenu.Content keepMounted className={styles.Content}>\n {item.groups.map((group, index) => (\n <MenuGroup key={index} group={group} />\n ))}\n </NavigationMenu.Content>\n </>\n ) : (\n <NavigationMenu.Link\n closeOnClick\n className={styles.Pill}\n href={item.href}\n render={item.render}\n >\n {item.label}\n </NavigationMenu.Link>\n )}\n </NavigationMenu.Item>\n );\n}\n\n/**\n * The website's header, as on every page of leabank.no: the logo, the main\n * navigation, the account link and a call to action on a white bar, with\n * the brand stripe under it.\n *\n * ```tsx\n * <SiteHeader\n * logo={<img src=\"/lea-bank.svg\" alt=\"\" />}\n * homeRender={<NextLink href=\"/\" />}\n * items={[\n * {\n * label: \"Lån\",\n * render: <NextLink href=\"/lan\" />,\n * groups: [\n * {\n * title: \"Låneprodukter\",\n * links: [{ label: \"Forbrukslån\", render: <NextLink href=\"/lan/forbrukslan\" /> }],\n * cta: { label: \"Se våre lån\", render: <NextLink href=\"/lan\" /> },\n * },\n * ],\n * },\n * { label: \"Økonomiguiden\", render: <NextLink href=\"/okonomiguiden\" /> },\n * ]}\n * endItems={[{ label: \"Kundeservice\", href: \"/kundeservice\" }]}\n * account={{ label: \"Logg inn\", href: \"https://secure.leabank.no/\" }}\n * action={<ActionButton size=\"sm\" render={<a href=\"https://secure.leabank.no/\" />}>Min side</ActionButton>}\n * />\n * ```\n *\n * From 1312px the bar is 72px tall: the items are 40px pills that turn grey\n * on hover, and an item with `groups` opens its menu, a white panel with the\n * website's soft shadow, on hover, on a click and with the arrow-down key\n * (Base UI's `NavigationMenu`: links, not a `role=\"menu\"`; Escape closes it,\n * and the focus moves into it with Tab). Below 1312px the bar is 55px tall\n * with a menu button, the logo in the middle and the account icon; the menu\n * drops in from under the bar as a dialog (Base UI's `Drawer` from the top),\n * the items 72px rows and the menus disclosures, one open at a time. The\n * menu closes when a link in it is followed, and its links are not in the\n * page while it is closed.\n */\nexport function SiteHeader({\n logo,\n homeRender,\n items,\n endItems = [],\n account,\n action,\n className,\n ...rest\n}: SiteHeaderProps) {\n const labels = useSiteHeaderLabels();\n const [openItem, setOpenItem] = useState<string | null>(null);\n\n // Tabbing on from a menu to the next item closes the menu, as on the\n // website; Base UI would keep it open while the focus is in the row.\n const closeOthers = (value: string) => {\n if (openItem != null && openItem !== value) setOpenItem(null);\n };\n\n const home = (\n <SiteLink\n href={homeRender == null ? \"/\" : undefined}\n render={homeRender}\n className={styles.Home}\n aria-label={labels.home}\n >\n {logo}\n </SiteLink>\n );\n\n const accountLink = account != null && (\n <SiteLink\n href={account.href}\n render={account.render}\n className={styles.Account}\n aria-label={account.label}\n >\n {account.icon ?? <CircleUserRound aria-hidden />}\n </SiteLink>\n );\n\n return (\n <header\n {...rest}\n className={[styles.SiteHeader, className].filter(Boolean).join(\" \")}\n >\n <div className={styles.Bar}>\n <div className={styles.BarContent}>\n <SiteHeaderMenu\n items={[...items, ...endItems]}\n home={home}\n account={accountLink}\n />\n {home}\n <NavigationMenu.Root\n className={styles.Nav}\n aria-label={labels.navigation}\n closeDelay={150}\n value={openItem}\n onValueChange={setOpenItem}\n >\n <NavigationMenu.List className={styles.List}>\n {[...items, ...endItems].map((item, index) => (\n <NavItem\n key={index}\n item={item}\n value={`item-${index}`}\n end={endItems.length > 0 && index === items.length}\n onFocus={closeOthers}\n />\n ))}\n </NavigationMenu.List>\n <NavigationMenu.Portal>\n <NavigationMenu.Positioner\n className={styles.Positioner}\n side=\"bottom\"\n align=\"start\"\n sideOffset={8}\n collisionPadding={16}\n >\n <NavigationMenu.Popup className={styles.Popup} render={<div />}>\n <NavigationMenu.Viewport className={styles.Viewport} />\n </NavigationMenu.Popup>\n </NavigationMenu.Positioner>\n </NavigationMenu.Portal>\n </NavigationMenu.Root>\n {(accountLink || action != null) && (\n <div className={styles.Actions}>\n {accountLink}\n {action != null && <div className={styles.Action}>{action}</div>}\n </div>\n )}\n </div>\n </div>\n <BrandDivider size=\"sm\" />\n </header>\n );\n}\n","/*\n * The website's CardWithImageSection: no colour of its own. From 1024px two\n * equal columns 64px apart, centred on each other: the title (`heading-xl`)\n * and the text (`body-lg`) 32px apart, and a photo without corners, at most\n * 560px wide, at the end of its column (next to the text when it comes\n * first). The action and the price example are centred 80px under both.\n * Below 1024px one column, the photo at most 440px and the actions at the\n * start; below 768px everything 32px apart (the text 16px) and the title on\n * `heading-md`.\n */\n.SplitContent {\n display: flex;\n min-width: 0;\n flex-direction: column;\n /* 80px, the website's `xxl`. */\n gap: calc(var(--lea-spacing) * 10);\n}\n\n.Grid {\n display: grid;\n grid-template-columns: minmax(0, 1fr);\n align-items: center;\n gap: var(--lea-spacing-8);\n}\n\n.Text {\n display: flex;\n min-width: 0;\n flex-direction: column;\n gap: var(--lea-spacing-4);\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.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.Description > * {\n margin: 0;\n}\n\n.Description > * + * {\n margin-block-start: var(--lea-spacing-1);\n}\n\n.Media {\n display: flex;\n min-width: 0;\n}\n\n.SplitContent[data-image-position=\"start\"] .Media {\n order: -1;\n}\n\n.Image {\n display: block;\n width: 100%;\n /* The website's 440px below 1024px. */\n max-width: calc(var(--lea-spacing) * 55);\n height: auto;\n}\n\n.Actions {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--lea-spacing-2);\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@media (min-width: 1024px) {\n .SplitContent[data-has-image] .Grid {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n }\n\n /* At the end of its column on either side, as the website: next to the\n text when it comes first. */\n .Media {\n justify-content: flex-end;\n }\n\n .Image {\n /* The website's 560px. */\n max-width: calc(var(--lea-spacing) * 70);\n }\n\n .Actions {\n align-items: center;\n text-align: center;\n }\n}\n\n@media (max-width: 767.95px) {\n .SplitContent,\n .Grid {\n gap: var(--lea-spacing-4);\n }\n\n .Text {\n gap: var(--lea-spacing-2);\n }\n\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 type { ReactElement, ReactNode, Ref } from \"react\";\n\nimport { mergeClassName } from \"../../utils/merge-class-name\";\nimport { Paragraphs } from \"../paragraphs\";\nimport styles from \"./split-content.module.css\";\n\nexport type SplitContentImagePosition = \"start\" | \"end\";\n\n/** `alt` is required with a photo, `\"\"` when it is decoration. */\ntype SplitContentImage =\n | {\n /** The photo. Without one, the text takes the whole width. */\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`, 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 SplitContentProps = Omit<\n useRender.ComponentProps<\"div\">,\n \"ref\" | \"title\" | \"children\"\n> &\n SplitContentImage & {\n /** The rendered element: the `<div>`, or the element passed to `render`. */\n ref?: Ref<HTMLElement>;\n /** The title. */\n title: ReactNode;\n /** The element for the title, an `<h2>` by default: `titleRender={<h3 />}`. */\n titleRender?: ReactElement;\n /**\n * The text under the title. A string becomes one paragraph; pass `<p>`\n * elements for several.\n */\n text?: ReactNode;\n /**\n * Where the photo sits: `\"start\"` before the text (on the left, or on\n * top below 1024px), `\"end\"` after it.\n * @default \"end\"\n */\n imagePosition?: SplitContentImagePosition;\n /** The call to action under both columns: an `ActionButton`. */\n action?: ReactNode;\n /** The price example under the action, in the small print. */\n priceExample?: ReactNode;\n };\n\n/**\n * Text beside a photo, on the section's own colour: a title and a text in one\n * column, a photo without corners in the other, and the call to action\n * centred under both.\n *\n * ```tsx\n * <Section surface=\"subtle\">\n * <SplitContent\n * title=\"Refinansier dyre smålån\"\n * text=\"Samle kredittkortgjeld og smålån i ett lån med én månedlig betaling.\"\n * src=\"/bilder/par.jpg\"\n * alt=\"\"\n * action={<ActionButton render={<NextLink href=\"/refinansiering\" />}>Søk om refinansiering</ActionButton>}\n * />\n * </Section>\n * ```\n *\n * Two equal columns 64px apart from 1024px, the photo at most 560px wide at\n * the end of its column; one column below, the photo at most 440px and\n * the action at the start. It takes the text colour of its section.\n */\nexport function SplitContent({\n title,\n titleRender,\n text,\n src,\n alt,\n imageRender,\n imagePosition = \"end\",\n action,\n priceExample,\n render,\n ref,\n className,\n ...rest\n}: SplitContentProps) {\n const titleElement = useRender({\n render: titleRender,\n defaultTagName: \"h2\",\n props: { className: styles.Title, 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: \"div\",\n props: {\n ...rest,\n \"data-image-position\": imagePosition,\n \"data-has-image\": image != null || undefined,\n className: mergeClassName(styles.SplitContent, className)({}),\n children: (\n <>\n <div className={styles.Grid}>\n <div className={styles.Text}>\n {titleElement}\n {text != null && (\n <Paragraphs className={styles.Description}>{text}</Paragraphs>\n )}\n </div>\n {image != null && <div className={styles.Media}>{image}</div>}\n </div>\n {(action != null || priceExample != null) && (\n <div className={styles.Actions}>\n {action}\n {priceExample != null && (\n <p className={styles.Price}>{priceExample}</p>\n )}\n </div>\n )}\n </>\n ),\n },\n });\n}\n","/*\n * The website's FourStatsBlock. The list has 64px above and below. From\n * 1024px the numbers and the bars share the row equally, as the website's\n * grid of 2n + 1 columns: a number column is at least 200px (its labels wrap\n * inside it) and the bars, 4px high and round, take what is left. The stats\n * line up at the top and the bars sit on the middle of the numbers (the\n * website centres each stat in the row, so the numbers of stats with longer\n * text sit higher). Below 1024px the bars go and the numbers sit in a row\n * 16px apart, at least 148px each; below 768px in a column 32px apart.\n *\n * No colour of its own: the numbers, the titles and the bars are the\n * section's text colour, and the text under the titles is that colour at\n * 70 %, which reads on all five surfaces (the website's grey, #5B5B5B, on the\n * light ones).\n */\n.Stats {\n /* The line of a number: 240px, 180px below 1024px, 120px below 768px. */\n --stats-number-line: calc(var(--lea-spacing) * 30);\n\n display: flex;\n min-width: 0;\n flex-direction: column;\n}\n\n.List {\n display: flex;\n align-items: flex-start;\n margin: 0;\n padding: var(--lea-spacing-8) 0;\n list-style: none;\n}\n\n.Bar {\n flex: 1 1 0;\n min-width: 0;\n height: var(--lea-spacing-0-5);\n margin-block-start: calc(\n (var(--stats-number-line) - var(--lea-spacing-0-5)) / 2\n );\n border-radius: var(--lea-radius-full);\n background-color: currentColor;\n}\n\n/* The same share of the row as a bar, but never narrower than its content\n (the number keeps it at least 200px). */\n.Stat {\n display: flex;\n flex: 1 1 0;\n flex-direction: column;\n align-items: center;\n gap: var(--lea-spacing-2);\n text-align: center;\n}\n\n/*\n * The 224px numbers in a 240px line are off the type scale, on the website's\n * stats only; 160px in 180px below 1024px and 128px in 120px below 768px.\n */\n.Number {\n /* The website's StatItem min-width: 200px. */\n min-width: calc(var(--lea-spacing) * 25);\n margin: 0;\n font-size: calc(var(--lea-font-step-0) * 14);\n font-weight: var(--lea-font-weight-semibold);\n line-height: var(--stats-number-line);\n white-space: nowrap;\n}\n\n.Labels {\n display: flex;\n width: 100%;\n flex-direction: column;\n gap: var(--lea-spacing-0-5);\n}\n\n/* 18px semibold, as the Accordion card titles: no text style has it. */\n.Title {\n margin: 0;\n font-size: var(--lea-font-step-1);\n font-weight: var(--lea-font-weight-semibold);\n line-height: var(--lea-line-height-snug);\n overflow-wrap: break-word;\n}\n\n.Text {\n margin: 0;\n color: color-mix(in srgb, currentColor 70%, transparent);\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 overflow-wrap: break-word;\n}\n\n.Actions {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--lea-spacing-2-5);\n text-align: center;\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@media (max-width: 1023.95px) {\n .Stats {\n --stats-number-line: calc(var(--lea-spacing) * 22.5);\n }\n\n .List {\n gap: var(--lea-spacing-2);\n }\n\n .Bar {\n display: none;\n }\n\n .Number {\n /* The website's 148px. */\n min-width: calc(var(--lea-spacing) * 18.5);\n font-size: calc(var(--lea-font-step-0) * 10);\n }\n}\n\n@media (max-width: 767.95px) {\n .Stats {\n --stats-number-line: calc(var(--lea-spacing) * 15);\n }\n\n .List {\n flex-direction: column;\n align-items: stretch;\n gap: var(--lea-spacing-4);\n }\n\n .Number {\n font-size: calc(var(--lea-font-step-0) * 8);\n }\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { Fragment, type ReactElement, type ReactNode, type Ref } from \"react\";\n\nimport { mergeClassName } from \"../../utils/merge-class-name\";\nimport styles from \"./stats.module.css\";\n\n/** One number, with what it stands for. */\nexport type StatsItem = {\n /** The big number: a figure, or the step of a process. */\n number: ReactNode;\n /** What the number is. */\n title?: ReactNode;\n /** A line about it, under the title. */\n text?: ReactNode;\n};\n\nexport type StatsProps = 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 /** The heading above the numbers: a `SectionHeader`. */\n header?: ReactNode;\n /** The numbers, in a row from 1024px. Two to four fit the website's row. */\n items: readonly StatsItem[];\n /**\n * The element for the title of each number, an `<h3>` by default, as the\n * titles sit under the section's `<h2>`: `titleRender={<p />}` where they\n * should not be headings.\n */\n titleRender?: ReactElement;\n /** The call to action under the numbers: an `ActionButton`. */\n action?: ReactNode;\n /** The price example under the action, in the small print. */\n priceExample?: ReactNode;\n};\n\n/**\n * A row of big numbers with a line on each side, each with a title and a\n * line of text, as the four steps of \"Slik fungerer det\" on leabank.no's\n * loan pages. The numbers are a list.\n *\n * ```tsx\n * <Section>\n * <Stats\n * header={<SectionHeader eyebrow=\"Søk uforpliktende\" title=\"Slik fungerer det\" />}\n * items={[\n * { number: \"1\", title: \"Fyll inn søknaden\", text: \"Tar ca. 3 minutter.\" },\n * { number: \"2\", title: \"Send inn søknad\", text: \"…\" },\n * ]}\n * />\n * </Section>\n * ```\n *\n * From 1024px the numbers are 224px, at least 200px wide each, with rounded\n * bars filling the space between and around them; below 1024px they are\n * 160px in a row without bars, and below 768px 128px in a column. The bars\n * and the text follow the section's text colour.\n */\nexport function Stats({\n header,\n items,\n titleRender,\n action,\n priceExample,\n render,\n ref,\n className,\n ...rest\n}: StatsProps) {\n return useRender({\n render,\n ref,\n defaultTagName: \"div\",\n props: {\n ...rest,\n className: mergeClassName(styles.Stats, className)({}),\n children: (\n <>\n {header}\n <ul className={styles.List}>\n {items.map((item, index) => (\n <Fragment key={index}>\n <li aria-hidden className={styles.Bar} />\n <StatsStat {...item} titleRender={titleRender} />\n </Fragment>\n ))}\n <li aria-hidden className={styles.Bar} />\n </ul>\n {(action != null || priceExample != null) && (\n <div className={styles.Actions}>\n {action}\n {priceExample != null && (\n <p className={styles.Price}>{priceExample}</p>\n )}\n </div>\n )}\n </>\n ),\n },\n });\n}\n\nfunction StatsStat({\n number,\n title,\n text,\n titleRender,\n}: StatsItem & { titleRender?: ReactElement }) {\n const titleElement = useRender({\n enabled: title != null,\n render: titleRender,\n defaultTagName: \"h3\",\n props: { className: styles.Title, children: title },\n });\n\n return (\n <li className={styles.Stat}>\n <p className={styles.Number}>{number}</p>\n {(titleElement != null || text != null) && (\n <div className={styles.Labels}>\n {titleElement}\n {text != null && <p className={styles.Text}>{text}</p>}\n </div>\n )}\n </li>\n );\n}\n","/*\n * The website's TestimonialsBlock: the header, the featured customer and the\n * carousel, 64px apart. The featured row is two equal columns 64px apart from\n * 768px, centred on each other: the video (a square, `huge` corners,\n * near-black until its still loads, dimmed with `bg.overlay` under the\n * elevated play button) and the quote (stars, 16px, the quote `body-lg` in\n * semibold, 32px, the person). Below 768px the video is on top, at most 560px\n * wide, 32px above the quote. No colour of its own: the text is the\n * section's, the line under the name the section's at 70 %, as `Stats`.\n */\n.Testimonials {\n display: flex;\n min-width: 0;\n flex-direction: column;\n gap: var(--lea-spacing-8);\n}\n\n.Featured {\n display: grid;\n align-items: center;\n gap: var(--lea-spacing-4);\n grid-template-columns: minmax(0, 1fr);\n}\n\n@media (min-width: 768px) {\n .Featured[data-video] {\n gap: var(--lea-spacing-8);\n grid-template-columns: repeat(2, minmax(0, 1fr));\n }\n\n .Featured[data-video-position=\"end\"] .Video {\n order: 1;\n }\n}\n\n.Quote {\n display: flex;\n min-width: 0;\n flex-direction: column;\n align-items: flex-start;\n margin: 0;\n}\n\n.Blockquote {\n display: flex;\n margin: 0;\n flex-direction: column;\n gap: var(--lea-spacing-1);\n font-size: var(--lea-text-body-lg-font-size);\n font-weight: var(--lea-font-weight-semibold);\n line-height: var(--lea-text-body-lg-line-height);\n letter-spacing: var(--lea-text-body-lg-letter-spacing);\n}\n\n.Quote > * + .Blockquote {\n margin-block-start: var(--lea-spacing-2);\n}\n\n.Blockquote > * {\n margin: 0;\n}\n\n/* The quotation marks around the whole quote, as on `TestimonialCard`. */\n.Blockquote > :first-child::before {\n content: \"\\201C\";\n}\n\n.Blockquote > :last-child::after {\n content: \"\\201D\";\n}\n\n.Person {\n display: flex;\n flex-direction: column;\n}\n\n.Quote > * + .Person {\n margin-block-start: var(--lea-spacing-4);\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: color-mix(in srgb, currentColor 70%, transparent);\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.Video {\n position: relative;\n display: grid;\n width: 100%;\n /* The website's 560px below 768px. */\n max-width: calc(var(--lea-spacing) * 70);\n order: -1;\n overflow: hidden;\n border-radius: var(--lea-radius-huge);\n aspect-ratio: 1;\n background-color: var(--lea-palette-bg-contrast);\n isolation: isolate;\n place-items: center;\n}\n\n@media (min-width: 768px) {\n .Video {\n max-width: none;\n }\n}\n\n.Video::after {\n position: absolute;\n background-color: var(--lea-palette-bg-overlay);\n content: \"\";\n inset: 0;\n}\n\n.Still {\n position: absolute;\n display: block;\n width: 100%;\n height: 100%;\n inset: 0;\n object-fit: cover;\n}\n\n.Play {\n position: relative;\n z-index: 1;\n}\n\n/*\n * The video dialog: a near-black popup the width of the large dialog, the\n * video filling it and the close button floating over its corner. Two\n * classes, so the dialog's own look loses whichever stylesheet loads last.\n */\n.DialogViewport .DialogPopup {\n position: relative;\n background-color: var(--lea-palette-bg-contrast);\n color: var(--lea-palette-text-contrast);\n}\n\n.DialogVideo {\n display: block;\n width: 100%;\n max-height: calc(100dvh - var(--lea-spacing-4));\n aspect-ratio: 16 / 9;\n background-color: var(--lea-palette-bg-contrast);\n}\n\n.Close {\n position: absolute;\n z-index: 1;\n inset-block-start: var(--lea-spacing-2);\n inset-inline-end: var(--lea-spacing-2);\n}\n","import { createTranslation } from \"../../index\";\n\n/**\n * The words of `Testimonials`: the carousel's name when the section has no\n * title, the video button and dialog, and the language of the captions\n * (`captionsLanguage`, a BCP 47 tag for the `<track>`, and its name).\n */\nexport const useTestimonialsLabels = createTranslation({\n reviews: {\n no: \"Kundeomtaler\",\n se: \"Kundomdömen\",\n fi: \"Asiakasarviot\",\n es: \"Opiniones de clientes\",\n },\n play: {\n no: \"Spill av video\",\n se: \"Spela upp video\",\n fi: \"Toista video\",\n es: \"Reproducir vídeo\",\n },\n video: {\n no: \"Video fra kunde\",\n se: \"Video från kund\",\n fi: \"Asiakkaan video\",\n es: \"Vídeo de un cliente\",\n },\n close: {\n no: \"Lukk video\",\n se: \"Stäng video\",\n fi: \"Sulje video\",\n es: \"Cerrar vídeo\",\n },\n captionsLanguage: { no: \"nb\", se: \"sv\", fi: \"fi\", es: \"es\" },\n captionsLabel: {\n no: \"Norsk\",\n se: \"Svenska\",\n fi: \"Suomi\",\n es: \"Español\",\n },\n});\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { Play, X } from \"lucide-react\";\nimport {\n cloneElement,\n Children,\n useId,\n type ReactElement,\n type ReactNode,\n type Ref,\n} from \"react\";\n\nimport { Dialog, IconButton, StarRating } from \"../../index\";\nimport { mergeClassName } from \"../../utils/merge-class-name\";\nimport { Carousel } from \"../carousel/carousel\";\nimport { SectionHeader } from \"../section-header/section-header\";\nimport styles from \"./testimonials.module.css\";\nimport { useTestimonialsLabels } from \"./use-testimonials-labels\";\n\nexport type TestimonialsVideoPosition = \"start\" | \"end\";\n\n/** The video's other props only make sense with a video. */\ntype TestimonialsVideo =\n | {\n /**\n * A video of the featured customer, shown as a square still with a\n * play button that opens it in a dialog, with sound and controls.\n */\n videoSrc: string;\n /**\n * The still to show. Left out, the video's first frame is shown, which\n * loads the start of the video with the page.\n */\n videoPoster?: string;\n /**\n * Captions for the video, a WebVTT file in the `LeaProvider` language.\n * A video in which people speak needs them.\n */\n videoCaptions?: string;\n /**\n * Where the video sits from 768px: `\"start\"` (on the left of the quote)\n * or `\"end\"`. Below 768px it is always above the quote.\n * @default \"start\"\n */\n videoPosition?: TestimonialsVideoPosition;\n }\n | {\n videoSrc?: undefined;\n videoPoster?: undefined;\n videoCaptions?: undefined;\n videoPosition?: undefined;\n };\n\n/** A subtitle goes under a title. */\ntype TestimonialsHeader =\n | {\n /** The title of the section. It also names the carousel. */\n title: ReactNode;\n /**\n * The element for the title, an `<h2>` by default, as on\n * `SectionHeader`.\n */\n titleRender?: ReactElement;\n /** The text under the title. */\n subtitle?: ReactNode;\n }\n | { title?: undefined; titleRender?: undefined; subtitle?: undefined };\n\nexport type TestimonialsProps = Omit<\n useRender.ComponentProps<\"div\">,\n \"ref\" | \"title\" | \"children\"\n> &\n TestimonialsHeader &\n TestimonialsVideo & {\n /** The rendered element: the `<div>`, or the element passed to `render`. */\n ref?: Ref<HTMLElement>;\n /**\n * What the featured customer said, without quotation marks: the section\n * adds them. Shown large beside the video. A string becomes one\n * paragraph; pass `<p>` elements for several.\n */\n quote?: ReactNode;\n /** Who said the featured quote. */\n name?: ReactNode;\n /** A line under the name, such as the position and the company. */\n meta?: ReactNode;\n /** Stars above the featured quote, from 0 to 5. Left out, no stars. */\n rating?: number;\n /** The reviews in the carousel: `TestimonialCard`s. */\n children?: ReactNode;\n };\n\n/**\n * The website's block of customer reviews: a centred title, a featured\n * customer with a video, and a carousel of `TestimonialCard`s, as on the home\n * page of leabank.no.\n *\n * ```tsx\n * <Section surface=\"subtle\">\n * <Testimonials\n * title=\"Kundeanmeldelser\"\n * subtitle=\"Se hva andre synes om oss\"\n * rating={5}\n * quote=\"Lea Bank gjorde det enkelt å samle lånene mine.\"\n * name=\"Marte Olsen\"\n * meta=\"Kunde, Oslo\"\n * videoSrc=\"/video/marte.mp4\"\n * videoPoster=\"/video/marte.jpg\"\n * >\n * <TestimonialCard rating={5} quote=\"…\" name=\"Kari Nordmann\" meta=\"Kunde, bytt.no\" />\n * <TestimonialCard rating={5} quote=\"…\" name=\"Ola Hansen\" meta=\"Kunde, bytt.no\" />\n * </Testimonials>\n * </Section>\n * ```\n *\n * The parts are 64px apart. From 768px the video (a square with 64px\n * corners, dimmed under its play button) and the featured quote share the\n * width, 64px apart; below 768px the video is on top. The play button,\n * named \"Spill av video\", opens the video in a dialog with its controls and\n * captions, and the focus comes back to it when the dialog closes. The\n * reviews are a `Carousel`, named by the title (or \"Kundeomtaler\" without\n * one). It takes the text colour of its section; the carousel's controls\n * are made for the light surfaces.\n */\nexport function Testimonials({\n title,\n titleRender,\n subtitle,\n quote,\n name,\n meta,\n rating,\n videoSrc,\n videoPoster,\n videoCaptions,\n videoPosition = \"start\",\n render,\n ref,\n className,\n children,\n ...rest\n}: TestimonialsProps) {\n const labels = useTestimonialsLabels();\n const titleId = useId();\n const hasQuote = quote != null || name != null;\n const hasCards = Children.toArray(children).length > 0;\n\n return useRender({\n render,\n ref,\n defaultTagName: \"div\",\n props: {\n ...rest,\n className: mergeClassName(styles.Testimonials, className)({}),\n children: (\n <>\n {title != null && (\n <SectionHeader\n title={title}\n titleRender={cloneElement(titleRender ?? <h2 />, {\n id: titleId,\n })}\n subtitle={subtitle}\n />\n )}\n {(hasQuote || videoSrc != null) && (\n <div\n className={styles.Featured}\n data-video={videoSrc != null || undefined}\n data-video-position={videoPosition}\n >\n {hasQuote && (\n <figure className={styles.Quote}>\n {rating != null && <StarRating rating={rating} />}\n {quote != null && (\n <blockquote className={styles.Blockquote}>\n {typeof quote === \"string\" ||\n typeof quote === \"number\" ? (\n <p>{quote}</p>\n ) : (\n quote\n )}\n </blockquote>\n )}\n {(name != null || meta != null) && (\n <figcaption className={styles.Person}>\n {name != null && (\n <span className={styles.Name}>{name}</span>\n )}\n {meta != null && (\n <span className={styles.Meta}>{meta}</span>\n )}\n </figcaption>\n )}\n </figure>\n )}\n {videoSrc != null && (\n <div className={styles.Video}>\n {videoPoster != null ? (\n <img className={styles.Still} src={videoPoster} alt=\"\" />\n ) : (\n // The first frame as the still: a fragment just past the\n // start, which Safari also draws with `preload=\"metadata\"`.\n <video\n className={styles.Still}\n src={`${videoSrc}#t=0.1`}\n preload=\"metadata\"\n muted\n playsInline\n aria-hidden\n tabIndex={-1}\n />\n )}\n <Dialog.Root>\n <Dialog.Trigger\n render={\n <IconButton\n variant=\"elevated\"\n color=\"secondary\"\n aria-label={labels.play}\n className={styles.Play}\n >\n <Play aria-hidden />\n </IconButton>\n }\n />\n <Dialog.Portal>\n <Dialog.Backdrop />\n <Dialog.Viewport className={styles.DialogViewport}>\n <Dialog.Popup\n size=\"lg\"\n aria-label={labels.video}\n className={styles.DialogPopup}\n >\n <Dialog.Close\n render={\n <IconButton\n variant=\"elevated\"\n color=\"secondary\"\n aria-label={labels.close}\n className={styles.Close}\n >\n <X aria-hidden />\n </IconButton>\n }\n />\n <video\n className={styles.DialogVideo}\n src={videoSrc}\n controls\n autoPlay\n playsInline\n >\n {videoCaptions != null && (\n <track\n kind=\"captions\"\n src={videoCaptions}\n srcLang={labels.captionsLanguage}\n label={labels.captionsLabel}\n default\n />\n )}\n </video>\n </Dialog.Popup>\n </Dialog.Viewport>\n </Dialog.Portal>\n </Dialog.Root>\n </div>\n )}\n </div>\n )}\n {hasCards && (\n <Carousel\n {...(title != null\n ? { \"aria-labelledby\": titleId }\n : { \"aria-label\": labels.reviews })}\n >\n {children}\n </Carousel>\n )}\n </>\n ),\n },\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"website.cjs","names":[],"sources":["../src/website/paragraphs.tsx","../src/website/alert-banner/alert-banner.module.css","../src/website/alert-banner/alert-banner.tsx","../src/utils/merge-class-name.ts","../src/website/brand-letters/brand-letters.module.css","../src/website/brand-letters/use-brand-letters-labels.ts","../src/website/brand-letters/brand-letters.tsx","../src/website/calculator/calculator.module.css","../src/website/calculator/calculator.tsx","../src/website/carousel/carousel.module.css","../src/theme/tokens/transitions.ts","../src/website/carousel/use-carousel.ts","../src/website/carousel/use-carousel-labels.ts","../src/website/carousel/carousel.tsx","../src/website/cta-banner/cta-banner.module.css","../src/website/cta-banner/cta-banner.tsx","../src/website/expanding-cards/expanding-cards-context.ts","../src/website/expanding-cards/expanding-cards.module.css","../src/website/expanding-cards/expanding-cards.tsx","../src/website/expanding-cards/expanding-cards-item.module.css","../src/website/expanding-cards/expanding-cards-item.tsx","../src/website/hero/hero.module.css","../src/website/hero/hero.tsx","../src/website/hero-service-grid/hero-service-grid.module.css","../src/website/hero-service-grid/hero-service-grid.tsx","../src/website/navigation-grid/navigation-grid.module.css","../src/website/navigation-grid/navigation-grid.tsx","../src/website/partner-list/partner-list.module.css","../src/website/partner-list/use-partner-list-labels.ts","../src/website/partner-list/partner-list.tsx","../src/website/payment-feature/payment-feature.module.css","../src/website/payment-feature/payment-feature.tsx","../src/website/promo-grid/promo-grid.module.css","../src/website/promo-grid/promo-grid.tsx","../src/website/section/section.module.css","../src/website/section/section.tsx","../src/website/section-header/section-header.module.css","../src/website/section-header/section-header.tsx","../src/website/service-card-grid/service-card-grid.module.css","../src/website/service-card-grid/service-card-grid.tsx","../src/website/arrow-reveal-link.module.css","../src/website/arrow-reveal-link.tsx","../src/website/side-nav/side-nav.module.css","../src/website/side-nav/use-side-nav-labels.ts","../src/website/side-nav/side-nav.tsx","../src/website/site-footer/flags.tsx","../src/website/site-header-link.tsx","../src/website/site-footer/site-footer.module.css","../src/website/site-footer/use-site-footer-labels.ts","../src/website/use-site-header-labels.ts","../src/website/site-footer/site-footer.tsx","../src/website/site-header/site-header.module.css","../src/website/site-header/site-header-menu.tsx","../src/website/site-header/site-header.tsx","../src/website/split-content/split-content.module.css","../src/website/split-content/split-content.tsx","../src/website/stats/stats.module.css","../src/website/stats/stats.tsx","../src/website/testimonials/testimonials.module.css","../src/website/testimonials/use-testimonials-labels.ts","../src/website/testimonials/testimonials.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\n\n/**\n * A text prop of the sections: a string or a number becomes one `<p>`, and\n * anything else (paragraphs the page passes, rich text from the CMS) goes in\n * a `<div>`, so a `<p>` never holds another. Internal.\n */\nexport function Paragraphs({\n className,\n id,\n children,\n}: {\n className?: string;\n id?: string;\n children: ReactNode;\n}) {\n return typeof children === \"string\" || typeof children === \"number\" ? (\n <p className={className} id={id}>\n {children}\n </p>\n ) : (\n <div className={className} id={id}>\n {children}\n </div>\n );\n}\n","/*\n * The website's `AlertBanner`: a 48px icon and a column of title and text,\n * 32 apart (16 below 768px), from the top. The title and text are the\n * `Section`'s text colour; the icon is the `color` prop's status colour.\n *\n * Each colour sets the icon for the light surfaces and for the dark ones\n * (`contrast`, `contrast-accent`): the status token where it reaches 3:1 on\n * every surface of that kind, else the nearest step of its scale that does.\n * The palette has no status step for a dark surface, hence the raw steps.\n */\n.AlertBanner {\n /* error: 4.2 to 5.0 on the light surfaces. */\n --alert-banner-icon-light: var(--lea-palette-status-error-base);\n /* `status.error.base` is 2.7 on `contrast-accent`; this is 3.7 and 5.3. */\n --alert-banner-icon-dark: var(--lea-color-red-400);\n --alert-banner-icon: var(--alert-banner-icon-light);\n\n display: flex;\n align-items: flex-start;\n gap: var(--lea-spacing-2);\n}\n\n.AlertBanner[data-color=\"warning\"] {\n /* `status.warning.base` is 2.0 on white; this is 3.2 to 3.9. */\n --alert-banner-icon-light: var(--lea-color-amber-600);\n --alert-banner-icon-dark: var(--lea-palette-status-warning-base);\n}\n\n.AlertBanner[data-color=\"info\"] {\n /* 3.3 to 4.8 on all five surfaces. */\n --alert-banner-icon-light: var(--lea-palette-status-info-base);\n --alert-banner-icon-dark: var(--lea-palette-status-info-base);\n}\n\n.AlertBanner[data-color=\"success\"] {\n /* `status.success.base` is 1.9 on white; this is 3.3 to 4.0. */\n --alert-banner-icon-light: var(--lea-color-teal-600);\n --alert-banner-icon-dark: var(--lea-palette-status-success-base);\n}\n\n.AlertBanner[data-color=\"inherit\"] {\n --alert-banner-icon-light: currentcolor;\n --alert-banner-icon-dark: currentcolor;\n}\n\n/*\n * On a dark `Section` or card, and back to the light colours on a light card\n * inside one. A white box that does not set `data-surface` (`Panel`) inside\n * a dark section keeps the dark step.\n */\n:is([data-surface=\"contrast\"], [data-surface=\"contrast-accent\"]) .AlertBanner {\n --alert-banner-icon: var(--alert-banner-icon-dark);\n}\n\n:is([data-surface=\"contrast\"], [data-surface=\"contrast-accent\"])\n :is([data-surface=\"base\"], [data-surface=\"subtle\"], [data-surface=\"muted\"])\n .AlertBanner {\n --alert-banner-icon: var(--alert-banner-icon-light);\n}\n\n@media (min-width: 768px) {\n .AlertBanner {\n gap: var(--lea-spacing-4);\n }\n}\n\n.Icon {\n display: flex;\n width: var(--lea-spacing-6);\n height: var(--lea-spacing-6);\n flex-shrink: 0;\n color: var(--alert-banner-icon);\n}\n\n.Icon > :is(svg, img) {\n width: 100%;\n height: 100%;\n}\n\n/* 2px between title and text, the website's: a hair, off the scale. */\n.Content {\n display: flex;\n min-width: 0;\n flex: 1;\n flex-direction: column;\n gap: calc(var(--lea-spacing) * 0.25);\n}\n\n/* 14px semibold, as `SiteHeader`'s group titles. */\n.Title {\n margin: 0;\n font-size: var(--lea-font-step-minus-1);\n font-weight: var(--lea-font-weight-semibold);\n line-height: var(--lea-line-height-tight);\n}\n\n.Text {\n margin: 0;\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.Text p {\n margin: 0;\n}\n\n.Text p + p {\n margin-block-start: var(--lea-spacing-1);\n}\n\n/* The CMS's bare links; a Paloma `Link` keeps its own look. */\n.Text a:not([class]) {\n color: inherit;\n text-decoration: underline;\n}\n\n.Text a:not([class]):focus-visible {\n border-radius: var(--lea-radius-sm);\n outline: 2px solid currentcolor;\n outline-offset: 0.125rem;\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { TriangleAlert } from \"lucide-react\";\nimport type { ComponentProps, ReactElement, ReactNode } from \"react\";\n\nimport type { ColorRoles } from \"../../components/color-roles\";\nimport { Paragraphs } from \"../paragraphs\";\nimport styles from \"./alert-banner.module.css\";\n\n/**\n * The colour of the icon: a status, or `inherit` for the text colour of the\n * section around it.\n */\nexport type AlertBannerColor =\n | ColorRoles<\"error\" | \"warning\" | \"info\" | \"success\">\n | \"inherit\";\n\n/** A title, a text or both: a banner with neither does not compile. */\ntype AlertBannerContent =\n | {\n /** The title, 14px semibold, a heading (`titleRender`). */\n title: ReactNode;\n /**\n * The notice. A string becomes a `<p>`; anything else (the CMS's\n * paragraphs, with links) goes in a `<div>`.\n */\n text?: ReactNode;\n }\n | { title?: ReactNode; text: ReactNode };\n\nexport type AlertBannerProps = Omit<\n ComponentProps<\"div\">,\n \"children\" | \"title\" | \"color\"\n> &\n AlertBannerContent & {\n /**\n * The element of the title.\n * @default <h2 />\n */\n titleRender?: ReactElement;\n /**\n * The picture before the text, 48px, decoration: the CMS's image with\n * `alt=\"\"`, or an icon. Left out, Lucide's `TriangleAlert`; `null` for\n * none.\n */\n icon?: ReactNode;\n /**\n * The colour of the icon: `\"error\"` (red), `\"warning\"` (amber), `\"info\"`\n * (blue), `\"success\"` (teal), or `\"inherit\"` for the section's text\n * colour. The title and the text keep the section's colour whichever it\n * is. On a dark section (`surface` `contrast` or `contrast-accent`) the\n * icon takes a lighter step of the same colour. An image passed as `icon`\n * keeps its own colours.\n * @default \"error\"\n */\n color?: AlertBannerColor;\n };\n\nfunction Title({\n render,\n children,\n}: {\n render?: ReactElement;\n children: ReactNode;\n}) {\n return useRender({\n render,\n defaultTagName: \"h2\",\n props: { className: styles.Title, children },\n });\n}\n\n/**\n * The website's notice (`AlertBanner`, the global one at the foot of every\n * page and the `sections.alert-banner` block): a 48px icon, red unless\n * `color` says otherwise, beside a short title and a text in the colour of\n * the `Section` it sits in.\n *\n * ```tsx\n * <Section surface=\"muted\" spacingTop=\"sm\" spacingBottom=\"sm\">\n * <AlertBanner\n * title=\"Viktig informasjon\"\n * text=\"Lea Bank ringer deg aldri for å be om BankID, passord eller kortopplysninger.\"\n * />\n * </Section>\n * ```\n *\n * A notice that stays on the page, not a message that has just appeared: it\n * has no `role=\"alert\"` and is not announced, and it has no close button.\n * For a status message over the page, use `GlobalAlert`. The icon and the\n * text are 32px apart (16 below 768px); title and text are 14px.\n */\nexport function AlertBanner({\n title,\n text,\n titleRender,\n icon,\n color = \"error\",\n className,\n ...rest\n}: AlertBannerProps) {\n const shownIcon = icon === undefined ? <TriangleAlert /> : icon;\n\n return (\n <div\n {...rest}\n className={[styles.AlertBanner, className].filter(Boolean).join(\" \")}\n data-color={color}\n >\n {shownIcon != null && shownIcon !== false && (\n <span aria-hidden className={styles.Icon}>\n {shownIcon}\n </span>\n )}\n <div className={styles.Content}>\n {title != null && <Title render={titleRender}>{title}</Title>}\n {text != null && (\n <Paragraphs className={styles.Text}>{text}</Paragraphs>\n )}\n </div>\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 LeaBrandVideoGrid: three cells side by side with no gap,\n * each 437 : 510 and cut to a letter of the logo (L, E, A) with a mask from\n * the website's letter shapes (lea-letter-paths.ts, viewBox 0 0 437 510; the\n * L has a hole, so its path is even-odd). A video fills each letter,\n * near-black until its first frame. The pause button sits centred 24px under\n * the letters, as on `VideoPlayer`.\n */\n.BrandLetters {\n display: flex;\n min-width: 0;\n flex-direction: column;\n align-items: center;\n gap: var(--lea-spacing-3);\n}\n\n.Letters {\n display: grid;\n width: 100%;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n}\n\n.Letter {\n aspect-ratio: 437 / 510;\n overflow: hidden;\n background-color: var(--lea-palette-bg-contrast);\n -webkit-mask-repeat: no-repeat;\n mask-repeat: no-repeat;\n -webkit-mask-size: 100% 100%;\n mask-size: 100% 100%;\n}\n\n.Letter[data-letter=\"L\"] {\n -webkit-mask-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 437 510' preserveAspectRatio='none'%3E%3Cpath fill-rule='evenodd' d='M0 410.002L2.00774e-05 91.8634C2.45127e-05 41.1287 41.1287 -4.43537e-06 91.8634 0C142.598 4.43538e-06 183.727 41.1286 183.727 91.8634V99.8089C183.831 44.6692 228.563 0.0019483 283.727 0.00195312L336.615 0.00195775C391.843 0.00196258 436.615 44.7735 436.615 100.002V221.524C436.615 276.688 391.948 321.419 336.808 321.523H342.376C394.423 321.523 436.615 363.716 436.615 415.763C436.615 467.809 394.423 510.002 342.376 510.002H99.9999C44.7714 510.002 -4.82822e-06 465.23 0 410.002ZM283.53 321.523C228.458 321.418 183.833 276.793 183.727 221.721V321.522L283.53 321.523Z'/%3E%3C/svg%3E\");\n mask-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 437 510' preserveAspectRatio='none'%3E%3Cpath fill-rule='evenodd' d='M0 410.002L2.00774e-05 91.8634C2.45127e-05 41.1287 41.1287 -4.43537e-06 91.8634 0C142.598 4.43538e-06 183.727 41.1286 183.727 91.8634V99.8089C183.831 44.6692 228.563 0.0019483 283.727 0.00195312L336.615 0.00195775C391.843 0.00196258 436.615 44.7735 436.615 100.002V221.524C436.615 276.688 391.948 321.419 336.808 321.523H342.376C394.423 321.523 436.615 363.716 436.615 415.763C436.615 467.809 394.423 510.002 342.376 510.002H99.9999C44.7714 510.002 -4.82822e-06 465.23 0 410.002ZM283.53 321.523C228.458 321.418 183.833 276.793 183.727 221.721V321.522L283.53 321.523Z'/%3E%3C/svg%3E\");\n}\n\n.Letter[data-letter=\"E\"] {\n -webkit-mask-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 437 510' preserveAspectRatio='none'%3E%3Cpath d='M100 0C44.7715 0 0 44.7715 0 100V170V310C0 326.569 13.4314 340 30 340H0V510H351.615C398.559 510 436.615 471.944 436.615 425C436.615 378.056 398.559 340 351.615 340C398.559 340 436.615 301.944 436.615 255C436.615 208.056 398.559 170 351.615 170C398.559 170 436.615 131.944 436.615 85C436.615 38.0558 398.559 0 351.615 0H100Z'/%3E%3C/svg%3E\");\n mask-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 437 510' preserveAspectRatio='none'%3E%3Cpath d='M100 0C44.7715 0 0 44.7715 0 100V170V310C0 326.569 13.4314 340 30 340H0V510H351.615C398.559 510 436.615 471.944 436.615 425C436.615 378.056 398.559 340 351.615 340C398.559 340 436.615 301.944 436.615 255C436.615 208.056 398.559 170 351.615 170C398.559 170 436.615 131.944 436.615 85C436.615 38.0558 398.559 0 351.615 0H100Z'/%3E%3C/svg%3E\");\n}\n\n.Letter[data-letter=\"A\"] {\n -webkit-mask-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 437 510' preserveAspectRatio='none'%3E%3Cpath d='M436.615 170V100C436.615 44.7715 391.843 0 336.615 0H85C38.0558 0 0 38.0558 0 85C0 131.944 38.0557 170 84.9999 170H100C44.7715 170 0 214.772 0 270V410C0 465.228 44.7714 510 99.9999 510H336.615C391.843 510 436.615 465.228 436.615 410V170Z'/%3E%3C/svg%3E\");\n mask-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 437 510' preserveAspectRatio='none'%3E%3Cpath d='M436.615 170V100C436.615 44.7715 391.843 0 336.615 0H85C38.0558 0 0 38.0558 0 85C0 131.944 38.0557 170 84.9999 170H100C44.7715 170 0 214.772 0 270V410C0 465.228 44.7714 510 99.9999 510H336.615C391.843 510 436.615 465.228 436.615 410V170Z'/%3E%3C/svg%3E\");\n}\n\n.Video {\n display: block;\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n","import { createTranslation } from \"../../index\";\n\n/** The names of `BrandLetters`' button, for paused and playing videos. */\nexport const useBrandLettersLabels = createTranslation({\n play: {\n no: \"Spill av videoene\",\n se: \"Spela upp videorna\",\n fi: \"Toista videot\",\n es: \"Reproducir los vídeos\",\n },\n pause: {\n no: \"Sett videoene på pause\",\n se: \"Pausa videorna\",\n fi: \"Keskeytä videot\",\n es: \"Pausar los vídeos\",\n },\n});\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { Pause, Play } from \"lucide-react\";\nimport { useEffect, useRef, useState, type Ref } from \"react\";\n\nimport { IconButton } from \"../../index\";\nimport { mergeClassName } from \"../../utils/merge-class-name\";\nimport styles from \"./brand-letters.module.css\";\nimport { useBrandLettersLabels } from \"./use-brand-letters-labels\";\n\nconst REDUCED_MOTION = \"(prefers-reduced-motion: reduce)\";\nconst LETTERS = [\"L\", \"E\", \"A\"] as const;\n\n/** The video in one letter. */\nexport type BrandLettersVideo = {\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\nexport type BrandLettersProps = 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 /** The three videos, in the L, the E and the A. */\n videos: readonly [BrandLettersVideo, BrandLettersVideo, BrandLettersVideo];\n};\n\n/**\n * The letters L, E and A of the logo side by side, each filled with a silent\n * video in a loop, as the brand block of leabank.no.\n *\n * ```tsx\n * <Section>\n * <BrandLetters\n * videos={[\n * { src: \"/video/l.mp4\", poster: \"/video/l.jpg\" },\n * { src: \"/video/e.mp4\", poster: \"/video/e.jpg\" },\n * { src: \"/video/a.mp4\", poster: \"/video/a.jpg\" },\n * ]}\n * />\n * </Section>\n * ```\n *\n * The letters share the width they are given, each 437 : 510. The videos\n * start on their own once the page has loaded, unless the visitor has asked\n * for reduced motion: then they wait for the button. One button under the\n * letters pauses and plays all three, named \"Sett videoene på pause\" or\n * \"Spill av videoene\" in the `LeaProvider` language, since moving content\n * that plays on its own needs a way to stop it. The videos are decoration\n * and hidden from screen readers.\n */\nexport function BrandLetters({\n videos,\n render,\n ref,\n className,\n ...rest\n}: BrandLettersProps) {\n const labels = useBrandLettersLabels();\n const videoRefs = useRef<(HTMLVideoElement | null)[]>([]);\n const [playing, setPlaying] = useState(false);\n\n const each = (action: (video: HTMLVideoElement) => void) => {\n for (const video of videoRefs.current) if (video) action(video);\n };\n\n const play = (video: HTMLVideoElement) => {\n video.muted = true;\n video.play().catch(() => {});\n };\n\n useEffect(() => {\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 videos paused, with the button to start them.\n if (!reducedMotion.matches) each(play);\n\n const stopOnReducedMotion = (event: MediaQueryListEvent) => {\n if (event.matches) each((video) => video.pause());\n };\n\n reducedMotion.addEventListener(\"change\", stopOnReducedMotion);\n\n return () =>\n reducedMotion.removeEventListener(\"change\", stopOnReducedMotion);\n }, []);\n\n const update = () =>\n setPlaying(\n videoRefs.current.some((video) => video != null && !video.paused),\n );\n\n const toggle = () => {\n if (playing) each((video) => video.pause());\n else each(play);\n };\n\n return useRender({\n render,\n ref,\n defaultTagName: \"div\",\n props: {\n ...rest,\n className: mergeClassName(styles.BrandLetters, className)({}),\n children: (\n <>\n <div className={styles.Letters}>\n {LETTERS.map((letter, index) => (\n <div key={letter} className={styles.Letter} data-letter={letter}>\n <video\n ref={(video) => {\n videoRefs.current[index] = video;\n }}\n className={styles.Video}\n src={videos[index].src}\n poster={videos[index].poster}\n muted\n loop\n playsInline\n aria-hidden\n onPlay={update}\n onPause={update}\n />\n </div>\n ))}\n </div>\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 </>\n ),\n },\n });\n}\n","/*\n * The website's Calculator: the header, then 80px (32px below 768px) to the\n * body. From 1024px the body is the panel and a 280px summary column, 64px\n * apart; below, one column 32px apart. The panel is black at 30 % over the\n * section (`color-mix` on `bg.contrast`; there is no scrim token), `5xl`\n * corners, 32px inside, the sliders 16px apart. The summary: the results\n * 16px apart (the label `purple-100`, the website's light purple, the value\n * semibold, 8px apart), right-aligned from 1024px; the action at the foot\n * of the column with the price example 20px under it. No colour of its\n * own: on leabank.no it sits on purple or black, where the text is white.\n */\n.Calculator {\n display: flex;\n min-width: 0;\n flex-direction: column;\n /* The website's 80px. */\n gap: calc(var(--lea-spacing) * 10);\n}\n\n@media (max-width: 767.95px) {\n .Calculator {\n gap: var(--lea-spacing-4);\n }\n}\n\n.Body {\n display: grid;\n gap: var(--lea-spacing-4);\n grid-template-columns: minmax(0, 1fr);\n}\n\n@media (min-width: 1024px) {\n .Body {\n align-items: start;\n gap: var(--lea-spacing-8);\n /* The website's 280px summary column. */\n grid-template-columns: minmax(0, 1fr) calc(var(--lea-spacing) * 35);\n }\n}\n\n.Panel {\n display: flex;\n min-width: 0;\n flex-direction: column;\n padding: var(--lea-spacing-4);\n border-radius: var(--lea-radius-5xl);\n background-color: color-mix(\n in srgb,\n var(--lea-palette-bg-contrast) 30%,\n transparent\n );\n gap: var(--lea-spacing-2);\n}\n\n.Summary {\n display: flex;\n min-width: 0;\n flex-direction: column;\n align-items: flex-start;\n align-self: stretch;\n gap: var(--lea-spacing-4);\n}\n\n.Results {\n display: flex;\n flex-direction: column;\n margin: 0;\n gap: var(--lea-spacing-2);\n}\n\n.Result {\n display: flex;\n flex-direction: column;\n gap: var(--lea-spacing-1);\n}\n\n.Label,\n.Value {\n margin: 0;\n font-size: var(--lea-text-body-md-font-size);\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 color: var(--lea-color-purple-100);\n font-weight: var(--lea-text-body-md-font-weight);\n}\n\n.Value {\n font-variant-numeric: tabular-nums;\n font-weight: var(--lea-font-weight-semibold);\n}\n\n.Actions {\n display: flex;\n flex-direction: column;\n align-items: inherit;\n margin-block-start: auto;\n gap: var(--lea-spacing-2-5);\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@media (min-width: 1024px) {\n .Summary {\n align-items: flex-end;\n text-align: end;\n }\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport type { ReactNode, Ref } from \"react\";\n\nimport { mergeClassName } from \"../../utils/merge-class-name\";\nimport styles from \"./calculator.module.css\";\n\n/** A figure the calculator works out, shown in the summary column. */\nexport type CalculatorResult = {\n /** What the figure is: \"Beregnet sparesaldo\". */\n label: ReactNode;\n /** The figure, written out: \"399 418 kr\". */\n value: ReactNode;\n};\n\nexport type CalculatorProps = Omit<\n useRender.ComponentProps<\"div\">,\n \"ref\" | \"children\" | \"results\"\n> & {\n /** The rendered element: the `<div>`, or the element passed to `render`. */\n ref?: Ref<HTMLElement>;\n /** The heading above the calculator: a `SectionHeader`. */\n header?: ReactNode;\n /**\n * The inputs, in the panel: `Slider`s with `color=\"contrast\"`, each with\n * its `label`, `min`, `max`, `step`, and `format` and `locale` or, for a\n * unit such as \"kr/måned\", `formatValue`.\n */\n children: ReactNode;\n /**\n * The figures worked out from the inputs, at the top of the summary\n * column. The page does the sums: one result for savings, three for\n * refinancing on leabank.no. A change is announced to screen readers.\n */\n results: readonly CalculatorResult[];\n /** The call to action under the results: an `ActionButton`. */\n action?: ReactNode;\n /** The price example under the action, in the small print. */\n priceExample?: ReactNode;\n};\n\n/**\n * The website's savings and refinancing calculator: a heading, a panel of\n * sliders and a column with the results and a call to action, as on\n * leabank.no/sparing and leabank.no/lan. It lays the parts out; the inputs\n * are `Slider`s and the sums stay with the page.\n *\n * ```tsx\n * <Section surface=\"contrast-accent\">\n * <Calculator\n * header={<SectionHeader eyebrow=\"Sparekalkulator\" title=\"Se hvor mye sparingen din kan vokse\" />}\n * results={[{ label: \"Beregnet sparesaldo\", value: format(balance) }]}\n * action={<ActionButton render={<NextLink href=\"/sparing/apne-konto\" />}>Opprett konto</ActionButton>}\n * >\n * <Slider color=\"contrast\" label=\"Startbeløp\" min={0} max={1000000} step={1000} value={start} onValueChange={setStart} locale=\"nb-NO\" format={{ style: \"currency\", currency: \"NOK\", maximumFractionDigits: 0 }} />\n * …\n * </Calculator>\n * </Section>\n * ```\n *\n * The header is 80px above the rest (32px below 768px). From 1024px the\n * panel and a 280px column sit side by side, 64px apart: the results at the\n * top of the column, right-aligned, the action and the price example at its\n * foot; below 1024px one under the other, left-aligned, 32px apart. The\n * panel is black at 30 % over the section, with `5xl` corners and 32px\n * inside, the sliders 16px apart. It has no colour of its own and is made\n * for the dark surfaces, `contrast` and `contrast-accent`.\n */\nexport function Calculator({\n header,\n results,\n action,\n priceExample,\n render,\n ref,\n className,\n children,\n ...rest\n}: CalculatorProps) {\n return useRender({\n render,\n ref,\n defaultTagName: \"div\",\n props: {\n ...rest,\n className: mergeClassName(styles.Calculator, className)({}),\n children: (\n <>\n {header}\n <div className={styles.Body}>\n <div className={styles.Panel}>{children}</div>\n <div className={styles.Summary}>\n {results.length > 0 && (\n // Announced when a figure changes, as the sliders move.\n <dl className={styles.Results} aria-live=\"polite\">\n {results.map((result, index) => (\n <div key={index} className={styles.Result}>\n <dt className={styles.Label}>{result.label}</dt>\n <dd className={styles.Value}>{result.value}</dd>\n </div>\n ))}\n </dl>\n )}\n {(action != null || priceExample != null) && (\n <div className={styles.Actions}>\n {action}\n {priceExample != null && (\n <p className={styles.Price}>{priceExample}</p>\n )}\n </div>\n )}\n </div>\n </div>\n </>\n ),\n },\n });\n}\n","/*\n * The website's carousel of reviews (react-slick there, CSS scroll snap\n * here): 2.2 slides in view from 1024px and 1.15 below, 16px apart, each\n * `(view + gap) / per-view - gap` wide as react-slick sizes them (588px of\n * 1312, 310 of 358). The slides stop at their start and stretch to the\n * tallest. The controls sit 32px under the track, 32px apart: the elevated\n * buttons (40px) and the dots between them, as on `PartnerList`.\n *\n * The edges fade over 48px into the colour behind the carousel: the colour\n * of its `Section` (`--section-bg`), white outside one. A fade shows only\n * where there is more to scroll, so the first slide is clear at the start\n * (the website fades it too).\n */\n.Carousel {\n --carousel-gap: var(--lea-spacing-2);\n --carousel-per-view: 1.15;\n --carousel-fade-color: var(--section-bg, var(--lea-palette-bg-base));\n\n display: flex;\n min-width: 0;\n flex-direction: column;\n gap: var(--lea-spacing-4);\n}\n\n@media (min-width: 1024px) {\n .Carousel {\n --carousel-per-view: 2.2;\n }\n}\n\n.Viewport {\n position: relative;\n min-width: 0;\n}\n\n.Viewport::before,\n.Viewport::after {\n position: absolute;\n z-index: 1;\n width: var(--lea-spacing-6);\n content: \"\";\n inset-block: 0;\n pointer-events: none;\n transition: opacity var(--lea-duration-fast) var(--lea-easing-standard);\n}\n\n.Viewport::before {\n background: linear-gradient(\n to right,\n var(--carousel-fade-color),\n transparent\n );\n inset-inline-start: 0;\n}\n\n.Viewport::after {\n background: linear-gradient(to left, var(--carousel-fade-color), transparent);\n inset-inline-end: 0;\n}\n\n.Viewport[data-at-start]::before,\n.Viewport[data-at-end]::after {\n opacity: 0;\n}\n\n.Track {\n position: relative;\n display: grid;\n padding: 0;\n margin: 0;\n gap: var(--carousel-gap);\n grid-auto-columns: calc(\n (100% + var(--carousel-gap)) / var(--carousel-per-view) -\n var(--carousel-gap)\n );\n grid-auto-flow: column;\n list-style: none;\n overflow-x: auto;\n overscroll-behavior-x: contain;\n scroll-snap-type: x mandatory;\n scrollbar-width: none;\n}\n\n.Track::-webkit-scrollbar {\n display: none;\n}\n\n/* The ring in the section's text colour, so it shows on any surface. */\n.Track:focus-visible {\n outline: 2px solid currentColor;\n outline-offset: 0.125rem;\n}\n\n.Slide {\n display: grid;\n min-width: 0;\n scroll-snap-align: start;\n}\n\n.Controls {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--lea-spacing-4);\n}\n\n/* \"Side 2 av 3\", for screen readers when the carousel moves. */\n.VisuallyHidden {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Viewport::before,\n .Viewport::after {\n transition-duration: 0.01ms;\n }\n}\n","export const easings = {\n standard: \"ease-in-out\",\n spring: \"cubic-bezier(0.34, 1.56, 0.64, 1)\",\n} as const;\n\nexport const durations = {\n fast: \"0.15s\",\n normal: \"0.2s\",\n emphasis: \"0.3s\",\n} as const;\n\nexport const transition = (\n properties: string[],\n duration: string = durations.normal,\n easing: string = easings.standard,\n) => properties.map((p) => `${p} ${duration} ${easing}`).join(\", \");\n","\"use client\";\n\nimport {\n useCallback,\n useEffect,\n useRef,\n useState,\n type RefObject,\n} from \"react\";\n\nimport { durations } from \"../../theme/tokens/transitions\";\n\nconst REDUCED_MOTION = \"(prefers-reduced-motion: reduce)\";\n\n/** How long the track has to rest before a page counts as reached, in ms. */\nconst SETTLE_DELAY = 150;\n\n/**\n * How long the buttons and dots take to scroll to a place, in ms: twice\n * `emphasis`, as the expanding cards open. The browser's own smooth scroll\n * crossed most of a 604px place in a few frames, which read as a jump.\n */\nconst SCROLL_DURATION = parseFloat(durations.emphasis) * 1000;\n\n/**\n * The `standard` easing token, `ease-in-out`, which is\n * `cubic-bezier(0.42, 0, 0.58, 1)`: how far along the scroll is at a point in\n * its time, both from 0 to 1.\n */\nfunction easeInOut(time: number) {\n const bezier = (t: number, p1: number, p2: number) =>\n 3 * p1 * t * (1 - t) ** 2 + 3 * p2 * t ** 2 * (1 - t) + t ** 3;\n // The curve is given by a parameter, not by time: find the parameter at\n // this time, then the distance there.\n let low = 0;\n let high = 1;\n for (let step = 0; step < 20; step += 1) {\n const middle = (low + high) / 2;\n if (bezier(middle, 0.42, 0.58) < time) low = middle;\n else high = middle;\n }\n return bezier((low + high) / 2, 0, 1);\n}\n\nexport type CarouselState = {\n /** How many places the track can stop at: one per slide that can start it. */\n pageCount: number;\n /** The place the track is nearest to now, from 0. */\n page: number;\n /** The place the track came to rest at, from 0: what is announced. */\n settledPage: number;\n /** Whether the track is at its start, so nothing lies before the view. */\n atStart: boolean;\n /** Whether the track is at its end, so nothing lies after the view. */\n atEnd: boolean;\n /** Whether the slides are wider than the track, so it scrolls. */\n overflowing: boolean;\n /**\n * Whether the track has moved since it was shown, so that the place it\n * shows first is not announced on load.\n */\n moved: boolean;\n};\n\n/**\n * The state of a scroll-snap track, for `Carousel`: the places it can stop at\n * (a slide's start, or the end of the track for the last slides, which never\n * reach the start), the one it is at, and `goTo` to scroll to another. The\n * browser does the swiping and the scrolling by hand; `goTo` scrolls over\n * `SCROLL_DURATION` itself, since the browser's smooth scroll can't be timed.\n * Internal.\n *\n * Before it has measured (on the server, and in the first render), it\n * assumes the website's 2.2 slides from 1024px: one place per slide but the\n * last.\n */\nexport function useCarousel(\n trackRef: RefObject<HTMLElement | null>,\n slideCount: number,\n) {\n const positions = useRef<number[]>([0]);\n const settleTimer = useRef<ReturnType<typeof setTimeout>>(undefined);\n /** The scroll `goTo` is running: its next frame and where it ends. */\n const scroll = useRef<{ frame: number; to: number }>(undefined);\n const [state, setState] = useState<CarouselState>(() => ({\n pageCount: Math.max(1, slideCount - 1),\n page: 0,\n settledPage: 0,\n atStart: true,\n atEnd: slideCount <= 2,\n overflowing: slideCount > 2,\n moved: false,\n }));\n\n const read = useCallback(\n (settled: boolean, moved = false) => {\n const track = trackRef.current;\n if (!track) return;\n\n const left = track.scrollLeft;\n const max = Math.max(0, track.scrollWidth - track.clientWidth);\n const places = positions.current;\n let page = 0;\n for (let index = 1; index < places.length; index += 1) {\n if (Math.abs(places[index] - left) < Math.abs(places[page] - left)) {\n page = index;\n }\n }\n\n setState((previous) => {\n const next: CarouselState = {\n pageCount: places.length,\n page,\n settledPage: settled ? page : previous.settledPage,\n atStart: left <= 1,\n atEnd: left >= max - 1,\n overflowing: max > 1,\n moved: previous.moved || moved,\n };\n return (Object.keys(next) as (keyof CarouselState)[]).every(\n (key) => next[key] === previous[key],\n )\n ? previous\n : next;\n });\n },\n [trackRef],\n );\n\n const measure = useCallback(() => {\n const track = trackRef.current;\n if (!track) return;\n\n // A slide's start, measured from the first slide, as far as the track can\n // scroll: the last slides all stop at the end. Places less than a pixel\n // apart are one.\n const max = Math.max(0, track.scrollWidth - track.clientWidth);\n const slides = Array.from(track.children) as HTMLElement[];\n const origin = slides[0]?.offsetLeft ?? 0;\n const places: number[] = [];\n for (const slide of slides) {\n const left = Math.min(Math.max(0, slide.offsetLeft - origin), max);\n if (places.length === 0 || left - places[places.length - 1] > 1) {\n places.push(left);\n }\n }\n positions.current = places.length > 0 ? places : [0];\n read(true);\n }, [read, trackRef]);\n\n /** Ends the scroll `goTo` is running, where it is, and snaps again. */\n const stop = useCallback(() => {\n if (!scroll.current) return;\n cancelAnimationFrame(scroll.current.frame);\n scroll.current = undefined;\n trackRef.current?.style.removeProperty(\"scroll-snap-type\");\n }, [trackRef]);\n\n useEffect(() => {\n const track = trackRef.current;\n if (!track) return;\n\n measure();\n\n const onScroll = () => {\n read(false, true);\n clearTimeout(settleTimer.current);\n settleTimer.current = setTimeout(() => read(true), SETTLE_DELAY);\n };\n const resizeObserver = new ResizeObserver(measure);\n // A swipe, a wheel or a key takes over from a scroll the buttons started.\n const takeOver = [\"pointerdown\", \"touchstart\", \"wheel\", \"keydown\"];\n\n track.addEventListener(\"scroll\", onScroll, { passive: true });\n for (const type of takeOver) {\n track.addEventListener(type, stop, { passive: true });\n }\n resizeObserver.observe(track);\n\n return () => {\n track.removeEventListener(\"scroll\", onScroll);\n for (const type of takeOver) track.removeEventListener(type, stop);\n resizeObserver.disconnect();\n clearTimeout(settleTimer.current);\n stop();\n };\n }, [measure, read, slideCount, stop, trackRef]);\n\n const goTo = useCallback(\n (page: number) => {\n const track = trackRef.current;\n if (!track) return;\n\n const places = positions.current;\n const to = places[Math.min(places.length - 1, Math.max(0, page))];\n // Already on its way there: a second press doesn't start it again.\n if (scroll.current?.to === to) return;\n stop();\n\n const from = track.scrollLeft;\n if (\n window.matchMedia(REDUCED_MOTION).matches ||\n Math.abs(to - from) < 1\n ) {\n track.scrollTo({ left: to, behavior: \"instant\" });\n return;\n }\n\n // Each step would snap back to a slide, so snapping is off on the way.\n // \"instant\", so that an app's `scroll-behavior: smooth` doesn't smooth\n // every step.\n track.style.setProperty(\"scroll-snap-type\", \"none\");\n const start = performance.now();\n const step = (now: number) => {\n const time = Math.min(1, (now - start) / SCROLL_DURATION);\n track.scrollTo({\n left: from + (to - from) * easeInOut(time),\n behavior: \"instant\",\n });\n if (time < 1) {\n scroll.current = { frame: requestAnimationFrame(step), to };\n } else {\n scroll.current = undefined;\n track.style.removeProperty(\"scroll-snap-type\");\n }\n };\n scroll.current = { frame: requestAnimationFrame(step), to };\n },\n [stop, trackRef],\n );\n\n return { ...state, goTo };\n}\n","import { createTranslation } from \"../../index\";\n\n/**\n * The words of `Carousel`. `carousel` describes the region to screen readers\n * (`aria-roledescription`); `{page}` and `{count}` in `status` are replaced\n * with the page shown and the number of pages.\n */\nexport const useCarouselLabels = createTranslation({\n carousel: {\n no: \"karusell\",\n se: \"karusell\",\n fi: \"karuselli\",\n es: \"carrusel\",\n },\n previous: {\n no: \"Forrige\",\n se: \"Föregående\",\n fi: \"Edellinen\",\n es: \"Anterior\",\n },\n next: {\n no: \"Neste\",\n se: \"Nästa\",\n fi: \"Seuraava\",\n es: \"Siguiente\",\n },\n status: {\n no: \"Side {page} av {count}\",\n se: \"Sida {page} av {count}\",\n fi: \"Sivu {page}/{count}\",\n es: \"Página {page} de {count}\",\n },\n});\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { ChevronLeft, ChevronRight } from \"lucide-react\";\nimport { Children, useRef, type ReactNode, type Ref } from \"react\";\n\nimport { IconButton, PaginationDots } from \"../../index\";\nimport { mergeClassName } from \"../../utils/merge-class-name\";\nimport styles from \"./carousel.module.css\";\nimport { useCarousel } from \"./use-carousel\";\nimport { useCarouselLabels } from \"./use-carousel-labels\";\n\n/**\n * A carousel is a region, so it needs a name: the title of what it shows.\n * Provide exactly one of `aria-label` or `aria-labelledby`.\n */\ntype CarouselName =\n | { \"aria-label\": string; \"aria-labelledby\"?: never }\n | { \"aria-labelledby\": string; \"aria-label\"?: never };\n\nexport type CarouselProps = Omit<\n useRender.ComponentProps<\"div\">,\n \"ref\" | \"children\" | \"aria-label\" | \"aria-labelledby\"\n> &\n CarouselName & {\n /** The rendered element: the `<div>`, or the element passed to `render`. */\n ref?: Ref<HTMLElement>;\n /**\n * The slides, such as `TestimonialCard`s. Each one is put in an item of\n * a list and stretched to the height of the tallest.\n */\n children: ReactNode;\n /**\n * The name of each dot, when \"Gå til side 3\" does not say enough, as on\n * `PaginationDots`.\n */\n getItemLabel?: (page: number) => string;\n };\n\n/**\n * A row of slides that scrolls sideways, as the website's carousel of\n * reviews: 2.2 slides in view from 1024px and 1.15 below, 16px apart, with\n * previous and next buttons and a dot per place under them.\n *\n * ```tsx\n * <Carousel aria-labelledby={titleId}>\n * <TestimonialCard rating={5} quote=\"…\" name=\"Kari Nordmann\" meta=\"Kunde, Oslo\" />\n * <TestimonialCard rating={5} quote=\"…\" name=\"Ola Hansen\" meta=\"Kunde, Bergen\" />\n * <TestimonialCard rating={4} quote=\"…\" name=\"Ingrid Berg\" meta=\"Kunde, Trondheim\" />\n * </Carousel>\n * ```\n *\n * The track scrolls on its own, with scroll snap: swipe it, scroll it with a\n * trackpad, or focus it and use the arrow keys. It stops at the start of\n * each slide; the last slides, which can't reach the start, share the end.\n * The buttons and dots move it a place at a time, and once it has moved, the\n * place it comes to rest at is announced (\"Side 2 av 3\"). The edges fade into the section's colour where\n * there is more to scroll. Under reduced motion the buttons jump instead of\n * scrolling smoothly.\n *\n * It is a region named by `aria-label` or `aria-labelledby`, described as a\n * carousel in the `LeaProvider` language. The controls show only when the\n * slides don't fit. The buttons and dots are made for the light surfaces\n * (`base`, `subtle`, `muted`).\n */\nexport function Carousel({\n getItemLabel,\n render,\n ref,\n className,\n children,\n ...rest\n}: CarouselProps) {\n const labels = useCarouselLabels();\n const trackRef = useRef<HTMLUListElement>(null);\n const slides = Children.toArray(children);\n const {\n pageCount,\n page,\n settledPage,\n atStart,\n atEnd,\n overflowing,\n moved,\n goTo,\n } = useCarousel(trackRef, slides.length);\n\n return useRender({\n render,\n ref,\n defaultTagName: \"div\",\n props: {\n role: \"region\",\n \"aria-roledescription\": labels.carousel,\n ...rest,\n className: mergeClassName(styles.Carousel, className)({}),\n children: (\n <>\n <div\n className={styles.Viewport}\n data-at-start={atStart || undefined}\n data-at-end={atEnd || undefined}\n >\n <ul\n ref={trackRef}\n className={styles.Track}\n // A track that scrolls and holds nothing focusable has to take\n // the focus itself, so the arrow keys can scroll it.\n tabIndex={overflowing ? 0 : undefined}\n >\n {slides.map((slide, index) => (\n <li key={index} className={styles.Slide}>\n {slide}\n </li>\n ))}\n </ul>\n </div>\n {pageCount > 1 && (\n <div className={styles.Controls}>\n <IconButton\n variant=\"elevated\"\n color=\"secondary\"\n aria-label={labels.previous}\n disabled={page === 0}\n focusableWhenDisabled\n onClick={() => goTo(page - 1)}\n >\n <ChevronLeft aria-hidden />\n </IconButton>\n <PaginationDots\n count={pageCount}\n value={page + 1}\n onValueChange={(next) => goTo(next - 1)}\n getItemLabel={getItemLabel}\n />\n <IconButton\n variant=\"elevated\"\n color=\"secondary\"\n aria-label={labels.next}\n disabled={page === pageCount - 1}\n focusableWhenDisabled\n onClick={() => goTo(page + 1)}\n >\n <ChevronRight aria-hidden />\n </IconButton>\n <span role=\"status\" className={styles.VisuallyHidden}>\n {moved &&\n labels.status\n .replace(\"{page}\", String(settledPage + 1))\n .replace(\"{count}\", String(pageCount))}\n </span>\n </div>\n )}\n </>\n ),\n },\n });\n}\n","/*\n * The website's CTA block: from 1024px two equal columns 64px apart, the\n * title on `display-md` in the first and the text (`body-lg`), the action and\n * the price example 32px apart in the second. Below 1024px one column, 16px\n * apart, the title on `heading-xl`. No colour of its own.\n */\n.CtaBanner {\n display: grid;\n min-width: 0;\n grid-template-columns: minmax(0, 1fr);\n align-items: start;\n gap: var(--lea-spacing-2);\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.Content {\n display: flex;\n min-width: 0;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--lea-spacing-4);\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.Text > * {\n margin: 0;\n}\n\n.Text > * + * {\n margin-block-start: var(--lea-spacing-1);\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@media (min-width: 1024px) {\n .CtaBanner[data-has-content] {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: var(--lea-spacing-8);\n }\n\n .Title {\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","\"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 styles from \"./cta-banner.module.css\";\nimport { Paragraphs } from \"../paragraphs\";\n\nexport type CtaBannerProps = Omit<\n useRender.ComponentProps<\"div\">,\n \"ref\" | \"title\" | \"children\"\n> & {\n /** The rendered element: the `<div>`, or the element passed to `render`. */\n ref?: Ref<HTMLElement>;\n /** The big title. */\n title: ReactNode;\n /**\n * The element for the title, an `<h2>` by default: `titleRender={<h1 />}`\n * where the banner opens the page.\n */\n titleRender?: ReactElement;\n /**\n * The text beside the title. A string becomes one paragraph; pass `<p>`\n * elements for several.\n */\n text?: ReactNode;\n /** The call to action under the text: an `ActionButton`. */\n action?: ReactNode;\n /** The price example under the action, in the small print. */\n priceExample?: ReactNode;\n};\n\n/**\n * A closing call to action: a big title on the left, and a text, a button\n * and the price example on the right, as at the foot of leabank.no's home\n * page.\n *\n * ```tsx\n * <Section surface=\"muted\">\n * <CtaBanner\n * title=\"Klar for å komme i gang med Lea Bank?\"\n * text=\"Vi er her for deg og står klare til å hjelpe deg.\"\n * action={<ActionButton render={<NextLink href=\"/lan\" />}>Søk om forbrukslån</ActionButton>}\n * />\n * </Section>\n * ```\n *\n * Two equal columns 64px apart from 1024px, one column below with the title\n * at 41px. With only a title, the title takes the whole width. It takes the\n * text colour of its section.\n */\nexport function CtaBanner({\n title,\n titleRender,\n text,\n action,\n priceExample,\n render,\n ref,\n className,\n ...rest\n}: CtaBannerProps) {\n const titleElement = useRender({\n render: titleRender,\n defaultTagName: \"h2\",\n props: { className: styles.Title, children: title },\n });\n const hasContent = text != null || action != null || priceExample != null;\n\n return useRender({\n render,\n ref,\n defaultTagName: \"div\",\n props: {\n ...rest,\n \"data-has-content\": hasContent || undefined,\n className: mergeClassName(styles.CtaBanner, className)({}),\n children: (\n <>\n {titleElement}\n {hasContent && (\n <div className={styles.Content}>\n {text != null && (\n <Paragraphs className={styles.Text}>{text}</Paragraphs>\n )}\n {action != null && <div className={styles.Action}>{action}</div>}\n {priceExample != null && (\n <p className={styles.Price}>{priceExample}</p>\n )}\n </div>\n )}\n </>\n ),\n },\n });\n}\n","\"use client\";\n\nimport { createContext } from \"react\";\n\n/**\n * Whether an `ExpandingCardsItem` is open: the one card `ExpandingCards`\n * keeps open in its row from 1312px, or every card below that and outside\n * the row. Internal.\n */\nexport const ExpandingCardsContext = createContext<{ open: boolean }>({\n open: true,\n});\n","/*\n * The website's CardsWithButton: from 1312px the cards share a row 16px\n * apart, each `flex: 1` and the open one `flex: 2` (640 + 320 + 320 of\n * 1312). Below 1312px one under the other, 16px apart, every card open\n * (expanding-cards-item.module.css).\n *\n * In the row a card lays out its text and photo at the width of a closed\n * card, `--expanding-cards-column` (half the open one), whatever its own\n * width is on the way. The text never wraps anew while a card opens or\n * closes, so the row keeps its height and the page under it stays where it\n * is. The width changes over `--expanding-cards-duration`, 0.6s: twice\n * `emphasis`, off the duration scale. A card grows by 320px in the row of\n * three, and at `normal` that read as a jump (Klarna's cards, the model for\n * the motion, take 0.65s).\n *\n * `--expanding-cards-count`, the number of cards, is set on the list by\n * `ExpandingCards`.\n */\n.ExpandingCards {\n min-width: 0;\n}\n\n.List {\n display: flex;\n flex-direction: column;\n padding: 0;\n margin: 0;\n gap: var(--lea-spacing-2);\n list-style: none;\n}\n\n.Item {\n display: grid;\n min-width: 0;\n}\n\n@media (min-width: 1312px) {\n .List {\n /* `100cqw` in the cards is the width of the row. */\n container-type: inline-size;\n flex-direction: row;\n }\n\n .Item {\n /* The row less its gaps, in one part per card and one more for the\n open card's second half. */\n --expanding-cards-column: calc(\n (100cqw - (var(--expanding-cards-count) - 1) * var(--lea-spacing-2)) /\n (var(--expanding-cards-count) + 1)\n );\n --expanding-cards-duration: calc(var(--lea-duration-emphasis) * 1.3);\n\n flex: 1 1 0;\n transition: flex-grow var(--expanding-cards-duration)\n var(--lea-easing-standard);\n }\n\n .Item[data-open] {\n flex-grow: 2;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Item {\n transition-duration: 0.01ms;\n }\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport {\n Children,\n useState,\n useSyncExternalStore,\n type CSSProperties,\n type ReactNode,\n type Ref,\n} from \"react\";\n\nimport { mergeClassName } from \"../../utils/merge-class-name\";\nimport { ExpandingCardsContext } from \"./expanding-cards-context\";\nimport styles from \"./expanding-cards.module.css\";\n\n/** The width from which the cards sit in a row and one of them is open. */\nconst ROW = \"(min-width: 1312px)\";\n\nconst OPEN = { open: true };\nconst CLOSED = { open: false };\n\nconst subscribe = (onChange: () => void) => {\n const query = window.matchMedia(ROW);\n query.addEventListener(\"change\", onChange);\n return () => query.removeEventListener(\"change\", onChange);\n};\n\n/**\n * Whether the cards are in a row. On the server, and in the first render\n * after it, they are, as on the website: below 1312px the CSS opens every\n * card anyway, and only the buttons wait for the browser to tell.\n */\nconst useRow = () =>\n useSyncExternalStore(\n subscribe,\n () => window.matchMedia(ROW).matches,\n () => true,\n );\n\nexport type ExpandingCardsDefaultOpen = \"first\" | \"last\";\n\nexport type ExpandingCardsProps = 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 * The cards: `ExpandingCardsItem`s, usually three. Each one is put in an\n * item of a list.\n */\n children: ReactNode;\n /**\n * The card that is open before the visitor points at one: the website's\n * \"Main card\".\n * @default \"first\"\n */\n defaultOpen?: ExpandingCardsDefaultOpen;\n};\n\n/**\n * A row of photo cards in which one is open, as on the home page of\n * leabank.no: the open card is twice as wide as the others, with its title,\n * text and call to action beside its photo; the others show the photo and\n * the arrow.\n *\n * ```tsx\n * <Section>\n * <ExpandingCards>\n * <ExpandingCardsItem\n * surface=\"contrast\"\n * title=\"Forbrukslån\"\n * text=\"Lån til det du trenger, uten sikkerhet.\"\n * src=\"/bilder/forbrukslan.jpg\"\n * alt=\"\"\n * action={<ActionButton render={<NextLink href=\"/lan/forbrukslan\" />}>Søk nå</ActionButton>}\n * />\n * …\n * </ExpandingCards>\n * </Section>\n * ```\n *\n * From 1312px the cards share a row, 16px apart, and the card the pointer\n * or the keyboard focus moves into opens; the last one opened stays open.\n * The row keeps its height while a card opens, so the page under it does\n * not move. Below 1312px every card is open, one under the other, and below\n * 640px the photo sits on top of the text. The cards are a list. Under\n * reduced motion they open without moving.\n */\nexport function ExpandingCards({\n defaultOpen = \"first\",\n render,\n ref,\n className,\n children,\n ...rest\n}: ExpandingCardsProps) {\n const cards = Children.toArray(children);\n const row = useRow();\n const [open, setOpen] = useState(() =>\n defaultOpen === \"last\" ? Math.max(0, cards.length - 1) : 0,\n );\n\n return useRender({\n render,\n ref,\n defaultTagName: \"div\",\n props: {\n ...rest,\n className: mergeClassName(styles.ExpandingCards, className)({}),\n children: (\n <ul\n className={styles.List}\n // The stylesheet works out the width of a closed card from it.\n style={{ \"--expanding-cards-count\": cards.length } as CSSProperties}\n >\n {cards.map((card, index) => (\n <li\n key={index}\n className={styles.Item}\n data-open={index === open || undefined}\n onPointerEnter={() => setOpen(index)}\n onFocus={() => setOpen(index)}\n >\n <ExpandingCardsContext.Provider\n value={!row || index === open ? OPEN : CLOSED}\n >\n {card}\n </ExpandingCardsContext.Provider>\n </li>\n ))}\n </ul>\n ),\n },\n });\n}\n","/*\n * A card of the website's CardsWithButton: one of the five section colours,\n * `5xl` corners, at least 280px tall. Open, the text column takes the start\n * half (32px inside, its parts 16px apart at the bottom: the title\n * `heading-md` in capitals, the text `body-md`, the `ActionButton`, which\n * fits its label and may run over the photo) and the photo the end half,\n * 16px from the card's edges with `4xl` corners (320 × 248 in a 640 × 280\n * card). Closed (only from 1312px, in a row), the photo\n * fills the card, the title and text fade out (they stay for screen\n * readers) and the button is its arrow. In the row the text and the photo\n * keep the width the row gives them (`--expanding-cards-column`, from\n * expanding-cards.module.css) while the card opens and closes, so the card\n * never changes height. Below 640px the photo is on top,\n * 2 : 1 inside 16px, and the title 18px. The text is `text.contrast` on the\n * dark surfaces (the website paints it white on every card, code issue 1).\n *\n * `[data-image-as-background]`: the photo fills the card open as well, and\n * the text sits in a box of the card's colour (`4xl` corners, as the photo\n * beside the text has) 16px from the card's edges and 16px inside, so the\n * text is where it is without the box and wraps the same. Closed, the box\n * fades with the title and text. `[data-image-motion=\"zoom\"]` crops the\n * photo to the card as it changes width; `\"expand\"` lays it out at the open\n * card's width, centred, so the card uncovers its sides without scaling it.\n */\n.ExpandingCardsItem {\n --item-bg: var(--lea-palette-bg-base);\n --item-text: var(--lea-palette-text-base);\n\n position: relative;\n display: flex;\n box-sizing: border-box;\n min-width: 0;\n /* The website's 280px. */\n min-height: calc(var(--lea-spacing) * 35);\n overflow: hidden;\n border-radius: var(--lea-radius-5xl);\n background-color: var(--item-bg);\n color: var(--item-text);\n}\n\n.ExpandingCardsItem[data-surface=\"subtle\"] {\n --item-bg: var(--lea-palette-bg-subtle);\n}\n\n.ExpandingCardsItem[data-surface=\"muted\"] {\n --item-bg: var(--lea-palette-bg-muted);\n}\n\n.ExpandingCardsItem[data-surface=\"contrast\"] {\n --item-bg: var(--lea-palette-bg-contrast);\n --item-text: var(--lea-palette-text-contrast);\n}\n\n.ExpandingCardsItem[data-surface=\"contrast-accent\"] {\n --item-bg: var(--lea-palette-bg-contrast-accent);\n --item-text: var(--lea-palette-text-contrast);\n}\n\n/* The photo lies over the title and text, so a card that closes covers them\n and one that opens uncovers them; only the button lies over the photo. */\n.Content {\n position: relative;\n display: flex;\n box-sizing: border-box;\n width: 100%;\n min-width: 0;\n flex-direction: column;\n align-items: flex-start;\n justify-content: flex-end;\n padding: var(--lea-spacing-4);\n gap: var(--lea-spacing-2);\n}\n\n.ExpandingCardsItem[data-image] .Content {\n width: 50%;\n}\n\n.Title {\n max-width: 100%;\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 text-transform: uppercase;\n}\n\n.Text {\n margin: 0;\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 > * {\n margin: 0;\n}\n\n/*\n * The button is as wide as its label, and runs over the photo rather than\n * wrap its label in the text column: at most to 32px from the card's end\n * edge, where it wraps. With the photo beside the text, the card's inside\n * is twice the column's plus the 64px of padding between the two halves.\n */\n.Action {\n position: relative;\n z-index: 1;\n width: max-content;\n max-width: 100%;\n}\n\n.ExpandingCardsItem[data-image] .Action {\n max-width: calc(200% + var(--lea-spacing-8));\n}\n\n.Photo {\n position: absolute;\n width: 50%;\n inset-block: var(--lea-spacing-2);\n inset-inline-end: var(--lea-spacing-2);\n}\n\n.Image {\n display: block;\n width: 100%;\n height: 100%;\n border-radius: var(--lea-radius-4xl);\n object-fit: cover;\n}\n\n@media (min-width: 1312px) {\n /*\n * In the row, the width of a closed card (half the open one), set by\n * `ExpandingCards`, whatever the card's own width is on the way; alone,\n * the card's halves. The photo stays that wide, anchored at the end, and\n * only its 16px inset changes: closed, it fills the card.\n */\n .Content {\n width: calc(var(--expanding-cards-column, 50%) * 2);\n }\n\n .ExpandingCardsItem[data-image] .Content,\n .Photo {\n width: var(--expanding-cards-column, 50%);\n }\n\n .Photo {\n transition-duration: var(--expanding-cards-duration);\n transition-property: inset-block, inset-inline-end;\n transition-timing-function: var(--lea-easing-standard);\n }\n\n .ExpandingCardsItem:not([data-open]) .Photo {\n inset-block: 0;\n inset-inline-end: 0;\n }\n\n /*\n * Opening, the title and text fade in over the second half of the card's\n * 0.6s, as the photo moves off them; closing, they fade out at once,\n * before it covers them.\n */\n .Title,\n .Text {\n transition: opacity var(--lea-duration-emphasis) var(--lea-easing-standard)\n var(--lea-duration-emphasis);\n }\n\n /* Out of sight but still in the page: the card is as tall closed as\n open, and screen readers still read them. */\n .ExpandingCardsItem:not([data-open]) .Title,\n .ExpandingCardsItem:not([data-open]) .Text {\n opacity: 0;\n transition-delay: 0s;\n transition-duration: var(--lea-duration-fast);\n }\n}\n\n/*\n * The photo as the background (see the top of the file). These follow the\n * row's rules, which they override at the same specificity. Outside the row\n * `--expanding-cards-column` is unset and the card's halves count instead.\n */\n.ExpandingCardsItem[data-image-as-background] .Content {\n z-index: 1;\n align-self: flex-end;\n width: calc(var(--expanding-cards-column, 50%) - var(--lea-spacing-4));\n margin: var(--lea-spacing-2);\n padding: var(--lea-spacing-2);\n border-radius: var(--lea-radius-4xl);\n background-color: var(--item-bg);\n}\n\n/* The button keeps to the box. */\n.ExpandingCardsItem[data-image-as-background] .Action {\n max-width: 100%;\n}\n\n.ExpandingCardsItem[data-image-as-background] .Photo {\n width: auto;\n inset: 0;\n}\n\n/* As wide as the open card, centred: the open card shows all of it, a\n closed one its middle. */\n.ExpandingCardsItem[data-image-motion=\"expand\"] .Photo {\n inset-inline: calc(50% - var(--expanding-cards-column, 50%));\n}\n\n/* The card's corners round the photo. */\n.ExpandingCardsItem[data-image-as-background] .Image {\n border-radius: 0;\n}\n\n@media (min-width: 1312px) {\n /* The box fades in and out with the title and text. */\n .ExpandingCardsItem[data-image-as-background] .Content {\n transition: background-color var(--lea-duration-emphasis)\n var(--lea-easing-standard) var(--lea-duration-emphasis);\n }\n\n .ExpandingCardsItem[data-image-as-background]:not([data-open]) .Content {\n background-color: transparent;\n transition-delay: 0s;\n transition-duration: var(--lea-duration-fast);\n }\n}\n\n@media (max-width: 639.95px) {\n .ExpandingCardsItem {\n flex-direction: column;\n }\n\n .ExpandingCardsItem[data-image] .Content {\n width: 100%;\n flex-grow: 1;\n }\n\n /* The photo is on top: the button keeps to the text. */\n .ExpandingCardsItem[data-image] .Action {\n max-width: 100%;\n }\n\n .Photo {\n position: static;\n width: auto;\n order: -1;\n padding: var(--lea-spacing-2);\n }\n\n .Image {\n height: auto;\n aspect-ratio: 2 / 1;\n }\n\n .Title {\n font-size: var(--lea-font-step-1);\n font-weight: var(--lea-font-weight-semibold);\n line-height: var(--lea-line-height-snug);\n }\n\n /* The photo behind the card: the box spans the card at the bottom, and\n the space above it shows the photo at 2 : 1, as on top of a card\n without it. */\n .ExpandingCardsItem[data-image-as-background]::before {\n flex: 1 0 auto;\n aspect-ratio: 2 / 1;\n content: \"\";\n }\n\n .ExpandingCardsItem[data-image-as-background] .Content {\n width: auto;\n flex-grow: 0;\n align-self: stretch;\n }\n\n .ExpandingCardsItem[data-image-as-background] .Photo {\n position: absolute;\n padding: 0;\n }\n\n .ExpandingCardsItem[data-image-as-background] .Image {\n height: 100%;\n aspect-ratio: auto;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Photo,\n .Title,\n .Text,\n .ExpandingCardsItem:not([data-open]) .Title,\n .ExpandingCardsItem:not([data-open]) .Text,\n .ExpandingCardsItem[data-image-as-background] .Content,\n .ExpandingCardsItem[data-image-as-background]:not([data-open]) .Content {\n transition-duration: 0.01ms;\n transition-delay: 0s;\n }\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport {\n useContext,\n type ReactElement,\n type ReactNode,\n type Ref,\n} from \"react\";\n\nimport type { SurfaceRoles } from \"../../components/color-roles\";\nimport { ActionButtonProvider } from \"../../index\";\nimport { mergeClassName } from \"../../utils/merge-class-name\";\nimport { ExpandingCardsContext } from \"./expanding-cards-context\";\nimport styles from \"./expanding-cards-item.module.css\";\nimport { Paragraphs } from \"../paragraphs\";\n\nexport type ExpandingCardsItemSurface = SurfaceRoles<\n \"base\" | \"subtle\" | \"muted\" | \"contrast\" | \"contrast-accent\"\n>;\n\n/**\n * How a photo behind the whole card follows the card as it opens in the\n * row: `\"zoom\"` crops it to the card, so it grows as the card widens;\n * `\"expand\"` keeps it at the open card's size, so opening uncovers its sides.\n */\nexport type ExpandingCardsItemImageMotion = \"zoom\" | \"expand\";\n\n/** `alt` is required with a photo, `\"\"` when it is decoration. */\ntype ExpandingCardsItemImage =\n | {\n /** The photo: the whole card while it is closed, half of it open. */\n src: string;\n /** What the photo shows, for screen readers; `\"\"` for decoration. */\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 * The photo fills the whole card, open as well as closed, and the\n * title, text and action sit in a box of the card's colour at its\n * bottom-start corner, 16px in.\n * @default false\n */\n imageAsBackground?: boolean;\n /**\n * With `imageAsBackground`, how the photo follows the card as it\n * opens in the row: `\"zoom\"` keeps it cropped to the card, so it\n * grows as the card widens; `\"expand\"` keeps it the size of the open\n * card and centred, so opening uncovers its sides without scaling it.\n * The same outside the row, where every card is open.\n * @default \"zoom\"\n */\n imageMotion?: ExpandingCardsItemImageMotion;\n }\n | {\n src?: undefined;\n alt?: undefined;\n imageRender?: undefined;\n imageAsBackground?: undefined;\n imageMotion?: undefined;\n };\n\nexport type ExpandingCardsItemProps = Omit<\n useRender.ComponentProps<\"div\">,\n \"ref\" | \"title\" | \"children\"\n> &\n ExpandingCardsItemImage & {\n /** The rendered element: the `<div>`, or the element passed to `render`. */\n ref?: Ref<HTMLElement>;\n /** The title, in capitals. */\n title: ReactNode;\n /**\n * The element for the title, an `<h2>` by default. Pass another level\n * where the page outline needs one: `titleRender={<h3 />}`.\n */\n titleRender?: ReactElement;\n /** The text under the title. A string becomes one paragraph. */\n text?: ReactNode;\n /**\n * The call to action: an `ActionButton`, or a component of the app's\n * own that renders one. While the card is closed it shows only its\n * arrow, with the label kept for screen readers.\n */\n action?: ReactNode;\n /**\n * The colour of the card, after its `bg` token, as on `Section`. On\n * `\"contrast\"` and `\"contrast-accent\"` the text is white.\n * @default \"base\"\n */\n surface?: ExpandingCardsItemSurface;\n };\n\n/**\n * A card of `ExpandingCards`: a photo, a title in capitals, a text and a\n * call to action, on one of the five section colours.\n *\n * ```tsx\n * <ExpandingCardsItem\n * surface=\"contrast\"\n * title=\"Forbrukslån\"\n * text=\"Lån til det du trenger, uten sikkerhet. Søk på få minutter.\"\n * src=\"/bilder/forbrukslan.jpg\"\n * alt=\"\"\n * action={<ActionButton render={<NextLink href=\"/lan/forbrukslan\" />}>Søk nå</ActionButton>}\n * />\n * ```\n *\n * Open, the text takes the start half (32px inside, at the bottom) and the\n * photo the end half, 16px from the card's edges. Closed (in the row of\n * `ExpandingCards` from 1312px), the photo fills the card and only the\n * `ActionButton`'s arrow shows; the title and text are still there for\n * screen readers, and focusing the button opens the card. Below 640px the\n * photo is on top at 2 : 1. Outside `ExpandingCards` the card is always\n * open. At least 280px tall; it grows with its text.\n *\n * With `imageAsBackground` the photo fills the card open as well, and the\n * text sits in a box of the card's colour in the start half, 16px from the\n * card's edges; closed, the box fades with the text. `imageMotion` picks\n * whether the photo zooms as the card opens or only shows more of itself.\n * Below 640px the box sits at the bottom, under a 2 : 1 view of the photo.\n */\nexport function ExpandingCardsItem({\n title,\n titleRender,\n text,\n action,\n src,\n alt,\n imageRender,\n imageAsBackground = false,\n imageMotion = \"zoom\",\n surface = \"base\",\n render,\n ref,\n className,\n ...rest\n}: ExpandingCardsItemProps) {\n const { open } = useContext(ExpandingCardsContext);\n\n const titleElement = useRender({\n render: titleRender,\n defaultTagName: \"h2\",\n props: { className: styles.Title, 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 const background = src != null && imageAsBackground;\n\n return useRender({\n render,\n ref,\n defaultTagName: \"div\",\n props: {\n ...rest,\n \"data-surface\": surface,\n \"data-open\": open || undefined,\n \"data-image\": src != null || undefined,\n \"data-image-as-background\": background || undefined,\n \"data-image-motion\": background ? imageMotion : undefined,\n className: mergeClassName(styles.ExpandingCardsItem, className)({}),\n children: (\n <>\n <div className={styles.Content}>\n {titleElement}\n {text != null && (\n <Paragraphs className={styles.Text}>{text}</Paragraphs>\n )}\n {action != null && (\n <div className={styles.Action}>\n {/* A closed card has room for the arrow only. Through the\n provider, so an ActionButton inside a component of the\n app's own shrinks too. */}\n <ActionButtonProvider iconOnly={!open}>\n {action}\n </ActionButtonProvider>\n </div>\n )}\n </div>\n {image != null && <div className={styles.Photo}>{image}</div>}\n </>\n ),\n },\n });\n}\n","/*\n * The website's HeroWithBackgroundImage: a band at least 700px tall (600 below\n * 768px, 500 below 640px), 112px / 64px inside (80 / 16, then 64 / 16), with\n * the photo filling it behind the content and a `Panel` at most 600px wide\n * (500 below 768px, the full column below 640px) at the start of the 1312px\n * column, centred vertically. The panel's parts are 32px apart.\n */\n.Hero {\n position: relative;\n isolation: isolate;\n display: flex;\n box-sizing: border-box;\n width: 100%;\n /* The website's min-height: 700px. */\n min-height: calc(var(--lea-spacing) * 87.5);\n align-items: center;\n overflow: hidden;\n /* 112px, the website's `huge`: off the spacing scale. */\n padding: calc(var(--lea-spacing) * 14) var(--lea-spacing-8);\n /* Behind the photo while it loads. */\n background-color: var(--lea-palette-bg-muted);\n}\n\n.Background {\n position: absolute;\n z-index: -1;\n inset: 0;\n}\n\n.Image {\n display: block;\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n\n.Container {\n width: 100%;\n max-width: var(--lea-width-section);\n margin-inline: auto;\n}\n\n/* Two classes, to win over the Panel's own gap whichever stylesheet loads\n last. */\n.Container .Box {\n max-width: calc(var(--lea-spacing) * 75);\n gap: var(--lea-spacing-4);\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-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 > * {\n margin: 0;\n}\n\n.Text > * + * {\n margin-block-start: var(--lea-spacing-2);\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@media (max-width: 767.95px) {\n .Hero {\n /* The website's 600px. */\n min-height: calc(var(--lea-spacing) * 75);\n /* 80px, the website's `xxl`. */\n padding: calc(var(--lea-spacing) * 10) var(--lea-spacing-2);\n }\n\n .Container .Box {\n /* The website's 500px. */\n max-width: calc(var(--lea-spacing) * 62.5);\n }\n\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\n@media (max-width: 639.95px) {\n .Hero {\n /* The website's 500px. */\n min-height: calc(var(--lea-spacing) * 62.5);\n padding: var(--lea-spacing-8) var(--lea-spacing-2);\n }\n\n .Container .Box {\n max-width: none;\n }\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport type { ReactElement, ReactNode, Ref } from \"react\";\n\nimport { Panel } from \"../../index\";\nimport { mergeClassName } from \"../../utils/merge-class-name\";\nimport styles from \"./hero.module.css\";\nimport { Paragraphs } from \"../paragraphs\";\n\nexport type HeroProps = Omit<\n useRender.ComponentProps<\"section\">,\n \"ref\" | \"title\" | \"children\"\n> & {\n /** The rendered element: the `<section>`, or the element passed to `render`. */\n ref?: Ref<HTMLElement>;\n /** The photo behind the band, cropped to fill it. */\n src: string;\n /**\n * What the photo shows, for screen readers; `\"\"` when it is decoration,\n * as a photo behind a title usually is.\n */\n alt: string;\n /**\n * The image element to render instead of the plain `<img>`, such as\n * `next/image` with `fill` and `priority` (the hero photo is usually the\n * largest thing on the page). Its own props win over `src` and `alt`.\n */\n imageRender?: ReactElement;\n /** The title of the page. */\n title: ReactNode;\n /**\n * The element for the title, an `<h1>` by default, as a hero opens the\n * page: `titleRender={<h2 />}` where it does not.\n */\n titleRender?: ReactElement;\n /**\n * The text under the title. A string becomes one paragraph; pass `<p>`\n * elements for several.\n */\n text?: ReactNode;\n /** The call to action under the text: an `ActionButton`. */\n action?: ReactNode;\n /** The price example under the action, in the small print. */\n priceExample?: ReactNode;\n};\n\n/**\n * The top of a product page: a photo from edge to edge, and on it a white\n * `Panel` with the page title, a text, a call to action and the price\n * example, as on leabank.no's loan pages.\n *\n * ```tsx\n * <Hero\n * src=\"/bilder/kjokken.jpg\"\n * alt=\"\"\n * imageRender={<NextImage src={kitchen} alt=\"\" fill priority />}\n * title=\"Forbrukslån med rente fra 7,90 %\"\n * text=\"Søk om lån uten sikkerhet på få minutter og få svar med én gang.\"\n * action={<ActionButton render={<NextLink href=\"/sok\" />}>Søk om lån</ActionButton>}\n * />\n * ```\n *\n * It is a band of its own, not inside a `Section`: at least 700px tall\n * (600px below 768px, 500px below 640px), with the panel at most 600px wide\n * at the start of the 1312px column. In the reading order the panel comes\n * first and the photo after it.\n */\nexport function Hero({\n src,\n alt,\n imageRender,\n title,\n titleRender,\n text,\n action,\n priceExample,\n render,\n ref,\n className,\n ...rest\n}: HeroProps) {\n const titleElement = useRender({\n render: titleRender,\n defaultTagName: \"h1\",\n props: { className: styles.Title, children: title },\n });\n\n const image = useRender({\n render: imageRender,\n defaultTagName: \"img\",\n props: { src, alt, className: styles.Image },\n });\n\n return useRender({\n render,\n ref,\n defaultTagName: \"section\",\n props: {\n ...rest,\n className: mergeClassName(styles.Hero, className)({}),\n children: (\n <>\n <div className={styles.Container}>\n <Panel className={styles.Box}>\n {titleElement}\n {text != null && (\n <Paragraphs className={styles.Text}>{text}</Paragraphs>\n )}\n {action != null && <div className={styles.Action}>{action}</div>}\n {priceExample != null && (\n <p className={styles.Price}>{priceExample}</p>\n )}\n </Panel>\n </div>\n <div className={styles.Background}>{image}</div>\n </>\n ),\n },\n });\n}\n","/*\n * The website's HeroServiceGrid. From 1024px two equal columns 16px apart,\n * at least 600px tall: a `FeaturedImage` filling one, and the cards two by\n * two in the other, in rows of one height (292px each at 600). Below 1024px the\n * photo is on top at `FeaturedImage`'s own 1 : 0.7, the cards two by two\n * under it from 768px and one under the other below.\n */\n.HeroServiceGrid {\n display: grid;\n min-width: 0;\n grid-template-columns: minmax(0, 1fr);\n gap: var(--lea-spacing-2);\n}\n\n.Cards {\n display: grid;\n min-width: 0;\n grid-template-columns: minmax(0, 1fr);\n gap: var(--lea-spacing-2);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n/* The card fills its cell. */\n.Item {\n display: grid;\n min-width: 0;\n grid-template-columns: minmax(0, 1fr);\n}\n\n@media (min-width: 768px) {\n .Cards {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n /* Rows of one height, so a title that wraps further doesn't make its\n row taller than the other. */\n grid-auto-rows: 1fr;\n }\n}\n\n@media (min-width: 1024px) {\n .HeroServiceGrid {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n /* The website's min-height: 600px. */\n grid-template-rows: minmax(calc(var(--lea-spacing) * 75), auto);\n }\n\n /* Two classes, to win over FeaturedImage's own rules whichever stylesheet\n loads last. The photo takes the height the row is given and never sets\n it, so a tall photo can't stretch the block. */\n .HeroServiceGrid .Featured {\n contain: size;\n }\n\n .HeroServiceGrid[data-image-position=\"end\"] .Featured {\n order: 1;\n }\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { Children, type ReactElement, type ReactNode, type Ref } from \"react\";\n\nimport { FeaturedImage } from \"../../index\";\nimport { mergeClassName } from \"../../utils/merge-class-name\";\nimport styles from \"./hero-service-grid.module.css\";\n\nexport type HeroServiceGridImagePosition = \"start\" | \"end\";\n\nexport type HeroServiceGridProps = 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 /** The large photo. */\n src: string;\n /** What the photo shows, for screen readers; `\"\"` when it is decoration. */\n alt: string;\n /**\n * The image element to render instead of the plain `<img>`, as on\n * `FeaturedImage`: `next/image` with `fill`, or an `<img loading=\"lazy\">`.\n */\n imageRender?: ReactElement;\n /** A call to action on the photo: an `ActionButton`. */\n action?: ReactNode;\n /**\n * Where the photo sits from 1024px: `\"start\"` (on the left) or `\"end\"`.\n * Below 1024px it is always on top.\n * @default \"start\"\n */\n imagePosition?: HeroServiceGridImagePosition;\n /**\n * The cards beside the photo: up to four `LinkCard`s, two by two. Each one\n * is put in an item of a list.\n */\n children?: ReactNode;\n};\n\n/**\n * A large photo beside a block of link cards, as on leabank.no's home page:\n * a `FeaturedImage` with a call to action, and up to four `LinkCard`s.\n *\n * ```tsx\n * <Section>\n * <HeroServiceGrid\n * src=\"/bilder/familie.jpg\"\n * alt=\"\"\n * action={<ActionButton render={<NextLink href=\"/lan\" />}>Se våre lån</ActionButton>}\n * >\n * <LinkCard render={<NextLink href=\"/lan\" />} title=\"Lån etter dine behov.\" surface=\"contrast\" />\n * <LinkCard render={<NextLink href=\"/sparing\" />} title=\"Se hvor mye du kan spare.\" surface=\"muted\" />\n * </HeroServiceGrid>\n * </Section>\n * ```\n *\n * From 1024px two equal columns 16px apart, at least 600px tall: the photo\n * fills one, the cards share the other two by two. Below 1024px the photo is\n * on top at 1 : 0.7, the cards two by two under it, and one under the other\n * below 768px. The cards are a list.\n */\nexport function HeroServiceGrid({\n src,\n alt,\n imageRender,\n action,\n imagePosition = \"start\",\n render,\n ref,\n className,\n children,\n ...rest\n}: HeroServiceGridProps) {\n const cards = Children.toArray(children);\n\n return useRender({\n render,\n ref,\n defaultTagName: \"div\",\n props: {\n ...rest,\n \"data-image-position\": imagePosition,\n className: mergeClassName(styles.HeroServiceGrid, className)({}),\n children: (\n <>\n <FeaturedImage\n className={styles.Featured}\n src={src}\n alt={alt}\n imageRender={imageRender}\n action={action}\n />\n {cards.length > 0 && (\n <ul className={styles.Cards}>\n {cards.map((card, index) => (\n <li key={index} className={styles.Item}>\n {card}\n </li>\n ))}\n </ul>\n )}\n </>\n ),\n },\n });\n}\n","/*\n * The website's NavigationGrid: the title on `heading-xl` (`heading-md` below\n * 768px), 32px over a grid of cards 16px apart. From 1024px four columns\n * with rows of 300px; every card is two columns wide, except the first two\n * of an odd number, which are one (1 + 1 + 2, then 2 + 2). Two columns from\n * 768px, where the wide cards take a row of their own; one column below.\n * The title is the section's text colour.\n */\n.NavigationGrid {\n display: flex;\n min-width: 0;\n flex-direction: column;\n gap: var(--lea-spacing-4);\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.List {\n display: grid;\n grid-template-columns: minmax(0, 1fr);\n gap: var(--lea-spacing-2);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n/* The card fills its cell. */\n.Item {\n display: grid;\n min-width: 0;\n grid-template-columns: minmax(0, 1fr);\n}\n\n@media (min-width: 768px) {\n .List {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n }\n\n .Item {\n grid-column: span 2;\n }\n\n .List[data-odd] > .Item:nth-child(-n + 2) {\n grid-column: span 1;\n }\n}\n\n@media (min-width: 1024px) {\n .List {\n grid-template-columns: repeat(4, minmax(0, 1fr));\n /* The website's 300px rows; a long title makes its row taller. */\n grid-auto-rows: minmax(calc(var(--lea-spacing) * 37.5), auto);\n }\n}\n\n@media (max-width: 767.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 { Children, type ReactElement, type ReactNode, type Ref } from \"react\";\n\nimport { mergeClassName } from \"../../utils/merge-class-name\";\nimport styles from \"./navigation-grid.module.css\";\n\nexport type NavigationGridProps = Omit<\n useRender.ComponentProps<\"div\">,\n \"ref\" | \"title\" | \"children\"\n> & {\n /** The rendered element: the `<div>`, or the element passed to `render`. */\n ref?: Ref<HTMLElement>;\n /**\n * The heading over the cards. Left out, there is no heading and no room\n * kept for one.\n */\n title?: ReactNode;\n /** The element for the title, an `<h2>` by default: `titleRender={<h3 />}`. */\n titleRender?: ReactElement;\n /**\n * The cards: `LinkCard`s, three to five as on the website. Each one is put\n * in an item of a list.\n */\n children?: ReactNode;\n};\n\n/**\n * A grid of `LinkCard`s under a title, as the \"Hva kan vi hjelpe deg med?\"\n * block on leabank.no's customer service page.\n *\n * ```tsx\n * <Section surface=\"subtle\">\n * <NavigationGrid title=\"Hva kan vi hjelpe deg med?\">\n * <LinkCard render={<NextLink href=\"/lan\" />} title=\"Lån\" surface=\"muted\" />\n * <LinkCard render={<NextLink href=\"/sparing\" />} title=\"Sparing\" surface=\"contrast\" />\n * <LinkCard render={<NextLink href=\"/forsikring\" />} title=\"Forsikring\" surface=\"muted\" />\n * </NavigationGrid>\n * </Section>\n * ```\n *\n * From 1024px the cards are two to a row of four columns, each two columns\n * wide; with an odd number of cards the first row holds three, the first two\n * one column wide (1 + 1 + 2). Rows are 300px, or taller for a long title.\n * Between 768 and 1024px two columns, the wide cards taking a row of their\n * own; below 768px one column. The cards are a list.\n */\nexport function NavigationGrid({\n title,\n titleRender,\n render,\n ref,\n className,\n children,\n ...rest\n}: NavigationGridProps) {\n const cards = Children.toArray(children);\n const odd = cards.length >= 3 && cards.length % 2 === 1;\n\n const titleElement = useRender({\n enabled: title != null,\n render: titleRender,\n defaultTagName: \"h2\",\n props: { className: styles.Title, children: title },\n });\n\n return useRender({\n render,\n ref,\n defaultTagName: \"div\",\n props: {\n ...rest,\n className: mergeClassName(styles.NavigationGrid, className)({}),\n children: (\n <>\n {titleElement}\n <ul className={styles.List} data-odd={odd || undefined}>\n {cards.map((card, index) => (\n <li key={index} className={styles.Item}>\n {card}\n </li>\n ))}\n </ul>\n </>\n ),\n },\n });\n}\n","/*\n * The website's PartnersBlock: the rows one under the other (16px apart\n * below 768px, where each row is one column), then the controls centred,\n * 32px apart, with 32px above them and 64px below: two elevated buttons\n * (40px) and the dots between.\n */\n.PartnerList {\n min-width: 0;\n}\n\n.List {\n display: flex;\n flex-direction: column;\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.Controls {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--lea-spacing-4);\n padding-block: var(--lea-spacing-4) var(--lea-spacing-8);\n}\n\n/* \"Side 2 av 3\", for screen readers when the page turns. */\n.VisuallyHidden {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n}\n\n@media (max-width: 767.95px) {\n .List {\n gap: var(--lea-spacing-2);\n }\n}\n","import { createTranslation } from \"../../index\";\n\n/**\n * The words of `PartnerList`'s controls. `{page}` and `{count}` are replaced\n * with the page number and the number of pages. `collation` is the language\n * the partners are sorted in, as a BCP 47 tag for `Intl.Collator`.\n */\nexport const usePartnerListLabels = createTranslation({\n previous: {\n no: \"Forrige side\",\n se: \"Föregående sida\",\n fi: \"Edellinen sivu\",\n es: \"Página anterior\",\n },\n next: {\n no: \"Neste side\",\n se: \"Nästa sida\",\n fi: \"Seuraava sivu\",\n es: \"Página siguiente\",\n },\n status: {\n no: \"Side {page} av {count}\",\n se: \"Sida {page} av {count}\",\n fi: \"Sivu {page}/{count}\",\n es: \"Página {page} de {count}\",\n },\n collation: { no: \"nb\", se: \"sv\", fi: \"fi\", es: \"es\" },\n});\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { ChevronLeft, ChevronRight } from \"lucide-react\";\nimport {\n useMemo,\n useState,\n type ReactElement,\n type ReactNode,\n type Ref,\n} from \"react\";\n\nimport { IconButton, PaginationDots, PartnerRow } from \"../../index\";\nimport { mergeClassName } from \"../../utils/merge-class-name\";\nimport styles from \"./partner-list.module.css\";\nimport { usePartnerListLabels } from \"./use-partner-list-labels\";\n\n/** `alt` is required with a logo. */\ntype PartnerListLogo =\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>`, as on\n * `PartnerRow`: `next/image`, or an `<img loading=\"lazy\">`.\n */\n imageRender?: ReactElement;\n }\n | { src?: undefined; alt?: undefined; imageRender?: undefined };\n\n/** One partner in the list. */\nexport type PartnerListItem = PartnerListLogo & {\n /** The partner's name. The list is sorted by it. */\n name: string;\n /**\n * The text about the partner, beside the logo: usually `RichText` with the\n * CMS content, whose heading is the name.\n */\n content: ReactNode;\n};\n\nexport type PartnerListProps = Omit<\n useRender.ComponentProps<\"div\">,\n \"ref\" | \"children\" | \"defaultValue\" | \"onChange\"\n> & {\n /** The rendered element: the `<div>`, or the element passed to `render`. */\n ref?: Ref<HTMLElement>;\n /** The partners, in any order. */\n items: readonly PartnerListItem[];\n /**\n * How many partners a page shows.\n * @default 9\n */\n pageSize?: number;\n /** The page shown, counted from 1. Pass it to control the list. */\n value?: number;\n /**\n * The page shown first when the list is not controlled.\n * @default 1\n */\n defaultValue?: number;\n /** Called with the page number when the visitor turns the page. */\n onValueChange?: (page: number) => void;\n};\n\n/**\n * The website's list of partners, as on leabank.no/om-oss/samarbeidspartnere:\n * a `PartnerRow` per partner, sorted by name, nine to a page, with previous\n * and next buttons and a dot per page under them.\n *\n * ```tsx\n * <Section surface=\"subtle\" width=\"wide\">\n * <PartnerList\n * items={partners.map((partner) => ({\n * name: partner.name,\n * content: <RichText>…</RichText>,\n * src: partner.logo,\n * alt: \"\",\n * }))}\n * />\n * </Section>\n * ```\n *\n * The partners are sorted in the `LeaProvider` language (Å after Z in\n * Norwegian and Swedish, Ä and Ö in Swedish and Finnish order), so the\n * order follows the market. The controls show from two pages: elevated\n * buttons named \"Forrige side\" and \"Neste side\", which stay focusable at the\n * first and last page, and `PaginationDots`; the new page is announced as\n * \"Side 2 av 3\". The list takes the width it is given: on the website a\n * `Section` with `width=\"wide\"`.\n */\nexport function PartnerList({\n items,\n pageSize = 9,\n value: valueProp,\n defaultValue = 1,\n onValueChange,\n render,\n ref,\n className,\n ...rest\n}: PartnerListProps) {\n const labels = usePartnerListLabels();\n const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);\n\n const sorted = useMemo(() => {\n const collator = new Intl.Collator(labels.collation, {\n sensitivity: \"base\",\n });\n return [...items].sort((a, b) => collator.compare(a.name, b.name));\n }, [items, labels.collation]);\n\n const size = Math.max(1, Math.floor(pageSize));\n const pageCount = Math.max(1, Math.ceil(sorted.length / size));\n const page = Math.min(pageCount, Math.max(1, valueProp ?? uncontrolledValue));\n const start = (page - 1) * size;\n\n const select = (next: number) => {\n const clamped = Math.min(pageCount, Math.max(1, next));\n if (valueProp === undefined) setUncontrolledValue(clamped);\n if (clamped !== page) onValueChange?.(clamped);\n };\n\n return useRender({\n enabled: sorted.length > 0,\n render,\n ref,\n defaultTagName: \"div\",\n props: {\n ...rest,\n className: mergeClassName(styles.PartnerList, className)({}),\n children: (\n <>\n <ul className={styles.List}>\n {sorted\n .slice(start, start + size)\n .map(({ name, content, ...logo }, index) => (\n <PartnerRow\n key={`${start + index}-${name}`}\n {...logo}\n render={<li />}\n >\n {content}\n </PartnerRow>\n ))}\n </ul>\n {pageCount > 1 && (\n <div className={styles.Controls}>\n <IconButton\n variant=\"elevated\"\n color=\"secondary\"\n aria-label={labels.previous}\n disabled={page === 1}\n focusableWhenDisabled\n onClick={() => select(page - 1)}\n >\n <ChevronLeft aria-hidden />\n </IconButton>\n <PaginationDots\n count={pageCount}\n value={page}\n onValueChange={select}\n />\n <IconButton\n variant=\"elevated\"\n color=\"secondary\"\n aria-label={labels.next}\n disabled={page === pageCount}\n focusableWhenDisabled\n onClick={() => select(page + 1)}\n >\n <ChevronRight aria-hidden />\n </IconButton>\n <span role=\"status\" className={styles.VisuallyHidden}>\n {labels.status\n .replace(\"{page}\", String(page))\n .replace(\"{count}\", String(pageCount))}\n </span>\n </div>\n )}\n </>\n ),\n },\n });\n}\n","/*\n * The website's FeaturePaymentBlock on a `Panel` (64px inside, 32px below\n * 768px). From 1024px a text column of at most 560px and a photo column 64px\n * apart, centred on each other; the photo at most 435px wide at the end of\n * its column. In the text column, 64px apart (32px below 1024px): the logos,\n * the title (`heading-xl`, `heading-md` below 768px) and the text (`body-lg`)\n * 32px apart, the actions 16px apart, the price example. Below 1024px one\n * column, 32px between the text and the photo.\n */\n.Grid {\n display: grid;\n grid-template-columns: minmax(0, 1fr);\n align-items: center;\n gap: var(--lea-spacing-4);\n}\n\n.Content {\n display: flex;\n min-width: 0;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--lea-spacing-4);\n}\n\n/* A line between two logos, 32px from each. It hangs after a logo, so when\n the row wraps on a phone it ends the first row, as in Figma, rather than\n opening the next. */\n.Logos {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--lea-spacing-2) var(--lea-spacing-4);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.LogoItem {\n display: flex;\n align-items: center;\n gap: var(--lea-spacing-4);\n}\n\n.LogoItem:not(:last-child)::after {\n content: \"\";\n flex: none;\n width: 1px;\n height: var(--lea-spacing-5);\n background-color: var(--lea-palette-border-base);\n}\n\n/*\n * A height of its own, not only a cap: an SVG with a viewBox and no width or\n * height attribute (the Apple Pay logo) has no size of its own and came out\n * 0 × 0. The width follows the logo's ratio.\n */\n.Logo {\n display: block;\n width: auto;\n max-width: 100%;\n height: var(--lea-spacing-6);\n object-fit: contain;\n}\n\n.Text {\n display: flex;\n flex-direction: column;\n gap: var(--lea-spacing-4);\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.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.Description > * {\n margin: 0;\n}\n\n.Description > * + * {\n margin-block-start: var(--lea-spacing-1);\n}\n\n.Actions {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--lea-spacing-2);\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.Media {\n display: flex;\n min-width: 0;\n}\n\n.PaymentFeature[data-image-position=\"start\"] .Media {\n order: -1;\n}\n\n.Image {\n display: block;\n width: 100%;\n /* The website's max-width: 435px. */\n max-width: calc(var(--lea-spacing) * 54.375);\n height: auto;\n}\n\n@media (min-width: 1024px) {\n .PaymentFeature[data-has-image] .Grid {\n /* The website's minmax(auto, 560px): the text column stops at 560px. */\n grid-template-columns: minmax(0, calc(var(--lea-spacing) * 70)) minmax(\n 0,\n 1fr\n );\n }\n\n .PaymentFeature[data-image-position=\"start\"][data-has-image] .Grid {\n grid-template-columns: minmax(0, 1fr) minmax(\n 0,\n calc(var(--lea-spacing) * 70)\n );\n }\n\n .Grid,\n .Content {\n gap: var(--lea-spacing-8);\n }\n\n .Media {\n justify-content: flex-end;\n }\n}\n\n@media (max-width: 767.95px) {\n .Logo {\n height: var(--lea-spacing-4);\n }\n\n .LogoItem:not(:last-child)::after {\n height: var(--lea-spacing-4);\n }\n\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 type { ReactElement, ReactNode } from \"react\";\n\nimport { Panel, type PanelProps } from \"../../index\";\nimport { Paragraphs } from \"../paragraphs\";\nimport styles from \"./payment-feature.module.css\";\n\nexport type PaymentFeatureImagePosition = \"start\" | \"end\";\n\n/** A logo in the row at the top: 48px tall, 32px below 768px. */\nexport type PaymentFeatureLogo = {\n /** The logo file. */\n src: string;\n /** The name of the brand, for screen readers. */\n alt: string;\n /**\n * The image element to render instead of the plain `<img>`, such as\n * `next/image` with the logo's own `width` and `height`.\n */\n imageRender?: ReactElement;\n};\n\n/** `alt` is required with a photo, `\"\"` when it is decoration. */\ntype PaymentFeatureImage =\n | {\n /** The photo beside the text. Without one, the text takes the card. */\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`. Its own props win over `src` and `alt`.\n */\n imageRender?: ReactElement;\n }\n | { src?: undefined; alt?: undefined; imageRender?: undefined };\n\nexport type PaymentFeatureProps = Omit<\n PanelProps,\n \"title\" | \"children\" | \"size\"\n> &\n PaymentFeatureImage & {\n /** The logos of the payment brands, in a row over the text. */\n logos?: readonly PaymentFeatureLogo[];\n /** The title. */\n title: ReactNode;\n /** The element for the title, an `<h2>` by default: `titleRender={<h3 />}`. */\n titleRender?: ReactElement;\n /**\n * The text under the title. A string becomes one paragraph; pass `<p>`\n * elements for several.\n */\n text?: ReactNode;\n /**\n * The calls to action, in a row that wraps: an `ActionButton`, a `Link`\n * with `arrow`, or both.\n */\n actions?: ReactNode;\n /** The price example under the actions, in the small print. */\n priceExample?: ReactNode;\n /**\n * Where the photo sits: `\"start\"` before the text (on the left, or on\n * top below 1024px), `\"end\"` after it.\n * @default \"end\"\n */\n imagePosition?: PaymentFeatureImagePosition;\n };\n\n/**\n * A payment feature on a white `Panel`: a row of brand logos, a title, a\n * text and the calls to action in one column, and a photo in the other.\n *\n * ```tsx\n * <Section surface=\"subtle\">\n * <PaymentFeature\n * logos={[{ src: \"/logoer/vipps.svg\", alt: \"Vipps\" }, { src: \"/logoer/visa.svg\", alt: \"Visa\" }]}\n * title=\"Betal med Lea Bank i nettbutikken\"\n * text=\"Del opp betalingen eller betal senere når du handler hos våre partnere.\"\n * actions={<ActionButton render={<NextLink href=\"/betaling\" />}>Les mer</ActionButton>}\n * src=\"/bilder/betaling.jpg\"\n * alt=\"\"\n * />\n * </Section>\n * ```\n *\n * From 1024px the text column is at most 560px and the photo, at most 435px\n * wide, sits at the end of the other column; below 1024px they stack 32px\n * apart. The logos are 48px tall (32px below 768px), as wide as their ratio\n * makes them, with a line between them, and wrap on a narrow screen. An SVG\n * logo needs only a `viewBox`. Put it on a coloured section: the panel is\n * white.\n */\nexport function PaymentFeature({\n logos,\n title,\n titleRender,\n text,\n actions,\n priceExample,\n src,\n alt,\n imageRender,\n imagePosition = \"end\",\n className,\n ...rest\n}: PaymentFeatureProps) {\n const titleElement = useRender({\n render: titleRender,\n defaultTagName: \"h2\",\n props: { className: styles.Title, 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 (\n <Panel\n {...rest}\n className={[styles.PaymentFeature, className].filter(Boolean).join(\" \")}\n data-image-position={imagePosition}\n data-has-image={image != null || undefined}\n >\n <div className={styles.Grid}>\n <div className={styles.Content}>\n {logos != null && logos.length > 0 && (\n <ul className={styles.Logos}>\n {logos.map((logo, index) => (\n <PaymentFeatureLogoItem key={index} {...logo} />\n ))}\n </ul>\n )}\n <div className={styles.Text}>\n {titleElement}\n {text != null && (\n <Paragraphs className={styles.Description}>{text}</Paragraphs>\n )}\n </div>\n {actions != null && <div className={styles.Actions}>{actions}</div>}\n {priceExample != null && (\n <p className={styles.Price}>{priceExample}</p>\n )}\n </div>\n {image != null && <div className={styles.Media}>{image}</div>}\n </div>\n </Panel>\n );\n}\n\nfunction PaymentFeatureLogoItem({ src, alt, imageRender }: PaymentFeatureLogo) {\n const image = useRender({\n render: imageRender,\n defaultTagName: \"img\",\n props: { src, alt, className: styles.Logo },\n });\n\n return <li className={styles.LogoItem}>{image}</li>;\n}\n","/*\n * The website's FlexiblePromoBlock. From 1024px two equal columns 600px tall\n * and 16px apart: a `FeaturedImage` filling one, and in the other one or two\n * photos (`huge` corners) over a beige block (32px inside, `huge` corners)\n * with the title on `title-xl` in capitals and the subtitle on `body-lg`, 8px\n * apart. The photos take the room the block leaves. Below 1024px everything\n * stacks 16px apart, the photos at 1 : 0.5 and the title on `title-md`; the\n * two smaller photos stack below 768px.\n */\n.PromoGrid {\n display: grid;\n min-width: 0;\n grid-template-columns: minmax(0, 1fr);\n gap: var(--lea-spacing-2);\n}\n\n/* Two classes, to win over FeaturedImage's own 1 : 0.7 whichever stylesheet\n loads last. */\n.PromoGrid .Featured {\n aspect-ratio: 1 / 0.5;\n}\n\n.Content {\n display: flex;\n min-width: 0;\n min-height: 0;\n flex-direction: column;\n gap: var(--lea-spacing-2);\n}\n\n.Photos {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: var(--lea-spacing-2);\n}\n\n.Photos[data-count=\"1\"] {\n grid-template-columns: minmax(0, 1fr);\n}\n\n.Photo {\n display: block;\n width: 100%;\n aspect-ratio: 1 / 0.5;\n border-radius: var(--lea-radius-huge);\n /* Behind the photo while it loads. */\n background-color: var(--lea-palette-bg-muted);\n object-fit: cover;\n}\n\n.Block {\n display: flex;\n box-sizing: border-box;\n flex-direction: column;\n justify-content: center;\n gap: var(--lea-spacing-1);\n padding: var(--lea-spacing-4);\n border-radius: var(--lea-radius-huge);\n background-color: var(--lea-palette-bg-muted);\n color: var(--lea-palette-text-base);\n}\n\n.Title {\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 text-transform: uppercase;\n overflow-wrap: break-word;\n}\n\n.Subtitle {\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.Subtitle > * {\n margin: 0;\n}\n\n.Subtitle > * + * {\n margin-block-start: var(--lea-spacing-1);\n}\n\n@media (min-width: 1024px) {\n .PromoGrid {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n grid-template-rows: minmax(0, 1fr);\n /* The website's height: 600px. */\n height: calc(var(--lea-spacing) * 75);\n }\n\n .PromoGrid .Featured {\n aspect-ratio: auto;\n }\n\n .PromoGrid[data-image-position=\"end\"] .Featured {\n order: 1;\n }\n\n .Photos {\n flex: 1 1 0;\n min-height: 0;\n grid-template-rows: minmax(0, 1fr);\n }\n\n .Photo {\n height: 100%;\n aspect-ratio: auto;\n }\n\n /* Without photos the block takes the whole column. */\n .Block:only-child {\n flex: 1 1 auto;\n }\n\n .Title {\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\n@media (max-width: 767.95px) {\n .Photos {\n grid-template-columns: minmax(0, 1fr);\n }\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport type { ReactElement, ReactNode, Ref } from \"react\";\n\nimport { FeaturedImage } from \"../../index\";\nimport { mergeClassName } from \"../../utils/merge-class-name\";\nimport { Paragraphs } from \"../paragraphs\";\nimport styles from \"./promo-grid.module.css\";\n\nexport type PromoGridImagePosition = \"start\" | \"end\";\n\n/** One of the smaller photos over the text. */\nexport type PromoGridImage = {\n /** The photo. */\n src: string;\n /** What the photo shows, for screen readers; `\"\"` when it is decoration. */\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\nexport type PromoGridProps = Omit<\n useRender.ComponentProps<\"div\">,\n \"ref\" | \"title\" | \"children\"\n> & {\n /** The rendered element: the `<div>`, or the element passed to `render`. */\n ref?: Ref<HTMLElement>;\n /** The large photo. */\n src: string;\n /** What the large photo shows, for screen readers; `\"\"` when it is decoration. */\n alt: string;\n /**\n * The image element to render for the large photo instead of the plain\n * `<img>`, as on `FeaturedImage`.\n */\n imageRender?: ReactElement;\n /** A call to action on the large photo: an `ActionButton`. */\n action?: ReactNode;\n /** One or two smaller photos over the text, side by side. */\n images?:\n | readonly [PromoGridImage]\n | readonly [PromoGridImage, PromoGridImage];\n /** The title on the beige block, in capitals. */\n title: ReactNode;\n /** The element for the title, an `<h2>` by default: `titleRender={<h3 />}`. */\n titleRender?: ReactElement;\n /**\n * The text under the title. A string becomes one paragraph; pass `<p>`\n * elements for several.\n */\n subtitle?: ReactNode;\n /**\n * Where the large photo sits from 1024px: `\"start\"` (on the left) or\n * `\"end\"`. Below 1024px it is always on top.\n * @default \"start\"\n */\n imagePosition?: PromoGridImagePosition;\n};\n\n/**\n * A promotion of photos and a title: a large `FeaturedImage` on one side, and\n * on the other one or two photos over a beige block with the title in\n * capitals, as on leabank.no's about page.\n *\n * ```tsx\n * <Section>\n * <PromoGrid\n * src=\"/bilder/kontor.jpg\"\n * alt=\"\"\n * action={<ActionButton render={<NextLink href=\"/lan\" />}>Se våre lån</ActionButton>}\n * images={[{ src: \"/bilder/team.jpg\", alt: \"\" }]}\n * title=\"Lån til det du trenger\"\n * subtitle=\"Vi hjelper deg med lån uten sikkerhet til det du har lyst på.\"\n * />\n * </Section>\n * ```\n *\n * From 1024px two equal columns 600px tall, 16px apart, the smaller photos\n * taking the room the block leaves. Below 1024px everything stacks, the\n * photos at 1 : 0.5, the two smaller ones side by side down to 768px. The\n * block is beige with dark text on any section.\n */\nexport function PromoGrid({\n src,\n alt,\n imageRender,\n action,\n images,\n title,\n titleRender,\n subtitle,\n imagePosition = \"start\",\n render,\n ref,\n className,\n ...rest\n}: PromoGridProps) {\n const titleElement = useRender({\n render: titleRender,\n defaultTagName: \"h2\",\n props: { className: styles.Title, children: title },\n });\n\n return useRender({\n render,\n ref,\n defaultTagName: \"div\",\n props: {\n ...rest,\n \"data-image-position\": imagePosition,\n className: mergeClassName(styles.PromoGrid, className)({}),\n children: (\n <>\n <FeaturedImage\n className={styles.Featured}\n src={src}\n alt={alt}\n imageRender={imageRender}\n action={action}\n />\n <div className={styles.Content}>\n {images != null && images.length > 0 && (\n <div className={styles.Photos} data-count={images.length}>\n {images.map((image, index) => (\n <PromoGridPhoto key={index} {...image} />\n ))}\n </div>\n )}\n <div className={styles.Block}>\n {titleElement}\n {subtitle != null && (\n <Paragraphs className={styles.Subtitle}>{subtitle}</Paragraphs>\n )}\n </div>\n </div>\n </>\n ),\n },\n });\n}\n\nfunction PromoGridPhoto({ src, alt, imageRender }: PromoGridImage) {\n return useRender({\n render: imageRender,\n defaultTagName: \"img\",\n props: { src, alt, className: styles.Photo },\n });\n}\n","/*\n * The website's SectionContainer: a band in one of the five section colours,\n * 112px or 64px above and below (64 / 32 below 768px), 64px at the sides (16\n * below 768px), and the content in a column of at most `--lea-width-section`\n * (1312px), centred. Wide and narrow content is 75 % and 60 % of that column,\n * centred, from 1024px.\n *\n * `--section-bg` is inherited by what is inside: `Carousel` fades its edges\n * into it.\n */\n.Section {\n --section-bg: var(--lea-palette-bg-base);\n --section-text: var(--lea-palette-text-base);\n /* 112px, the website's `huge`: off the spacing scale. */\n --section-space-lg: calc(var(--lea-spacing) * 14);\n --section-space-sm: var(--lea-spacing-8);\n --section-side: var(--lea-spacing-8);\n\n box-sizing: border-box;\n width: 100%;\n padding-inline: var(--section-side);\n background-color: var(--section-bg);\n color: var(--section-text);\n}\n\n.Section[data-surface=\"subtle\"] {\n --section-bg: var(--lea-palette-bg-subtle);\n}\n\n.Section[data-surface=\"muted\"] {\n --section-bg: var(--lea-palette-bg-muted);\n}\n\n.Section[data-surface=\"contrast\"] {\n --section-bg: var(--lea-palette-bg-contrast);\n --section-text: var(--lea-palette-text-contrast);\n}\n\n.Section[data-surface=\"contrast-accent\"] {\n --section-bg: var(--lea-palette-bg-contrast-accent);\n --section-text: var(--lea-palette-text-contrast);\n}\n\n.Section[data-spacing-top=\"lg\"] {\n padding-block-start: var(--section-space-lg);\n}\n\n.Section[data-spacing-top=\"sm\"] {\n padding-block-start: var(--section-space-sm);\n}\n\n.Section[data-spacing-bottom=\"lg\"] {\n padding-block-end: var(--section-space-lg);\n}\n\n.Section[data-spacing-bottom=\"sm\"] {\n padding-block-end: var(--section-space-sm);\n}\n\n.Content {\n box-sizing: border-box;\n width: 100%;\n max-width: var(--lea-width-section);\n margin-inline: auto;\n}\n\n@media (min-width: 1024px) {\n .Content[data-width=\"wide\"] {\n max-width: calc(var(--lea-width-section) * 0.75);\n width: 75%;\n }\n\n .Content[data-width=\"narrow\"] {\n max-width: calc(var(--lea-width-section) * 0.6);\n width: 60%;\n }\n}\n\n@media (max-width: 767.95px) {\n .Section {\n --section-space-lg: var(--lea-spacing-8);\n --section-space-sm: var(--lea-spacing-4);\n --section-side: var(--lea-spacing-2);\n }\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport type { Ref } from \"react\";\n\nimport type { SurfaceRoles } from \"../../components/color-roles\";\nimport { mergeClassName } from \"../../utils/merge-class-name\";\nimport styles from \"./section.module.css\";\n\nexport type SectionSurface = SurfaceRoles<\n \"base\" | \"subtle\" | \"muted\" | \"contrast\" | \"contrast-accent\"\n>;\nexport type SectionSpacing = \"none\" | \"sm\" | \"lg\";\nexport type SectionWidth = \"full\" | \"wide\" | \"narrow\";\n\nexport type SectionProps = Omit<useRender.ComponentProps<\"section\">, \"ref\"> & {\n /** The rendered element: the `<section>`, or the element passed to `render`. */\n ref?: Ref<HTMLElement>;\n /**\n * The colour of the band, 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 is white;\n * give a `Link` inside `color=\"contrast\"` there.\n * @default \"base\"\n */\n surface?: SectionSurface;\n /**\n * The room above the content: `\"lg\"` 112px (64px below 768px), `\"sm\"` 64px\n * (32px), `\"none\"`.\n * @default \"lg\"\n */\n spacingTop?: SectionSpacing;\n /**\n * The room below the content, in the same steps as `spacingTop`.\n * @default \"lg\"\n */\n spacingBottom?: SectionSpacing;\n /**\n * How much of the content column the content takes, centred: `\"full\"`,\n * `\"wide\"` (75 %) or `\"narrow\"` (60 %). Below 1024px it is always the full\n * column.\n * @default \"full\"\n */\n width?: SectionWidth;\n};\n\n/**\n * A band of a website page: the colour from edge to edge, the content in a\n * column of at most 1312px in the middle, 64px from the sides (16px below\n * 768px). Every block on leabank.no sits in one.\n *\n * ```tsx\n * <Section surface=\"subtle\" spacingTop=\"none\">\n * <SectionHeader title=\"Vi gjør det enkelt å ta smarte økonomiske valg\" />\n * </Section>\n * ```\n *\n * The section sets the text colour for what is inside it, white on the dark\n * surfaces, so text, a vertical `MediaCard` and the sections that have no\n * colour of their own follow it. Renders a `section`; pass `render` for\n * another element.\n */\nexport function Section({\n surface = \"base\",\n spacingTop = \"lg\",\n spacingBottom = \"lg\",\n width = \"full\",\n render,\n ref,\n className,\n children,\n ...rest\n}: SectionProps) {\n return useRender({\n render,\n ref,\n defaultTagName: \"section\",\n props: {\n ...rest,\n \"data-surface\": surface,\n \"data-spacing-top\": spacingTop,\n \"data-spacing-bottom\": spacingBottom,\n className: mergeClassName(styles.Section, className)({}),\n children: (\n <div className={styles.Content} data-width={width}>\n {children}\n </div>\n ),\n },\n });\n}\n","/*\n * The website's SimpleHeaderBlock: everything centred, the text in a column\n * 16px apart (the eyebrow `heading-xs`, the title `heading-xl` or\n * `heading-md`, the subtitle `body-lg` at most 720px wide), the actions 32px\n * under it in a row that wraps, and the price example 32px under that. No\n * colour of its own: the text is the section's.\n */\n.SectionHeader {\n display: flex;\n min-width: 0;\n flex-direction: column;\n align-items: center;\n gap: var(--lea-spacing-4);\n text-align: center;\n}\n\n.Text {\n display: flex;\n max-width: 100%;\n flex-direction: column;\n align-items: center;\n gap: var(--lea-spacing-2);\n}\n\n.Eyebrow {\n margin: 0;\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.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.SectionHeader[data-title-size=\"sm\"] .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.Subtitle {\n /* The website's max-width: 720px. */\n max-width: calc(var(--lea-spacing) * 90);\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.Subtitle > * {\n margin: 0;\n}\n\n.Subtitle > * + * {\n margin-block-start: var(--lea-spacing-1);\n}\n\n.Actions {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: center;\n gap: var(--lea-spacing-4);\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@media (max-width: 767.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 type { ReactElement, ReactNode, Ref } from \"react\";\n\nimport { mergeClassName } from \"../../utils/merge-class-name\";\nimport { Paragraphs } from \"../paragraphs\";\nimport styles from \"./section-header.module.css\";\n\nexport type SectionHeaderTitleSize = \"sm\" | \"lg\";\n\nexport type SectionHeaderProps = Omit<\n useRender.ComponentProps<\"div\">,\n \"ref\" | \"title\" | \"children\"\n> & {\n /** The rendered element: the `<div>`, or the element passed to `render`. */\n ref?: Ref<HTMLElement>;\n /** The short line above the title. */\n eyebrow?: ReactNode;\n /** The title of the section. */\n title: ReactNode;\n /**\n * The element for the title, an `<h2>` by default. Pass another level where\n * the page outline needs one: `titleRender={<h1 />}`.\n */\n titleRender?: ReactElement;\n /**\n * `\"lg\"`: 41px (26px below 768px). `\"sm\"`: 26px at every width.\n * @default \"lg\"\n */\n titleSize?: SectionHeaderTitleSize;\n /**\n * The text under the title, at most 720px wide. A string becomes one\n * paragraph; pass `<p>` elements for several.\n */\n subtitle?: ReactNode;\n /**\n * The calls to action, in a centred row that wraps: an `ActionButton`,\n * a `Link` with `arrow`, or both. The row is only there when this is.\n */\n actions?: ReactNode;\n /** The price example under the actions, in the small print. */\n priceExample?: ReactNode;\n};\n\n/**\n * The centred heading of a website section: a short line, the title, a text\n * of at most 720px and a row of calls to action, as at the top of the\n * sections on leabank.no.\n *\n * ```tsx\n * <Section>\n * <SectionHeader\n * eyebrow=\"Om Lea Bank\"\n * title=\"Vi gjør det enkelt å ta smarte økonomiske valg\"\n * subtitle=\"Lea Bank er en digital bank …\"\n * actions={<ActionButton render={<NextLink href=\"/om-oss\" />}>Les mer om oss</ActionButton>}\n * />\n * </Section>\n * ```\n *\n * It takes the text colour of its section. The parts are 16px apart and the\n * actions 32px under them; the actions are 32px apart.\n */\nexport function SectionHeader({\n eyebrow,\n title,\n titleRender,\n titleSize = \"lg\",\n subtitle,\n actions,\n priceExample,\n render,\n ref,\n className,\n ...rest\n}: SectionHeaderProps) {\n const titleElement = useRender({\n render: titleRender,\n defaultTagName: \"h2\",\n props: { className: styles.Title, children: title },\n });\n\n return useRender({\n render,\n ref,\n defaultTagName: \"div\",\n props: {\n ...rest,\n \"data-title-size\": titleSize,\n className: mergeClassName(styles.SectionHeader, className)({}),\n children: (\n <>\n <div className={styles.Text}>\n {eyebrow != null && <p className={styles.Eyebrow}>{eyebrow}</p>}\n {titleElement}\n {subtitle != null && (\n <Paragraphs className={styles.Subtitle}>{subtitle}</Paragraphs>\n )}\n </div>\n {actions != null && <div className={styles.Actions}>{actions}</div>}\n {priceExample != null && (\n <p className={styles.Price}>{priceExample}</p>\n )}\n </>\n ),\n },\n });\n}\n","/*\n * The website's ServiceCardsBlock (Display = Grid): cards 16px apart. From\n * 1024px three columns with rows of 280, 280 and 320px: the first card takes\n * two columns and two rows (576px tall), the second the third column of the\n * same rows; a third card the whole row under them, or with a fourth, the\n * first column of it (the fourth the other two). Below 1024px one column:\n * a card with a photo 440px tall, one without as tall as its text.\n */\n.ServiceCardGrid {\n min-width: 0;\n}\n\n.List {\n display: grid;\n grid-template-columns: minmax(0, 1fr);\n gap: var(--lea-spacing-2);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n/* The card fills its cell, and its photo shrinks to fit it. */\n.Item {\n display: grid;\n min-width: 0;\n min-height: 0;\n grid-template-columns: minmax(0, 1fr);\n grid-template-rows: minmax(0, 1fr);\n}\n\n@media (max-width: 1023.95px) {\n /* A card with a photo (an <img> straight inside the card) is 440px tall,\n the photo taking what the text leaves, as the website's\n minmax(auto, 440px) rows; a card without one is as tall as its text. */\n .Item:has(> * > img) {\n height: calc(var(--lea-spacing) * 55);\n }\n}\n\n@media (min-width: 1024px) {\n .List {\n grid-template-columns: repeat(3, minmax(0, 1fr));\n /* The website's rows: 280 + 280 for the first two cards, 320 for the\n row under them. Fixed, so a photo's own height never sets them: the\n photo takes what the text leaves. */\n grid-template-rows: repeat(2, calc(var(--lea-spacing) * 35));\n grid-auto-rows: calc(var(--lea-spacing) * 40);\n }\n\n .Item:nth-child(1) {\n grid-column: 1 / 3;\n grid-row: 1 / 3;\n }\n\n .Item:nth-child(2) {\n grid-column: 3 / 4;\n grid-row: 1 / 3;\n }\n\n .List[data-count=\"3\"] > .Item:nth-child(3) {\n grid-column: 1 / 4;\n }\n\n .List[data-count=\"4\"] > .Item:nth-child(3) {\n grid-column: 1 / 2;\n }\n\n .List[data-count=\"4\"] > .Item:nth-child(4) {\n grid-column: 2 / 4;\n }\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { Children, type ReactNode, type Ref } from \"react\";\n\nimport { mergeClassName } from \"../../utils/merge-class-name\";\nimport styles from \"./service-card-grid.module.css\";\n\nexport type ServiceCardGridProps = 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 * The cards: one to four `ServiceCard`s, in the order of the website's\n * grid. The first two are `layout=\"stacked\"` (the default); give the third\n * and fourth `layout=\"row\"`. Each one is put in an item of a list.\n */\n children?: ReactNode;\n};\n\n/**\n * The website's grid of service cards, as on leabank.no/lan: a large card\n * and a narrow one side by side, then a wide row under them.\n *\n * ```tsx\n * <Section surface=\"muted\">\n * <ServiceCardGrid>\n * <ServiceCard render={<NextLink href=\"/lan/forbrukslan\" />} title=\"Forbrukslån\" … />\n * <ServiceCard render={<NextLink href=\"/lan/refinansiering\" />} title=\"Refinansiering\" … />\n * <ServiceCard render={<NextLink href=\"/sparing\" />} layout=\"row\" title=\"Sparing\" … />\n * <ServiceCard render={<NextLink href=\"/betalingsforsikring\" />} layout=\"row\" title=\"Betalingsforsikring\" … />\n * </ServiceCardGrid>\n * </Section>\n * ```\n *\n * From 1024px three columns: the first card two columns wide and the second\n * one, both 576px tall; a third card takes the row under them (320px) on its\n * own, or the first column of it with a fourth in the other two. Below\n * 1024px one column, each card at most 440px tall. The cards are a list.\n */\nexport function ServiceCardGrid({\n render,\n ref,\n className,\n children,\n ...rest\n}: ServiceCardGridProps) {\n const cards = Children.toArray(children);\n\n return useRender({\n render,\n ref,\n defaultTagName: \"div\",\n props: {\n ...rest,\n className: mergeClassName(styles.ServiceCardGrid, className)({}),\n children: (\n <ul className={styles.List} data-count={cards.length}>\n {cards.map((card, index) => (\n <li key={index} className={styles.Item}>\n {card}\n </li>\n ))}\n </ul>\n ),\n },\n });\n}\n","/*\n * Figma's `.ArrowRevealLink` at `md`, the website's `DropdownLink`: 16px\n * text 4px in from the start, and a purple arrow that grows from nothing to\n * 24px on hover while the text turns purple and moves 4px further in. The\n * arrow's room is its width, so the line keeps its height.\n */\n.ArrowRevealLink {\n display: inline-flex;\n max-width: 100%;\n align-items: center;\n gap: var(--lea-spacing-0-5);\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 letter-spacing: var(--lea-text-body-md-letter-spacing);\n line-height: var(--lea-text-body-md-line-height);\n text-decoration: none;\n}\n\n.Arrow {\n display: flex;\n overflow: hidden;\n width: 0;\n height: var(--lea-spacing-3);\n flex-shrink: 0;\n align-items: center;\n color: var(--lea-palette-primary-base);\n opacity: 0;\n transition:\n width var(--lea-duration-normal) var(--lea-easing-standard),\n opacity var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n.Arrow svg {\n width: var(--lea-spacing-3);\n height: var(--lea-spacing-3);\n flex-shrink: 0;\n}\n\n.Text {\n min-width: 0;\n transition:\n padding-inline-start var(--lea-duration-normal) var(--lea-easing-standard),\n color var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n@media (hover: hover) {\n .ArrowRevealLink:not([data-size]):hover .Arrow {\n width: var(--lea-spacing-3);\n opacity: 1;\n }\n\n .ArrowRevealLink:not([data-size]):hover .Text {\n padding-inline-start: var(--lea-spacing-0-5);\n color: var(--lea-palette-primary-base);\n }\n}\n\n/*\n * `lg`, the website's `SidebarLink`: 18px text from the start; the marked\n * link (`data-marked`, the current page) has the 24px arrow before it (4px\n * apart), and the others none. When the mark moves to another link, its\n * arrow grows in while the old one shrinks away, as the `md` arrow does on\n * hover. The arrow sits on the first line, so a long label wraps under its\n * text. Hover underlines the text: no movement.\n */\n.ArrowRevealLink[data-size=\"lg\"] {\n align-items: flex-start;\n /* The arrow's room holds the 4px to the text: an unmarked link starts at the edge. */\n gap: 0;\n font-size: var(--lea-text-body-lg-font-size);\n font-weight: var(--lea-text-body-lg-font-weight);\n letter-spacing: var(--lea-text-body-lg-letter-spacing);\n line-height: var(--lea-text-body-lg-line-height);\n}\n\n.ArrowRevealLink[data-size=\"lg\"] .Arrow {\n height: calc(var(--lea-text-body-lg-line-height) * 1em);\n}\n\n.ArrowRevealLink[data-size=\"lg\"][data-marked] .Arrow {\n width: calc(var(--lea-spacing-3) + var(--lea-spacing-0-5));\n opacity: 1;\n}\n\n@media (hover: hover) {\n .ArrowRevealLink[data-size=\"lg\"]:hover .Text {\n text-decoration: underline;\n }\n}\n\n.ArrowRevealLink:focus-visible {\n border-radius: var(--lea-radius-sm);\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Arrow,\n .Text {\n transition-duration: 0.01ms;\n }\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { ArrowRight } from \"lucide-react\";\n\nimport { mergeClassName } from \"../utils/merge-class-name\";\nimport styles from \"./arrow-reveal-link.module.css\";\n\nexport type ArrowRevealLinkProps = useRender.ComponentProps<\"a\"> & {\n /**\n * `\"md\"`: the header's menus, 16px, the arrow growing in on hover. `\"lg\"`:\n * the `SideNav`, 18px, underlined on hover, the arrow before the\n * `marked` link only.\n * @default \"md\"\n */\n size?: \"md\" | \"lg\";\n /**\n * `lg` only: the arrow shows before this link, and grows in when it is\n * set. `SideNav` marks the current page's link, and a link as soon as it\n * is clicked.\n */\n marked?: boolean;\n};\n\n/**\n * Internal: Figma's `.ArrowRevealLink`. At `md`, the links of the header's\n * menus: a plain 16px link whose purple arrow grows in on hover while the\n * text turns purple and steps aside, as the website's `DropdownLink`. At\n * `lg`, the links of `SideNav`: 18px, underlined on hover, and the arrow\n * marks the current page (`marked`), as the website's `SidebarLink`. Not\n * exported.\n *\n * Renders an `<a>`, or the element passed to `render`: a router link, or the\n * `NavigationMenu.Link` of the desktop menu, which renders the page's own\n * link in turn.\n */\nexport function ArrowRevealLink({\n render,\n className,\n children,\n size = \"md\",\n marked = false,\n ...rest\n}: ArrowRevealLinkProps) {\n return useRender({\n render,\n defaultTagName: \"a\",\n props: {\n ...rest,\n \"data-size\": size === \"md\" ? undefined : size,\n \"data-marked\": marked ? \"\" : undefined,\n className: mergeClassName(styles.ArrowRevealLink, className)({}),\n children: (\n <>\n <span aria-hidden className={styles.Arrow}>\n <ArrowRight />\n </span>\n <span className={styles.Text}>{children}</span>\n </>\n ),\n },\n });\n}\n","/*\n * The website's `TwoColumnLayout`: from 1024px a 280px sidebar (360 from\n * 1312px) 16 from the panel, both from the top; below 1024px one column, 32\n * apart.\n */\n.SideNav {\n display: grid;\n align-items: start;\n gap: var(--lea-spacing-4);\n grid-template-columns: minmax(0, 1fr);\n}\n\n@media (min-width: 1024px) {\n .SideNav {\n gap: var(--lea-spacing-2);\n /* 280px, the website's sidebar between 1024 and 1312px. */\n grid-template-columns: calc(var(--lea-spacing) * 35) minmax(0, 1fr);\n }\n}\n\n@media (min-width: 1312px) {\n .SideNav {\n /* 360px. */\n grid-template-columns: calc(var(--lea-spacing) * 45) minmax(0, 1fr);\n }\n}\n\n/* Beige, `5xl` corners, 32 inside; from 1024px it stays in view, 32 from the top. */\n.Sidebar {\n box-sizing: border-box;\n padding: var(--lea-spacing-4);\n border-radius: var(--lea-radius-5xl);\n background-color: var(--lea-palette-bg-muted);\n color: var(--lea-palette-text-base);\n}\n\n@media (min-width: 1024px) {\n .Sidebar {\n position: sticky;\n top: var(--lea-spacing-4);\n }\n}\n\n.List {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n padding: 0;\n margin: 0;\n gap: var(--lea-spacing-2);\n list-style: none;\n}\n\n/* A row is as tall as its link, with or without the arrow. */\n.List > li {\n display: flex;\n max-width: 100%;\n}\n\n/* The panel can hold a table wider than its column: it scrolls, not the page. */\n.SideNav .Content {\n min-width: 0;\n}\n","import { createTranslation } from \"../../index\";\n\n/**\n * The words of `SideNav`: the name of its navigation when the page gives\n * none, told apart from the header's \"Hovedmeny\".\n */\nexport const useSideNavLabels = createTranslation({\n navigation: {\n no: \"Sidemeny\",\n se: \"Sidomeny\",\n fi: \"Sivuvalikko\",\n es: \"Menú lateral\",\n },\n});\n","\"use client\";\n\nimport {\n type ComponentProps,\n type MouseEvent,\n type ReactNode,\n useEffect,\n useState,\n} from \"react\";\n\nimport { Panel } from \"../../index\";\nimport { ArrowRevealLink } from \"../arrow-reveal-link\";\nimport type { SiteHeaderLink } from \"../site-header/site-header\";\nimport styles from \"./side-nav.module.css\";\nimport { useSideNavLabels } from \"./use-side-nav-labels\";\n\n/**\n * A link in the side navigation: `{ label, href }`, or `{ label, render }`\n * with a router link carrying its own `href`. A link without either does not\n * compile.\n */\nexport type SideNavItem = SiteHeaderLink & {\n /**\n * This page: the link is marked `aria-current=\"page\"` and has the arrow\n * before it.\n */\n current?: boolean;\n};\n\nexport type SideNavProps = Omit<ComponentProps<\"div\">, \"children\"> & {\n /** The pages beside this one (the website's child pages), in order. */\n items: SideNavItem[];\n /**\n * The name of the navigation, read out with it: the part of the site it\n * covers (\"Investor Relations\"). Left out, \"Sidemeny\".\n */\n label?: string;\n /** The `Breadcrumbs` at the top of the content panel. */\n breadcrumbs?: ReactNode;\n /** The page's content, in the white panel. */\n children: ReactNode;\n};\n\n/**\n * The website's two-column page (`TwoColumnLayout`, the Investor Relations\n * pages of leabank.no): a beige sidebar of links to the pages of one part of\n * the site, beside a white panel with the breadcrumbs and the content. Put it\n * in a `Section` (grey, `spacingTop` and `spacingBottom` `sm`, as on the\n * website).\n *\n * ```tsx\n * <Section surface=\"subtle\" spacingTop=\"sm\" spacingBottom=\"sm\">\n * <SideNav\n * label=\"Investor Relations\"\n * items={[\n * { label: \"About Lea Bank\", render: <NextLink href=\"/om-oss/investor-relations/about-lea-bank\" />, current: true },\n * { label: \"Dividend policy\", render: <NextLink href=\"/om-oss/investor-relations/dividend-policy\" /> },\n * ]}\n * breadcrumbs={<Breadcrumbs items={crumbs} />}\n * >\n * <RichText>…</RichText>\n * </SideNav>\n * </Section>\n * ```\n *\n * From 1024px the sidebar is 280px wide (360 from 1312px), 16 from the\n * panel, and stays in view while the page scrolls (32 from the top); below\n * 1024px it is a block over the panel, 32 apart. The sidebar is a `nav`\n * named by `label`; the link to this page is marked `aria-current=\"page\"`\n * and has a purple arrow before it, the others underline on hover. A click\n * on another link moves the arrow there at once (it grows in over 0.2s),\n * before the next page arrives; `aria-current` stays on this page until\n * `items` says otherwise. The panel is a `Panel size=\"sm\"`: what is in it\n * stacks 16 apart.\n */\nexport function SideNav({\n items,\n label,\n breadcrumbs,\n children,\n className,\n ...rest\n}: SideNavProps) {\n const labels = useSideNavLabels();\n const currentIndex = items.findIndex((item) => item.current);\n\n // The link clicked on this page, which has the arrow until `current` moves.\n const [clicked, setClicked] = useState<{\n index: number;\n from: number;\n } | null>(null);\n if (clicked && clicked.from !== currentIndex) setClicked(null);\n const markedIndex = clicked?.index ?? currentIndex;\n\n // A page the browser brings back from its cache (the back button after a\n // full page load) still has the clicked link's arrow: return it.\n useEffect(() => {\n const reset = (event: PageTransitionEvent) => {\n if (event.persisted) setClicked(null);\n };\n window.addEventListener(\"pageshow\", reset);\n return () => window.removeEventListener(\"pageshow\", reset);\n }, []);\n\n return (\n <div\n {...rest}\n className={[styles.SideNav, className].filter(Boolean).join(\" \")}\n >\n <nav className={styles.Sidebar} aria-label={label ?? labels.navigation}>\n <ul className={styles.List}>\n {items.map((item, index) => (\n <li key={index}>\n <ArrowRevealLink\n size=\"lg\"\n href={item.href}\n render={item.render}\n aria-current={item.current ? \"page\" : undefined}\n marked={index === markedIndex}\n onClick={(event) => {\n if (opensHere(event))\n setClicked({ index, from: currentIndex });\n }}\n >\n {item.label}\n </ArrowRevealLink>\n </li>\n ))}\n </ul>\n </nav>\n <Panel size=\"sm\" className={styles.Content}>\n {breadcrumbs}\n {children}\n </Panel>\n </div>\n );\n}\n\n/**\n * Whether a click follows the link in this tab: not one that opens a new tab\n * or window (a modifier key, `target`, `download`), or one the page stopped.\n */\nfunction opensHere(event: MouseEvent<HTMLAnchorElement>) {\n const link = event.currentTarget;\n const target = link.getAttribute(\"target\");\n return (\n !event.defaultPrevented &&\n event.button === 0 &&\n !event.metaKey &&\n !event.ctrlKey &&\n !event.shiftKey &&\n !event.altKey &&\n (!target || target === \"_self\") &&\n !link.hasAttribute(\"download\")\n );\n}\n","import type { ComponentProps, ReactNode } from \"react\";\n\n/*\n * Internal to `SiteFooter`: the flags of the four markets, round, 20px, from\n * the website's `assets/icons/countries/*.svg` (also in Figma's\n * `.SiteFooter/Flag`). Their colours are the flags', not tokens. The\n * website's files clip to a 20px square by an id; every path already sits\n * inside the circle, so the clip is left out and two flags on a page cannot\n * clash. Decoration: the market's name is the text beside them.\n */\n\ntype FlagProps = Omit<ComponentProps<\"svg\">, \"children\">;\n\nconst circle =\n \"M10 20C15.5228 20 20 15.5228 20 10C20 4.47715 15.5228 0 10 0C4.47715 0 0 4.47715 0 10C0 15.5228 4.47715 20 10 20Z\";\n\n/** The Nordic cross, as Finland and Norway draw it. */\nconst cross =\n \"M19.9154 8.69625H7.82613H7.82609V0.238281C6.90195 0.443203 6.02582 0.775352 5.21738 1.21648V8.69617V8.69621H0.0846484C0.0289844 9.1232 0 9.55852 0 10.0006C0 10.4427 0.0289844 10.878 0.0846484 11.3049H5.21734H5.21738V18.7846C6.02582 19.2257 6.90195 19.558 7.82609 19.7629V11.305V11.305H19.9154C19.9709 10.878 20 10.4427 20 10.0006C20 9.55852 19.9709 9.1232 19.9154 8.69625V8.69625Z\";\n\nfunction Flag({ children, ...rest }: FlagProps & { children: ReactNode }) {\n return (\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n aria-hidden\n focusable=\"false\"\n {...rest}\n >\n {children}\n </svg>\n );\n}\n\nexport function FlagNorway(props: FlagProps) {\n return (\n <Flag {...props}>\n <path d={circle} fill=\"#F0F0F0\" />\n <path\n d=\"M0.34375 12.6094C0.922344 14.7562 2.19754 16.6169 3.9123 17.9345V12.6094H0.34375Z\"\n fill=\"#D80027\"\n />\n <path\n d=\"M9.12891 19.9626C9.41555 19.9873 9.70543 20.0007 9.99848 20.0007C14.6188 20.0007 18.5065 16.867 19.654 12.6094H9.12891V19.9626Z\"\n fill=\"#D80027\"\n />\n <path\n d=\"M19.654 7.39129C18.5065 3.13363 14.6188 0 9.99848 0C9.70543 0 9.41555 0.0133594 9.12891 0.0380469V7.39129H19.654Z\"\n fill=\"#D80027\"\n />\n <path\n d=\"M3.9123 2.06641C2.19754 3.38398 0.922344 5.24465 0.34375 7.39148H3.9123V2.06641Z\"\n fill=\"#D80027\"\n />\n <path d={cross} fill=\"#0052B4\" />\n </Flag>\n );\n}\n\nexport function FlagSweden(props: FlagProps) {\n return (\n <Flag {...props}>\n <path d={circle} fill=\"#FFDA44\" />\n <path\n d=\"M7.82816 8.69566H19.9174C19.2782 3.78914 15.0828 0 10.002 0C9.25539 0 8.52809 0.0825391 7.82812 0.237695V8.69566H7.82816Z\"\n fill=\"#0052B4\"\n />\n <path\n d=\"M5.21867 8.69848V1.21875C2.47281 2.71695 0.507148 5.46504 0.0859375 8.69852H5.21867V8.69848Z\"\n fill=\"#0052B4\"\n />\n <path\n d=\"M5.21863 11.3047H0.0859375C0.507148 14.5382 2.47281 17.2863 5.21867 18.7844L5.21863 11.3047Z\"\n fill=\"#0052B4\"\n />\n <path\n d=\"M7.82812 11.3047V19.7627C8.52809 19.9178 9.25539 20.0004 10.002 20.0004C15.0828 20.0004 19.2782 16.2112 19.9174 11.3047H7.82812V11.3047Z\"\n fill=\"#0052B4\"\n />\n </Flag>\n );\n}\n\nexport function FlagFinland(props: FlagProps) {\n return (\n <Flag {...props}>\n <path d={circle} fill=\"#F0F0F0\" />\n <path d={cross} fill=\"#0052B4\" />\n </Flag>\n );\n}\n\nexport function FlagSpain(props: FlagProps) {\n return (\n <Flag {...props}>\n <path\n d=\"M0 10.0002C0 11.2234 0.220039 12.3951 0.621992 13.4784L10 14.348L19.378 13.4784C19.78 12.3951 20 11.2234 20 10.0002C20 8.77695 19.78 7.60523 19.378 6.52191L10 5.65234L0.621992 6.52191C0.220039 7.60523 0 8.77695 0 10.0002H0Z\"\n fill=\"#FFDA44\"\n />\n <path\n d=\"M19.3771 6.52176C17.9642 2.71379 14.2987 0 9.9991 0C5.69945 0 2.03402 2.71379 0.621094 6.52176H19.3771Z\"\n fill=\"#D80027\"\n />\n <path\n d=\"M0.621094 13.4766C2.03402 17.2845 5.69945 19.9983 9.9991 19.9983C14.2987 19.9983 17.9642 17.2845 19.3771 13.4766H0.621094Z\"\n fill=\"#D80027\"\n />\n </Flag>\n );\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport type { ComponentProps, ReactElement } from \"react\";\n\nimport type {\n SiteHeaderGroup,\n SiteHeaderItem,\n} from \"./site-header/site-header\";\n\n/**\n * Internal to `SiteHeader`: a link to `href`, or the router link in\n * `render`, which carries its own.\n */\nexport function SiteLink({\n href,\n render,\n ...rest\n}: Omit<ComponentProps<\"a\">, \"href\"> & {\n href?: string;\n render?: ReactElement;\n}) {\n return useRender({\n render,\n defaultTagName: \"a\",\n props: href == null ? rest : { ...rest, href },\n });\n}\n\n/** Whether an item opens a menu: it has a group to show. */\nexport const hasMenu = (\n item: SiteHeaderItem,\n): item is Extract<SiteHeaderItem, { groups: SiteHeaderGroup[] }> =>\n item.groups != null && item.groups.length > 0;\n\n/** Whether an item has a page of its own to link to. */\nexport const hasPage = (item: SiteHeaderItem) =>\n item.href != null || item.render != null;\n","/*\n * The website's `Footer`: a near-black band, 80px above and below and 64 at\n * the sides (64 and 16 below 768px), its content at most 1312px wide in a\n * column 32 apart: the logo and the columns, a white line 32 off each side,\n * the cookie settings and the copyright. Everything in `text.contrast`.\n */\n.SiteFooter {\n box-sizing: border-box;\n padding-block: var(--lea-spacing-8);\n padding-inline: var(--lea-spacing-2);\n background-color: var(--lea-palette-bg-contrast);\n color: var(--lea-palette-text-contrast);\n}\n\n@media (min-width: 768px) {\n .SiteFooter {\n /* 80px, the website's `xxl`. */\n padding-block: calc(var(--lea-spacing) * 10);\n padding-inline: var(--lea-spacing-8);\n }\n}\n\n.Content {\n display: flex;\n max-width: var(--lea-width-section);\n flex-direction: column;\n margin-inline: auto;\n gap: var(--lea-spacing-4);\n}\n\n/* The logo and the columns: side by side from 1024px, 64 apart. */\n.Main {\n display: grid;\n align-items: start;\n gap: var(--lea-spacing-4);\n grid-template-columns: minmax(0, 1fr);\n}\n\n@media (min-width: 1024px) {\n .Main {\n gap: var(--lea-spacing-8);\n grid-template-columns: auto minmax(0, 1fr);\n }\n}\n\n.Home {\n display: inline-flex;\n justify-self: start;\n border-radius: var(--lea-radius-sm);\n color: inherit;\n}\n\n/* 140px, the website's logo. */\n.Home > :is(img, svg) {\n display: block;\n width: calc(var(--lea-spacing) * 17.5);\n height: auto;\n}\n\n/*\n * As many columns of at least 140px as fit, sharing the width (five of\n * 170.4 beside the logo at 1440px); two below 768px, one below 640px.\n */\n.Columns {\n display: grid;\n gap: var(--lea-spacing-4);\n grid-template-columns: minmax(0, 1fr);\n}\n\n@media (min-width: 640px) {\n .Columns {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n }\n}\n\n@media (min-width: 768px) {\n .Columns {\n gap: var(--lea-spacing-8);\n grid-template-columns: repeat(\n auto-fit,\n minmax(calc(var(--lea-spacing) * 17.5), 1fr)\n );\n }\n}\n\n.Column {\n display: flex;\n min-width: 0;\n flex-direction: column;\n gap: var(--lea-spacing-1);\n}\n\n/* 24px to what follows: 16 of its own and the column's 8. */\n.Title {\n margin: 0;\n margin-block-end: var(--lea-spacing-2);\n color: inherit;\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-text-heading-xs-line-height);\n}\n\n/*\n * 12px apart: with `body-sm`'s 21px lines that is the website's 33px from\n * link to link (its 14px text sits in 17px lines, 16 apart).\n */\n.Links,\n.Social {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n padding: 0;\n margin: 0;\n gap: var(--lea-spacing-1-5);\n list-style: none;\n}\n\n/* A row is as tall as its link, not the 16px line of the page's text. */\n.Links > li,\n.Social > li {\n display: flex;\n max-width: 100%;\n}\n\n/* The rows are as tall as their 24px icons, 16 apart. */\n.Social {\n gap: var(--lea-spacing-2);\n}\n\n.Link {\n border-radius: var(--lea-radius-sm);\n color: inherit;\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 text-decoration: none;\n}\n\n.Social .Link {\n display: inline-flex;\n align-items: center;\n gap: var(--lea-spacing-1);\n}\n\n.SocialIcon {\n display: flex;\n width: var(--lea-spacing-3);\n height: var(--lea-spacing-3);\n flex-shrink: 0;\n}\n\n.SocialIcon > :is(img, svg) {\n width: 100%;\n height: 100%;\n object-fit: contain;\n}\n\n/* 32px to the social links' title, as the website's. */\n.Market:not(:last-child) {\n margin-block-end: var(--lea-spacing-4);\n}\n\n.Divider {\n display: block;\n height: 1px;\n margin-block: var(--lea-spacing-4);\n background-color: var(--lea-palette-border-contrast);\n}\n\n/*\n * Centred, as the website's. At least 24px tall, the smallest target\n * (WCAG 2.5.8), where the text alone is 21.\n */\n.CookieSettings {\n min-height: var(--lea-spacing-3);\n align-self: center;\n padding: 0;\n border: 0;\n border-radius: var(--lea-radius-sm);\n background: none;\n color: inherit;\n cursor: pointer;\n font: inherit;\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.Copyright {\n margin: 0;\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.Copyright > p {\n margin: 0;\n}\n\n@media (hover: hover) {\n .Link:hover,\n .CookieSettings:hover {\n text-decoration: underline;\n }\n}\n\n/* White on the near-black band (`text.base` would not show). */\n.Home:focus-visible,\n.Link:focus-visible,\n.CookieSettings:focus-visible {\n outline: 2px solid var(--lea-palette-text-contrast);\n outline-offset: 0.125rem;\n}\n","import { createTranslation } from \"../../index\";\n\n/**\n * The words of `SiteFooter`: the titles of the market and social columns\n * when the page gives none, and the button that opens the cookie settings.\n * `collation` is the language the markets are sorted in, as a BCP 47 tag\n * for `Intl.Collator`. The link round the logo takes `SiteHeader`'s name.\n */\nexport const useSiteFooterLabels = createTranslation({\n market: {\n no: \"Marked\",\n se: \"Marknad\",\n fi: \"Markkina\",\n es: \"Mercado\",\n },\n social: {\n no: \"Sosiale medier\",\n se: \"Sociala medier\",\n fi: \"Sosiaalinen media\",\n es: \"Redes sociales\",\n },\n cookieSettings: {\n no: \"Cookie-innstillinger\",\n se: \"Cookieinställningar\",\n fi: \"Evästeasetukset\",\n es: \"Configuración de cookies\",\n },\n collation: { no: \"nb\", se: \"sv\", fi: \"fi\", es: \"es\" },\n});\n","import { createTranslation } from \"../index\";\n\n/**\n * The words of `SiteHeader`: the name of the navigation, the menu button on\n * a phone and the button that closes the menu, and the name of the link\n * around the logo. The link's name starts with the words in the logo, so\n * what a voice user reads out matches what they see.\n */\nexport const useSiteHeaderLabels = createTranslation({\n navigation: {\n no: \"Hovedmeny\",\n se: \"Huvudmeny\",\n fi: \"Päävalikko\",\n es: \"Menú principal\",\n },\n menu: {\n no: \"Meny\",\n se: \"Meny\",\n fi: \"Valikko\",\n es: \"Menú\",\n },\n closeMenu: {\n no: \"Lukk meny\",\n se: \"Stäng menyn\",\n fi: \"Sulje valikko\",\n es: \"Cerrar menú\",\n },\n home: {\n no: \"Lea Bank, forside\",\n se: \"Lea Bank, startsida\",\n fi: \"Lea Bank, etusivu\",\n es: \"Lea Bank, página de inicio\",\n },\n});\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport {\n useId,\n useMemo,\n type ComponentProps,\n type ReactElement,\n type ReactNode,\n} from \"react\";\n\nimport { Select } from \"../../index\";\nimport { FlagFinland, FlagNorway, FlagSpain, FlagSweden } from \"./flags\";\nimport { Paragraphs } from \"../paragraphs\";\nimport type { SiteHeaderLink } from \"../site-header/site-header\";\nimport { SiteLink } from \"../site-header-link\";\nimport styles from \"./site-footer.module.css\";\nimport { useSiteFooterLabels } from \"./use-site-footer-labels\";\nimport { useSiteHeaderLabels } from \"../use-site-header-labels\";\n\n/**\n * A link in the footer: `{ label, href }`, or `{ label, render }` with a\n * router link carrying its own `href`. A link without either does not\n * compile. A link that opens in a new tab is a `render` with `target`.\n */\nexport type SiteFooterLink = SiteHeaderLink;\n\n/** A column of links under a title (\"Tjenester\", \"Support\", \"Juridisk\"). */\nexport type SiteFooterColumn = {\n /** The title over the links, a heading (`titleRender`). */\n title: ReactNode;\n links: SiteFooterLink[];\n};\n\n/** A link to one of the bank's pages on a social network. */\nexport type SiteFooterSocialLink = SiteFooterLink & {\n /**\n * The network's logo, 24px: an `<img>` or `next/image` with `alt=\"\"`, as\n * the label names the link.\n */\n icon: ReactNode;\n};\n\n/** The four markets, by the codes of the languages they speak. */\nexport type SiteFooterMarketCode = \"no\" | \"se\" | \"fi\" | \"es\";\n\n/** The list of markets: the select that takes the visitor to another one. */\nexport type SiteFooterMarket = {\n /** The market of this site, shown with its flag. */\n value: SiteFooterMarketCode;\n /**\n * Called with the market the visitor picks. The website goes to that\n * market's site (`https://leabank.se/`).\n */\n onValueChange: (value: SiteFooterMarketCode) => void;\n /**\n * The markets to choose from, sorted by name in the page's language.\n * @default [\"no\", \"se\", \"fi\", \"es\"]\n */\n options?: readonly SiteFooterMarketCode[];\n /** The title over the select, which names it. Left out, \"Marked\". */\n title?: ReactNode;\n};\n\n/** The links to the bank on social networks, under a title. */\nexport type SiteFooterSocial = {\n /** The title over the links. Left out, \"Sosiale medier\". */\n title?: ReactNode;\n links: SiteFooterSocialLink[];\n};\n\nexport type SiteFooterProps = Omit<ComponentProps<\"footer\">, \"children\"> & {\n /**\n * The white logo, an `<img>` or `next/image`, 140px wide. It is the\n * content of the link to the start page, named \"Lea Bank, forside\" as in\n * `SiteHeader`, so the image can take `alt=\"\"`.\n */\n logo: ReactNode;\n /**\n * The link to the start page, a router link carrying its own `href`:\n * `<NextLink href=\"/\" />`.\n * @default <a href=\"/\" />\n */\n homeRender?: ReactElement;\n /** The columns of links, in order. */\n columns?: SiteFooterColumn[];\n /** The market select, at the top of the last column. */\n market?: SiteFooterMarket;\n /** The social links, at the foot of the last column. */\n social?: SiteFooterSocial;\n /**\n * Called by the button that opens the cookie settings, which shows only\n * with it: `openCookieSettings` from\n * `@leabankab/component-library/cookie-consent`, or the website's own.\n */\n onOpenCookieSettings?: () => void;\n /**\n * The line at the very end. A string becomes a `<p>`; anything else (the\n * CMS's paragraphs) goes in a `<div>`.\n */\n copyright?: ReactNode;\n /**\n * The element of every title in the footer.\n * @default <h2 />\n */\n titleRender?: ReactElement;\n};\n\nconst MARKETS: Record<SiteFooterMarketCode, { name: string; flag: ReactNode }> =\n {\n no: { name: \"Norge\", flag: <FlagNorway /> },\n se: { name: \"Sverige\", flag: <FlagSweden /> },\n fi: { name: \"Suomi\", flag: <FlagFinland /> },\n es: { name: \"España\", flag: <FlagSpain /> },\n };\n\nconst ALL_MARKETS: readonly SiteFooterMarketCode[] = [\"no\", \"se\", \"fi\", \"es\"];\n\nfunction Title({\n render,\n id,\n children,\n}: {\n render?: ReactElement;\n id?: string;\n children: ReactNode;\n}) {\n return useRender({\n render,\n defaultTagName: \"h2\",\n props: { id, className: styles.Title, children },\n });\n}\n\nfunction MarketSelect({\n market,\n render,\n}: {\n market: SiteFooterMarket;\n render?: ReactElement;\n}) {\n const labels = useSiteFooterLabels();\n const titleId = useId();\n const markets = market.options ?? ALL_MARKETS;\n\n const options = useMemo(() => {\n const collator = new Intl.Collator(labels.collation, {\n sensitivity: \"base\",\n });\n return [...markets]\n .sort((a, b) => collator.compare(MARKETS[a].name, MARKETS[b].name))\n .map((code) => ({\n value: code,\n label: MARKETS[code].name,\n icon: MARKETS[code].flag,\n }));\n }, [markets, labels.collation]);\n\n return (\n <>\n <Title render={render} id={titleId}>\n {market.title ?? labels.market}\n </Title>\n <Select\n size=\"sm\"\n aria-labelledby={titleId}\n options={options}\n value={market.value}\n onValueChange={market.onValueChange}\n className={styles.Market}\n />\n </>\n );\n}\n\n/**\n * The website's footer, as at the end of every page of leabank.no: the\n * white logo, the columns of links, the market select and the social links\n * on a near-black band, then a line, the cookie settings and the copyright.\n *\n * ```tsx\n * <SiteFooter\n * logo={<img src=\"/lea-bank-white.svg\" alt=\"\" />}\n * homeRender={<NextLink href=\"/\" />}\n * columns={[\n * {\n * title: \"Tjenester\",\n * links: [{ label: \"Lån\", render: <NextLink href=\"/lan\" /> }],\n * },\n * ]}\n * market={{ value: \"no\", onValueChange: (market) => location.assign(sites[market]) }}\n * social={{\n * links: [\n * {\n * label: \"Facebook\",\n * icon: <img src=\"/facebook.svg\" alt=\"\" />,\n * render: <a href=\"https://www.facebook.com/leabank\" target=\"_blank\" rel=\"noopener noreferrer\" />,\n * },\n * ],\n * }}\n * onOpenCookieSettings={openCookieSettings}\n * copyright=\"Copyright © 2005–2026 Lea Bank AB NUF (publ).\"\n * />\n * ```\n *\n * The columns share the width, as many 140px columns as fit beside the logo\n * from 1024px (five at 1440px), under it below; two columns below 768px and\n * one below 640px. The market select is Paloma's `Select` at `size=\"sm\"`,\n * each market with its flag, named by the title over it; picking a market\n * calls `onValueChange` and goes nowhere by itself. `market.onValueChange`\n * and `onOpenCookieSettings` are functions, so a server component renders\n * the footer through a client component of its own.\n */\nexport function SiteFooter({\n logo,\n homeRender,\n columns = [],\n market,\n social,\n onOpenCookieSettings,\n copyright,\n titleRender,\n className,\n ...rest\n}: SiteFooterProps) {\n const labels = useSiteFooterLabels();\n const headerLabels = useSiteHeaderLabels();\n const hasEnd = onOpenCookieSettings != null || copyright != null;\n\n return (\n <footer\n {...rest}\n className={[styles.SiteFooter, className].filter(Boolean).join(\" \")}\n >\n <div className={styles.Content}>\n <div className={styles.Main}>\n <SiteLink\n href={homeRender == null ? \"/\" : undefined}\n render={homeRender}\n className={styles.Home}\n aria-label={headerLabels.home}\n >\n {logo}\n </SiteLink>\n <div className={styles.Columns}>\n {columns.map((column, index) => (\n <div key={index} className={styles.Column}>\n <Title render={titleRender}>{column.title}</Title>\n {column.links.length > 0 && (\n <ul className={styles.Links}>\n {column.links.map((link, linkIndex) => (\n <li key={linkIndex}>\n <SiteLink\n href={link.href}\n render={link.render}\n className={styles.Link}\n >\n {link.label}\n </SiteLink>\n </li>\n ))}\n </ul>\n )}\n </div>\n ))}\n {(market != null || social != null) && (\n <div className={styles.Column}>\n {market != null && (\n <MarketSelect market={market} render={titleRender} />\n )}\n {social != null && (\n <>\n <Title render={titleRender}>\n {social.title ?? labels.social}\n </Title>\n <ul className={styles.Social}>\n {social.links.map((link, index) => (\n <li key={index}>\n <SiteLink\n href={link.href}\n render={link.render}\n className={styles.Link}\n >\n <span aria-hidden className={styles.SocialIcon}>\n {link.icon}\n </span>\n {link.label}\n </SiteLink>\n </li>\n ))}\n </ul>\n </>\n )}\n </div>\n )}\n </div>\n </div>\n {hasEnd && (\n <>\n <span aria-hidden className={styles.Divider} />\n {onOpenCookieSettings != null && (\n <button\n type=\"button\"\n className={styles.CookieSettings}\n onClick={onOpenCookieSettings}\n >\n {labels.cookieSettings}\n </button>\n )}\n {copyright != null && (\n <Paragraphs className={styles.Copyright}>{copyright}</Paragraphs>\n )}\n </>\n )}\n </div>\n </footer>\n );\n}\n","/*\n * The website's header (Figma `SiteHeader`, leabank.no's Header, MobileMenu\n * and DropdownShared): a white bar over the brand stripe. From 1312px, the\n * website's header width, the bar is 72px tall with the row of items; below\n * it, 55px with the menu button, the logo and the account icon. Both\n * heights are off the spacing grid (9 and 6.875 steps): they are the\n * website's.\n */\n.SiteHeader {\n --site-header-bar: calc(var(--lea-spacing) * 6.875);\n\n display: flex;\n width: 100%;\n flex-direction: column;\n background-color: var(--lea-palette-bg-base);\n color: var(--lea-palette-text-base);\n}\n\n.Bar {\n padding-inline: var(--lea-spacing-2);\n background-color: var(--lea-palette-bg-base);\n}\n\n.BarContent {\n display: flex;\n width: 100%;\n max-width: var(--lea-width-section);\n min-height: var(--site-header-bar, calc(var(--lea-spacing) * 6.875));\n align-items: center;\n justify-content: space-between;\n margin-inline: auto;\n}\n\n/*\n * The logo, as the content of the link to the start page. On the narrow bar\n * it takes the free room on both sides, which puts it where the website's\n * `space-between` does, and keeps it off the end when there is no account\n * link.\n */\n.Home {\n display: flex;\n height: var(--lea-spacing-4);\n flex-shrink: 0;\n align-items: center;\n margin-inline: auto;\n border-radius: var(--lea-radius-sm);\n color: inherit;\n}\n\n.Home img,\n.Home svg {\n display: block;\n width: auto;\n height: 100%;\n}\n\n.Actions {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n gap: var(--lea-spacing-2);\n}\n\n.Account {\n position: relative;\n display: flex;\n width: var(--lea-spacing-4);\n height: var(--lea-spacing-4);\n flex-shrink: 0;\n border-radius: var(--lea-radius-full);\n color: inherit;\n}\n\n.Account > * {\n width: 100%;\n height: 100%;\n}\n\n/* The menu button: a 24px icon at the start of the narrow bar. */\n.MenuButton {\n position: relative;\n display: flex;\n width: var(--lea-spacing-3);\n height: var(--lea-spacing-3);\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 background: transparent;\n color: inherit;\n cursor: pointer;\n}\n\n.MenuButton svg {\n width: var(--lea-spacing-3);\n height: var(--lea-spacing-3);\n}\n\n/*\n * The icon buttons of the narrow bar keep the website's size, and answer to\n * 44px around it, the size of a primary control: 10px round the 24px menu\n * icon, 6px round the 32px account icon.\n */\n.MenuButton::before,\n.Account::before {\n position: absolute;\n content: \"\";\n inset: calc(var(--lea-spacing) * -1.25);\n}\n\n.Account::before {\n inset: calc(var(--lea-spacing) * -0.75);\n}\n\n.Action {\n display: none;\n}\n\n.Nav {\n display: none;\n}\n\n.Home:focus-visible,\n.Account:focus-visible,\n.MenuButton:focus-visible,\n.Pill:focus-visible,\n.Cta:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n/* ---- The row of items, from 1312px ---- */\n\n.List {\n display: flex;\n min-width: 0;\n flex: 1 1 auto;\n align-items: center;\n gap: var(--lea-spacing-1);\n padding: 0;\n margin: 0;\n list-style: none;\n}\n\n/* The end items (Kundeservice, Om oss) sit at the end of the row. */\n.Item[data-end] {\n margin-inline-start: auto;\n}\n\n/*\n * A 40px pill: 16px text 16px from its sides, and the chevron of a menu\n * 4px after it. Grey on hover and while its menu is open, as the website.\n */\n.Pill {\n display: inline-flex;\n min-height: var(--lea-spacing-5);\n box-sizing: border-box;\n align-items: center;\n gap: var(--lea-spacing-0-5);\n padding: var(--lea-spacing-0-5) var(--lea-spacing-1-5);\n border: 0;\n border-radius: var(--lea-radius-5xl);\n background-color: transparent;\n color: var(--lea-palette-text-base);\n cursor: pointer;\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 text-decoration: none;\n transition: background-color var(--lea-duration-fast)\n var(--lea-easing-standard);\n white-space: nowrap;\n}\n\n@media (hover: hover) {\n .Pill:hover {\n background-color: var(--lea-palette-bg-subtle);\n }\n}\n\n.Pill[data-popup-open] {\n background-color: var(--lea-palette-bg-subtle);\n}\n\n.Chevron {\n display: flex;\n transition: rotate var(--lea-duration-fast) var(--lea-easing-standard);\n}\n\n.Chevron svg {\n width: var(--lea-spacing-3);\n height: var(--lea-spacing-3);\n}\n\n.Chevron[data-popup-open] {\n rotate: 180deg;\n}\n\n@media (min-width: 1312px) {\n .Bar {\n padding-inline: var(--lea-spacing-8);\n }\n\n .BarContent {\n --site-header-bar: calc(var(--lea-spacing) * 9);\n\n justify-content: flex-start;\n }\n\n /* 38px, the website's logo. */\n .Home {\n height: calc(var(--lea-spacing) * 4.75);\n margin-inline: 0;\n }\n\n .Account {\n width: var(--lea-spacing-5);\n height: var(--lea-spacing-5);\n }\n\n .Account::before {\n content: none;\n }\n\n .MenuButton {\n display: none;\n }\n\n .Action {\n display: flex;\n }\n\n /*\n * The items take the room between the logo and the actions. Below the\n * website's widest step the pills and the gaps round them are 4px\n * tighter on each side, so the longest row (leabank.es, with its 249px\n * call to action) fits at 1312px with room to spare.\n */\n .Nav {\n display: flex;\n min-width: 0;\n flex: 1 1 auto;\n padding-inline: var(--lea-spacing-2);\n }\n}\n\n@media (min-width: 1440px) {\n .Nav {\n padding-inline: var(--lea-spacing-4);\n }\n\n .Pill {\n padding-inline: var(--lea-spacing-2);\n }\n}\n\n/* ---- The menu of an item, from 1312px ---- */\n\n.Positioner {\n z-index: 1300;\n width: var(--positioner-width);\n height: var(--positioner-height);\n max-width: var(--available-width);\n transition-duration: var(--lea-duration-normal);\n transition-property: top, left, right, bottom;\n transition-timing-function: var(--lea-easing-standard);\n}\n\n/* Keeps the pointer over the menu on its way down from the pill. */\n.Positioner::before {\n position: absolute;\n top: calc(var(--lea-spacing) * -1);\n height: var(--lea-spacing);\n content: \"\";\n inset-inline: 0;\n}\n\n.Positioner[data-instant] {\n transition: none;\n}\n\n/*\n * The website's dropdown: a white panel 400px wide with 32px corners and\n * the soft shadow, 8px under the pill. It drops in from 16px above, and\n * moves and resizes when the pointer goes to the next item.\n */\n.Popup {\n position: relative;\n width: var(--popup-width);\n height: var(--popup-height);\n border-radius: var(--lea-radius-5xl);\n background-color: var(--lea-palette-bg-base);\n box-shadow: var(--lea-shadow-soft);\n color: var(--lea-palette-text-base);\n transform-origin: var(--transform-origin);\n transition-duration: var(--lea-duration-normal);\n transition-property: opacity, translate, width, height;\n transition-timing-function: var(--lea-easing-standard);\n}\n\n.Popup[data-starting-style],\n.Popup[data-ending-style] {\n opacity: 0;\n translate: 0 calc(var(--lea-spacing) * -2);\n}\n\n.Viewport {\n position: relative;\n overflow: hidden;\n width: 100%;\n height: 100%;\n border-radius: inherit;\n}\n\n.Content {\n display: flex;\n width: max-content;\n box-sizing: border-box;\n gap: var(--lea-spacing-4);\n padding: var(--lea-spacing-4);\n transition: opacity var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n/* In the server's markup a closed menu is there, hidden. */\n.Content[hidden] {\n display: none;\n}\n\n.Content[data-starting-style],\n.Content[data-ending-style] {\n opacity: 0;\n}\n\n/* One group makes the website's 400px panel: 336 + 2 × 32. */\n.Group {\n display: flex;\n width: calc(var(--lea-spacing) * 42);\n flex-direction: column;\n align-items: flex-start;\n gap: var(--lea-spacing-3);\n}\n\n/* 14px semibold, the website's category title. No text style has it. */\n.GroupTitle {\n margin: 0;\n font-size: var(--lea-font-step-minus-1);\n font-weight: var(--lea-font-weight-semibold);\n line-height: var(--lea-line-height-tight);\n}\n\n.Links {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--lea-spacing-2);\n padding: 0;\n margin: 0;\n list-style: none;\n}\n\n/* A list item would add a line box of its own round the inline link. */\n.Links > li {\n display: flex;\n}\n\n.Separator {\n display: block;\n width: var(--lea-spacing-3);\n height: 1px;\n background-color: var(--lea-palette-primary-base);\n}\n\n/* The last link: purple and underlined on hover, as the website's. */\n.Cta {\n display: inline-flex;\n align-items: center;\n gap: var(--lea-spacing-0-5);\n border-radius: var(--lea-radius-sm);\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 letter-spacing: var(--lea-text-body-md-letter-spacing);\n line-height: var(--lea-text-body-md-line-height);\n text-decoration: none;\n transition: color var(--lea-duration-fast) var(--lea-easing-standard);\n}\n\n@media (hover: hover) {\n .Cta:hover {\n color: var(--lea-palette-primary-base);\n }\n\n .Cta:hover > span {\n text-decoration: underline;\n }\n}\n\n.CtaArrow {\n width: var(--lea-spacing-3);\n height: var(--lea-spacing-3);\n flex-shrink: 0;\n}\n\n/* ---- The menu below 1312px ---- */\n\n.Backdrop {\n position: fixed;\n z-index: 1300;\n background-color: var(--lea-palette-bg-overlay);\n inset: 0;\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.DrawerViewport {\n position: fixed;\n z-index: 1300;\n inset: 0;\n pointer-events: none;\n}\n\n/*\n * The menu covers the header from the top, with a bar of its own that\n * looks like the header's (the close button where the menu button was), so\n * nothing seems to move but the icon. The list then slides down from under\n * that bar, as on the website: the panel is revealed downwards and the list\n * moves with it. On a phone it fills the screen; from 640px it is as tall as\n * its rows, over a dimmed page.\n */\n.MenuPopup {\n --site-header-bar: calc(var(--lea-spacing) * 6.875);\n\n position: absolute;\n top: 0;\n left: 0;\n display: flex;\n width: 100%;\n min-height: 100dvh;\n max-height: 100dvh;\n flex-direction: column;\n background-color: var(--lea-palette-bg-base);\n clip-path: inset(0);\n color: var(--lea-palette-text-base);\n outline: 0;\n pointer-events: auto;\n transform: translateY(var(--drawer-swipe-movement-y, 0px));\n transition-duration: var(--lea-duration-normal);\n transition-property: clip-path, transform;\n transition-timing-function: var(--lea-easing-standard);\n}\n\n@media (min-width: 640px) {\n .MenuPopup {\n min-height: 0;\n }\n}\n\n/* The bar and the stripe: 55px and 4px. */\n.MenuPopup[data-starting-style],\n.MenuPopup[data-ending-style] {\n clip-path: inset(\n 0 0 calc(100% - var(--site-header-bar) - var(--lea-spacing-0-5)) 0\n );\n}\n\n.MenuPopup[data-swiping] {\n transition-duration: 0ms;\n}\n\n.MenuTop {\n position: relative;\n z-index: 1;\n flex-shrink: 0;\n background-color: var(--lea-palette-bg-base);\n}\n\n.MenuContent {\n min-height: 0;\n flex: 1 1 auto;\n overflow-y: auto;\n overscroll-behavior: contain;\n transition: translate var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n.MenuPopup[data-starting-style] .MenuContent,\n.MenuPopup[data-ending-style] .MenuContent {\n translate: 0 -100%;\n}\n\n.MenuList {\n padding: 0;\n margin: 0;\n list-style: none;\n}\n\n/* A 72px row: a link, or the button of a submenu. */\n.Row {\n display: flex;\n width: 100%;\n min-height: calc(var(--lea-spacing) * 9);\n box-sizing: border-box;\n align-items: center;\n gap: var(--lea-spacing-0-5);\n padding: var(--lea-spacing-1) var(--lea-spacing-2);\n border: 0;\n background-color: transparent;\n color: var(--lea-palette-text-base);\n cursor: pointer;\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 text-align: start;\n text-decoration: none;\n transition: background-color var(--lea-duration-fast)\n var(--lea-easing-standard);\n}\n\n@media (hover: hover) {\n .Row:hover {\n background-color: var(--lea-palette-bg-subtle);\n }\n}\n\n.Row:active {\n background-color: var(--lea-palette-bg-subtle);\n}\n\n/* The rows span the menu edge to edge: the ring sits inside them. */\n.Row:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: -0.125rem;\n}\n\n.RowChevron {\n width: var(--lea-spacing-3);\n height: var(--lea-spacing-3);\n flex-shrink: 0;\n transition: rotate var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n.Row[data-panel-open] .RowChevron {\n rotate: 180deg;\n}\n\n.SubmenuPanel {\n overflow: hidden;\n height: var(--collapsible-panel-height);\n transition: height var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n.SubmenuPanel[data-starting-style],\n.SubmenuPanel[data-ending-style] {\n height: 0;\n}\n\n.Submenu {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--lea-spacing-2);\n padding: var(--lea-spacing-2);\n background-color: var(--lea-palette-bg-subtle);\n}\n\n/* The item's own page: 14px semibold, spaced out 4 %, as the website's. */\n.Submenu .Parent {\n font-size: var(--lea-font-step-minus-1);\n font-weight: var(--lea-font-weight-semibold);\n letter-spacing: 0.04em;\n}\n\n.Submenu .CtaArrow {\n width: var(--lea-spacing-2-5);\n height: var(--lea-spacing-2-5);\n}\n\n@media (min-width: 1312px) {\n /* The menu belongs to the narrow header. */\n .Backdrop,\n .DrawerViewport {\n display: none;\n }\n}\n\n@media (max-width: 1311.95px) {\n .Positioner {\n display: none;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .Pill,\n .Chevron,\n .Positioner,\n .Popup,\n .Content,\n .Cta,\n .Backdrop,\n .MenuPopup,\n .MenuContent,\n .Row,\n .RowChevron,\n .SubmenuPanel {\n transition-duration: 0.01ms;\n }\n}\n","\"use client\";\n\nimport { Collapsible } from \"@base-ui/react/collapsible\";\nimport { Drawer } from \"@base-ui/react/drawer\";\nimport { ArrowUpRight, ChevronDown, Menu, X } from \"lucide-react\";\nimport { useEffect, useState, type MouseEvent, type ReactNode } from \"react\";\n\nimport { BrandDivider } from \"../../index\";\nimport { ArrowRevealLink } from \"../arrow-reveal-link\";\nimport type { SiteHeaderItem } from \"./site-header\";\nimport styles from \"./site-header.module.css\";\nimport { hasMenu, hasPage, SiteLink } from \"../site-header-link\";\nimport { useSiteHeaderLabels } from \"../use-site-header-labels\";\n\n/** The width from which the header shows its row of items instead. */\nconst WIDE = \"(min-width: 1312px)\";\n\ntype SubmenuProps = {\n item: Extract<SiteHeaderItem, { groups: unknown[] }>;\n open: boolean;\n onOpenChange: (open: boolean) => void;\n};\n\n/** One item with a menu: a 72px row that opens its links under it. */\nfunction Submenu({ item, open, onOpenChange }: SubmenuProps) {\n const links = item.groups.flatMap((group) => group.links);\n const ctas = item.groups.flatMap((group) =>\n group.cta == null ? [] : [group.cta],\n );\n\n return (\n <Collapsible.Root open={open} onOpenChange={onOpenChange}>\n <Collapsible.Trigger className={styles.Row}>\n {item.label}\n <ChevronDown aria-hidden className={styles.RowChevron} />\n </Collapsible.Trigger>\n <Collapsible.Panel className={styles.SubmenuPanel}>\n <div className={styles.Submenu}>\n {(hasPage(item) || links.length > 0) && (\n <ul className={styles.Links}>\n {hasPage(item) && (\n <li>\n <ArrowRevealLink\n className={styles.Parent}\n href={item.href}\n render={item.render}\n >\n {item.label}\n </ArrowRevealLink>\n </li>\n )}\n {links.map((link, index) => (\n <li key={index}>\n <ArrowRevealLink href={link.href} render={link.render}>\n {link.label}\n </ArrowRevealLink>\n </li>\n ))}\n </ul>\n )}\n {ctas.length > 0 && links.length > 0 && (\n <span aria-hidden className={styles.Separator} />\n )}\n {ctas.map((cta, index) => (\n <SiteLink\n key={index}\n className={styles.Cta}\n href={cta.href}\n render={cta.render}\n >\n <span>{cta.label}</span>\n <ArrowUpRight aria-hidden className={styles.CtaArrow} />\n </SiteLink>\n ))}\n </div>\n </Collapsible.Panel>\n </Collapsible.Root>\n );\n}\n\n/**\n * The items as a list of 72px rows: a link, or a button that opens the\n * item's links under it. One submenu is open at a time, as on the website.\n */\nfunction MenuList({ items }: { items: SiteHeaderItem[] }) {\n const labels = useSiteHeaderLabels();\n const [openIndex, setOpenIndex] = useState<number | null>(null);\n\n return (\n <nav aria-label={labels.navigation}>\n <ul className={styles.MenuList}>\n {items.map((item, index) => (\n <li key={index}>\n {hasMenu(item) ? (\n <Submenu\n item={item}\n open={openIndex === index}\n onOpenChange={(open) => setOpenIndex(open ? index : null)}\n />\n ) : (\n <SiteLink\n className={styles.Row}\n href={item.href}\n render={item.render}\n >\n {item.label}\n </SiteLink>\n )}\n </li>\n ))}\n </ul>\n </nav>\n );\n}\n\ntype SiteHeaderMenuProps = {\n items: SiteHeaderItem[];\n /** The link around the logo, shown in the menu's own bar too. */\n home: ReactNode;\n /** The account link, shown in the menu's own bar too. */\n account: ReactNode;\n};\n\n/**\n * Internal to `SiteHeader`: the menu below 1312px. The button in the bar\n * opens a dialog that drops in from the top (Base UI's `Drawer`): focus\n * moves into it and stays there, the page behind it does not scroll, and\n * Escape, the close button, a swipe up or a press outside closes it. The\n * dialog starts with a bar of its own, the bar of the header with a close\n * button where the menu button was, so the visitor sees the same bar and\n * always has a visible way out. Its links are not in the page while it is\n * closed, and following one closes it.\n */\nexport function SiteHeaderMenu({ items, home, account }: SiteHeaderMenuProps) {\n const labels = useSiteHeaderLabels();\n const [open, setOpen] = useState(false);\n\n // The menu belongs to the narrow header: close it if the window widens.\n useEffect(() => {\n if (!open) return;\n const query = window.matchMedia(WIDE);\n const close = () => {\n if (query.matches) setOpen(false);\n };\n query.addEventListener(\"change\", close);\n return () => query.removeEventListener(\"change\", close);\n }, [open]);\n\n // A client-side route change keeps the header mounted: close the menu on\n // any link followed inside it.\n const closeOnLink = (event: MouseEvent) => {\n if (event.target instanceof Element && event.target.closest(\"a\")) {\n setOpen(false);\n }\n };\n\n return (\n <Drawer.Root open={open} onOpenChange={setOpen} swipeDirection=\"up\">\n <Drawer.Trigger className={styles.MenuButton} aria-label={labels.menu}>\n <Menu aria-hidden />\n </Drawer.Trigger>\n <Drawer.Portal>\n <Drawer.Backdrop className={styles.Backdrop} />\n <Drawer.Viewport className={styles.DrawerViewport}>\n <Drawer.Popup\n className={styles.MenuPopup}\n aria-label={labels.menu}\n onClick={closeOnLink}\n >\n <div className={styles.MenuTop}>\n <div className={styles.Bar}>\n <div className={styles.BarContent}>\n <Drawer.Close\n className={styles.MenuButton}\n aria-label={labels.closeMenu}\n >\n <X aria-hidden />\n </Drawer.Close>\n {home}\n {account && <div className={styles.Actions}>{account}</div>}\n </div>\n </div>\n <BrandDivider size=\"sm\" />\n </div>\n <Drawer.Content className={styles.MenuContent}>\n <MenuList items={items} />\n </Drawer.Content>\n </Drawer.Popup>\n </Drawer.Viewport>\n </Drawer.Portal>\n </Drawer.Root>\n );\n}\n","\"use client\";\n\nimport { NavigationMenu } from \"@base-ui/react/navigation-menu\";\nimport { ArrowUpRight, ChevronDown, CircleUserRound } from \"lucide-react\";\nimport {\n useId,\n useState,\n type ComponentProps,\n type ReactElement,\n type ReactNode,\n} from \"react\";\n\nimport { BrandDivider } from \"../../index\";\nimport { ArrowRevealLink } from \"../arrow-reveal-link\";\nimport styles from \"./site-header.module.css\";\nimport { hasMenu, SiteLink } from \"../site-header-link\";\nimport { SiteHeaderMenu } from \"./site-header-menu\";\nimport { useSiteHeaderLabels } from \"../use-site-header-labels\";\n\n/**\n * Where a link goes: `href`, or a router link in `render` that carries its\n * own (`<NextLink href=\"/lan\" />`). A link without either does not compile.\n */\nexport type SiteHeaderLinkTarget =\n | {\n /** Where the link goes. */\n href: string;\n /**\n * A router link to render instead of an `<a>`, carrying its own\n * `href`, and any `target` or `data-` attribute the page wants on it.\n */\n render?: ReactElement;\n }\n | { href?: undefined; render: ReactElement };\n\n/** A link in the header or in one of its menus. */\nexport type SiteHeaderLink = SiteHeaderLinkTarget & {\n /** The text of the link. */\n label: ReactNode;\n};\n\n/**\n * A group of links in a menu: the website's \"Sublink\", a column of the\n * dropdown. The menus on leabank.no have one each.\n */\nexport type SiteHeaderGroup = {\n /**\n * The small title over the links on a wide screen (\"Låneprodukter\"). It\n * names the list; the phone menu leaves it out, as the website's.\n */\n title?: ReactNode;\n /** The links, each with the arrow that grows in on hover. */\n links: SiteHeaderLink[];\n /**\n * The last link, under a short purple line, with an arrow pointing up and\n * to the right (\"Se våre lån\").\n */\n cta?: SiteHeaderLink;\n};\n\n/**\n * A top-level item: a link, or a menu of `groups`. With a menu the item is a\n * button that opens it, so its own page is not a click on the item any more:\n * link to it in the menu (the website's menus do, with the `cta` or a link).\n * Given `href` or `render`, a menu item's own page is the first link of its\n * submenu on a phone, in small bold type, as on the website.\n */\nexport type SiteHeaderItem =\n | (SiteHeaderLink & { groups?: undefined })\n | {\n /** The text of the item. */\n label: ReactNode;\n /** The item's own page, the first link of its submenu on a phone. */\n href?: string;\n /** A router link to the item's own page, instead of `href`. */\n render?: ReactElement;\n /** The menu that opens from the item. */\n groups: SiteHeaderGroup[];\n };\n\n/** The account link, a round icon beside the call to action. */\nexport type SiteHeaderAccount = SiteHeaderLinkTarget & {\n /**\n * The link's name, read out in place of the icon (\"Logg inn\", \"Min side\").\n */\n label: string;\n /**\n * The icon, 40px (32px below 1312px). Left out, Lucide's\n * `CircleUserRound`; the website passes its own image with `alt=\"\"`.\n */\n icon?: ReactNode;\n};\n\nexport type SiteHeaderProps = Omit<ComponentProps<\"header\">, \"children\"> & {\n /**\n * The logo, an `<img>` or `next/image`, 38px tall (32px below 1312px). It\n * is the content of the link to the start page, which is named \"Lea Bank,\n * forside\" by the header, so the image can take `alt=\"\"`.\n */\n logo: ReactNode;\n /**\n * The link to the start page, a router link carrying its own `href`:\n * `<NextLink href=\"/\" />`.\n * @default <a href=\"/\" />\n */\n homeRender?: ReactElement;\n /** The items at the start of the row (Lån, Sparing, Forsikring …). */\n items: SiteHeaderItem[];\n /**\n * The items pushed to the end of the row (Kundeservice, Om oss). On a\n * phone they follow `items` in the menu.\n */\n endItems?: SiteHeaderItem[];\n /** The account link, an icon at the end of the bar at every width. */\n account?: SiteHeaderAccount;\n /**\n * The call to action at the end of the bar from 1312px, an\n * `ActionButton size=\"sm\"` (\"Min side\"). Below 1312px it is left out, as\n * on the website.\n */\n action?: ReactNode;\n};\n\nfunction MenuGroup({ group }: { group: SiteHeaderGroup }) {\n const titleId = useId();\n const hasTitle = group.title != null;\n const hasLinks = group.links.length > 0;\n\n return (\n <div className={styles.Group}>\n {hasTitle && (\n <p id={titleId} className={styles.GroupTitle}>\n {group.title}\n </p>\n )}\n {hasLinks && (\n <ul\n className={styles.Links}\n aria-labelledby={hasTitle ? titleId : undefined}\n >\n {group.links.map((link, index) => (\n <li key={index}>\n <ArrowRevealLink\n render={\n <NavigationMenu.Link\n closeOnClick\n href={link.href}\n render={link.render}\n />\n }\n >\n {link.label}\n </ArrowRevealLink>\n </li>\n ))}\n </ul>\n )}\n {group.cta != null && hasLinks && (\n <span aria-hidden className={styles.Separator} />\n )}\n {group.cta != null && (\n <NavigationMenu.Link\n closeOnClick\n className={styles.Cta}\n href={group.cta.href}\n render={group.cta.render}\n >\n <span>{group.cta.label}</span>\n <ArrowUpRight aria-hidden className={styles.CtaArrow} />\n </NavigationMenu.Link>\n )}\n </div>\n );\n}\n\ntype NavItemProps = {\n item: SiteHeaderItem;\n value: string;\n end: boolean;\n /** Called when the focus comes to the item, its menu included. */\n onFocus: (value: string) => void;\n};\n\nfunction NavItem({ item, value, end, onFocus }: NavItemProps) {\n return (\n <NavigationMenu.Item\n value={value}\n className={styles.Item}\n data-end={end || undefined}\n // React's focus events bubble out of the menu's portal, so focus in\n // the open menu counts as focus in its item.\n onFocus={() => onFocus(value)}\n >\n {hasMenu(item) ? (\n <>\n <NavigationMenu.Trigger className={styles.Pill}>\n {item.label}\n <NavigationMenu.Icon className={styles.Chevron}>\n <ChevronDown />\n </NavigationMenu.Icon>\n </NavigationMenu.Trigger>\n {/* Mounted, hidden, in the server's markup too, so a crawler finds\n the links the menus hold. */}\n <NavigationMenu.Content keepMounted className={styles.Content}>\n {item.groups.map((group, index) => (\n <MenuGroup key={index} group={group} />\n ))}\n </NavigationMenu.Content>\n </>\n ) : (\n <NavigationMenu.Link\n closeOnClick\n className={styles.Pill}\n href={item.href}\n render={item.render}\n >\n {item.label}\n </NavigationMenu.Link>\n )}\n </NavigationMenu.Item>\n );\n}\n\n/**\n * The website's header, as on every page of leabank.no: the logo, the main\n * navigation, the account link and a call to action on a white bar, with\n * the brand stripe under it.\n *\n * ```tsx\n * <SiteHeader\n * logo={<img src=\"/lea-bank.svg\" alt=\"\" />}\n * homeRender={<NextLink href=\"/\" />}\n * items={[\n * {\n * label: \"Lån\",\n * render: <NextLink href=\"/lan\" />,\n * groups: [\n * {\n * title: \"Låneprodukter\",\n * links: [{ label: \"Forbrukslån\", render: <NextLink href=\"/lan/forbrukslan\" /> }],\n * cta: { label: \"Se våre lån\", render: <NextLink href=\"/lan\" /> },\n * },\n * ],\n * },\n * { label: \"Økonomiguiden\", render: <NextLink href=\"/okonomiguiden\" /> },\n * ]}\n * endItems={[{ label: \"Kundeservice\", href: \"/kundeservice\" }]}\n * account={{ label: \"Logg inn\", href: \"https://secure.leabank.no/\" }}\n * action={<ActionButton size=\"sm\" render={<a href=\"https://secure.leabank.no/\" />}>Min side</ActionButton>}\n * />\n * ```\n *\n * From 1312px the bar is 72px tall: the items are 40px pills that turn grey\n * on hover, and an item with `groups` opens its menu, a white panel with the\n * website's soft shadow, on hover, on a click and with the arrow-down key\n * (Base UI's `NavigationMenu`: links, not a `role=\"menu\"`; Escape closes it,\n * and the focus moves into it with Tab). Below 1312px the bar is 55px tall\n * with a menu button, the logo in the middle and the account icon; the menu\n * drops in from under the bar as a dialog (Base UI's `Drawer` from the top),\n * the items 72px rows and the menus disclosures, one open at a time. The\n * menu closes when a link in it is followed, and its links are not in the\n * page while it is closed.\n */\nexport function SiteHeader({\n logo,\n homeRender,\n items,\n endItems = [],\n account,\n action,\n className,\n ...rest\n}: SiteHeaderProps) {\n const labels = useSiteHeaderLabels();\n const [openItem, setOpenItem] = useState<string | null>(null);\n\n // Tabbing on from a menu to the next item closes the menu, as on the\n // website; Base UI would keep it open while the focus is in the row.\n const closeOthers = (value: string) => {\n if (openItem != null && openItem !== value) setOpenItem(null);\n };\n\n const home = (\n <SiteLink\n href={homeRender == null ? \"/\" : undefined}\n render={homeRender}\n className={styles.Home}\n aria-label={labels.home}\n >\n {logo}\n </SiteLink>\n );\n\n const accountLink = account != null && (\n <SiteLink\n href={account.href}\n render={account.render}\n className={styles.Account}\n aria-label={account.label}\n >\n {account.icon ?? <CircleUserRound aria-hidden />}\n </SiteLink>\n );\n\n return (\n <header\n {...rest}\n className={[styles.SiteHeader, className].filter(Boolean).join(\" \")}\n >\n <div className={styles.Bar}>\n <div className={styles.BarContent}>\n <SiteHeaderMenu\n items={[...items, ...endItems]}\n home={home}\n account={accountLink}\n />\n {home}\n <NavigationMenu.Root\n className={styles.Nav}\n aria-label={labels.navigation}\n closeDelay={150}\n value={openItem}\n onValueChange={setOpenItem}\n >\n <NavigationMenu.List className={styles.List}>\n {[...items, ...endItems].map((item, index) => (\n <NavItem\n key={index}\n item={item}\n value={`item-${index}`}\n end={endItems.length > 0 && index === items.length}\n onFocus={closeOthers}\n />\n ))}\n </NavigationMenu.List>\n <NavigationMenu.Portal>\n <NavigationMenu.Positioner\n className={styles.Positioner}\n side=\"bottom\"\n align=\"start\"\n sideOffset={8}\n collisionPadding={16}\n >\n <NavigationMenu.Popup className={styles.Popup} render={<div />}>\n <NavigationMenu.Viewport className={styles.Viewport} />\n </NavigationMenu.Popup>\n </NavigationMenu.Positioner>\n </NavigationMenu.Portal>\n </NavigationMenu.Root>\n {(accountLink || action != null) && (\n <div className={styles.Actions}>\n {accountLink}\n {action != null && <div className={styles.Action}>{action}</div>}\n </div>\n )}\n </div>\n </div>\n <BrandDivider size=\"sm\" />\n </header>\n );\n}\n","/*\n * The website's CardWithImageSection: no colour of its own. From 1024px two\n * equal columns 64px apart, centred on each other: the title (`heading-xl`)\n * and the text (`body-lg`) 32px apart, and a photo without corners, at most\n * 560px wide, at the end of its column (next to the text when it comes\n * first). The action and the price example are centred 80px under both.\n * Below 1024px one column, the photo at most 440px and the actions at the\n * start; below 768px everything 32px apart (the text 16px) and the title on\n * `heading-md`.\n */\n.SplitContent {\n display: flex;\n min-width: 0;\n flex-direction: column;\n /* 80px, the website's `xxl`. */\n gap: calc(var(--lea-spacing) * 10);\n}\n\n.Grid {\n display: grid;\n grid-template-columns: minmax(0, 1fr);\n align-items: center;\n gap: var(--lea-spacing-8);\n}\n\n.Text {\n display: flex;\n min-width: 0;\n flex-direction: column;\n gap: var(--lea-spacing-4);\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.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.Description > * {\n margin: 0;\n}\n\n.Description > * + * {\n margin-block-start: var(--lea-spacing-1);\n}\n\n.Media {\n display: flex;\n min-width: 0;\n}\n\n.SplitContent[data-image-position=\"start\"] .Media {\n order: -1;\n}\n\n.Image {\n display: block;\n width: 100%;\n /* The website's 440px below 1024px. */\n max-width: calc(var(--lea-spacing) * 55);\n height: auto;\n}\n\n.Actions {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--lea-spacing-2);\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@media (min-width: 1024px) {\n .SplitContent[data-has-image] .Grid {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n }\n\n /* At the end of its column on either side, as the website: next to the\n text when it comes first. */\n .Media {\n justify-content: flex-end;\n }\n\n .Image {\n /* The website's 560px. */\n max-width: calc(var(--lea-spacing) * 70);\n }\n\n .Actions {\n align-items: center;\n text-align: center;\n }\n}\n\n@media (max-width: 767.95px) {\n .SplitContent,\n .Grid {\n gap: var(--lea-spacing-4);\n }\n\n .Text {\n gap: var(--lea-spacing-2);\n }\n\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 type { ReactElement, ReactNode, Ref } from \"react\";\n\nimport { mergeClassName } from \"../../utils/merge-class-name\";\nimport { Paragraphs } from \"../paragraphs\";\nimport styles from \"./split-content.module.css\";\n\nexport type SplitContentImagePosition = \"start\" | \"end\";\n\n/** `alt` is required with a photo, `\"\"` when it is decoration. */\ntype SplitContentImage =\n | {\n /** The photo. Without one, the text takes the whole width. */\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`, 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 SplitContentProps = Omit<\n useRender.ComponentProps<\"div\">,\n \"ref\" | \"title\" | \"children\"\n> &\n SplitContentImage & {\n /** The rendered element: the `<div>`, or the element passed to `render`. */\n ref?: Ref<HTMLElement>;\n /** The title. */\n title: ReactNode;\n /** The element for the title, an `<h2>` by default: `titleRender={<h3 />}`. */\n titleRender?: ReactElement;\n /**\n * The text under the title. A string becomes one paragraph; pass `<p>`\n * elements for several.\n */\n text?: ReactNode;\n /**\n * Where the photo sits: `\"start\"` before the text (on the left, or on\n * top below 1024px), `\"end\"` after it.\n * @default \"end\"\n */\n imagePosition?: SplitContentImagePosition;\n /** The call to action under both columns: an `ActionButton`. */\n action?: ReactNode;\n /** The price example under the action, in the small print. */\n priceExample?: ReactNode;\n };\n\n/**\n * Text beside a photo, on the section's own colour: a title and a text in one\n * column, a photo without corners in the other, and the call to action\n * centred under both.\n *\n * ```tsx\n * <Section surface=\"subtle\">\n * <SplitContent\n * title=\"Refinansier dyre smålån\"\n * text=\"Samle kredittkortgjeld og smålån i ett lån med én månedlig betaling.\"\n * src=\"/bilder/par.jpg\"\n * alt=\"\"\n * action={<ActionButton render={<NextLink href=\"/refinansiering\" />}>Søk om refinansiering</ActionButton>}\n * />\n * </Section>\n * ```\n *\n * Two equal columns 64px apart from 1024px, the photo at most 560px wide at\n * the end of its column; one column below, the photo at most 440px and\n * the action at the start. It takes the text colour of its section.\n */\nexport function SplitContent({\n title,\n titleRender,\n text,\n src,\n alt,\n imageRender,\n imagePosition = \"end\",\n action,\n priceExample,\n render,\n ref,\n className,\n ...rest\n}: SplitContentProps) {\n const titleElement = useRender({\n render: titleRender,\n defaultTagName: \"h2\",\n props: { className: styles.Title, 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: \"div\",\n props: {\n ...rest,\n \"data-image-position\": imagePosition,\n \"data-has-image\": image != null || undefined,\n className: mergeClassName(styles.SplitContent, className)({}),\n children: (\n <>\n <div className={styles.Grid}>\n <div className={styles.Text}>\n {titleElement}\n {text != null && (\n <Paragraphs className={styles.Description}>{text}</Paragraphs>\n )}\n </div>\n {image != null && <div className={styles.Media}>{image}</div>}\n </div>\n {(action != null || priceExample != null) && (\n <div className={styles.Actions}>\n {action}\n {priceExample != null && (\n <p className={styles.Price}>{priceExample}</p>\n )}\n </div>\n )}\n </>\n ),\n },\n });\n}\n","/*\n * The website's FourStatsBlock. The list has 64px above and below. From\n * 1024px the numbers and the bars share the row equally, as the website's\n * grid of 2n + 1 columns: a number column is at least 200px (its labels wrap\n * inside it) and the bars, 4px high and round, take what is left. The stats\n * line up at the top and the bars sit on the middle of the numbers (the\n * website centres each stat in the row, so the numbers of stats with longer\n * text sit higher). Below 1024px the bars go and the numbers sit in a row\n * 16px apart, at least 148px each; below 768px in a column 32px apart.\n *\n * No colour of its own: the numbers, the titles and the bars are the\n * section's text colour, and the text under the titles is that colour at\n * 70 %, which reads on all five surfaces (the website's grey, #5B5B5B, on the\n * light ones).\n */\n.Stats {\n /* The line of a number: 240px, 180px below 1024px, 120px below 768px. */\n --stats-number-line: calc(var(--lea-spacing) * 30);\n\n display: flex;\n min-width: 0;\n flex-direction: column;\n}\n\n.List {\n display: flex;\n align-items: flex-start;\n margin: 0;\n padding: var(--lea-spacing-8) 0;\n list-style: none;\n}\n\n.Bar {\n flex: 1 1 0;\n min-width: 0;\n height: var(--lea-spacing-0-5);\n margin-block-start: calc(\n (var(--stats-number-line) - var(--lea-spacing-0-5)) / 2\n );\n border-radius: var(--lea-radius-full);\n background-color: currentColor;\n}\n\n/* The same share of the row as a bar, but never narrower than its content\n (the number keeps it at least 200px). */\n.Stat {\n display: flex;\n flex: 1 1 0;\n flex-direction: column;\n align-items: center;\n gap: var(--lea-spacing-2);\n text-align: center;\n}\n\n/*\n * The 224px numbers in a 240px line are off the type scale, on the website's\n * stats only; 160px in 180px below 1024px and 128px in 120px below 768px.\n */\n.Number {\n /* The website's StatItem min-width: 200px. */\n min-width: calc(var(--lea-spacing) * 25);\n margin: 0;\n font-size: calc(var(--lea-font-step-0) * 14);\n font-weight: var(--lea-font-weight-semibold);\n line-height: var(--stats-number-line);\n white-space: nowrap;\n}\n\n.Labels {\n display: flex;\n width: 100%;\n flex-direction: column;\n gap: var(--lea-spacing-0-5);\n}\n\n/* 18px semibold, as the Accordion card titles: no text style has it. */\n.Title {\n margin: 0;\n font-size: var(--lea-font-step-1);\n font-weight: var(--lea-font-weight-semibold);\n line-height: var(--lea-line-height-snug);\n overflow-wrap: break-word;\n}\n\n.Text {\n margin: 0;\n color: color-mix(in srgb, currentColor 70%, transparent);\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 overflow-wrap: break-word;\n}\n\n.Actions {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--lea-spacing-2-5);\n text-align: center;\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@media (max-width: 1023.95px) {\n .Stats {\n --stats-number-line: calc(var(--lea-spacing) * 22.5);\n }\n\n .List {\n gap: var(--lea-spacing-2);\n }\n\n .Bar {\n display: none;\n }\n\n .Number {\n /* The website's 148px. */\n min-width: calc(var(--lea-spacing) * 18.5);\n font-size: calc(var(--lea-font-step-0) * 10);\n }\n}\n\n@media (max-width: 767.95px) {\n .Stats {\n --stats-number-line: calc(var(--lea-spacing) * 15);\n }\n\n .List {\n flex-direction: column;\n align-items: stretch;\n gap: var(--lea-spacing-4);\n }\n\n .Number {\n font-size: calc(var(--lea-font-step-0) * 8);\n }\n}\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { Fragment, type ReactElement, type ReactNode, type Ref } from \"react\";\n\nimport { mergeClassName } from \"../../utils/merge-class-name\";\nimport styles from \"./stats.module.css\";\n\n/** One number, with what it stands for. */\nexport type StatsItem = {\n /** The big number: a figure, or the step of a process. */\n number: ReactNode;\n /** What the number is. */\n title?: ReactNode;\n /** A line about it, under the title. */\n text?: ReactNode;\n};\n\nexport type StatsProps = 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 /** The heading above the numbers: a `SectionHeader`. */\n header?: ReactNode;\n /** The numbers, in a row from 1024px. Two to four fit the website's row. */\n items: readonly StatsItem[];\n /**\n * The element for the title of each number, an `<h3>` by default, as the\n * titles sit under the section's `<h2>`: `titleRender={<p />}` where they\n * should not be headings.\n */\n titleRender?: ReactElement;\n /** The call to action under the numbers: an `ActionButton`. */\n action?: ReactNode;\n /** The price example under the action, in the small print. */\n priceExample?: ReactNode;\n};\n\n/**\n * A row of big numbers with a line on each side, each with a title and a\n * line of text, as the four steps of \"Slik fungerer det\" on leabank.no's\n * loan pages. The numbers are a list.\n *\n * ```tsx\n * <Section>\n * <Stats\n * header={<SectionHeader eyebrow=\"Søk uforpliktende\" title=\"Slik fungerer det\" />}\n * items={[\n * { number: \"1\", title: \"Fyll inn søknaden\", text: \"Tar ca. 3 minutter.\" },\n * { number: \"2\", title: \"Send inn søknad\", text: \"…\" },\n * ]}\n * />\n * </Section>\n * ```\n *\n * From 1024px the numbers are 224px, at least 200px wide each, with rounded\n * bars filling the space between and around them; below 1024px they are\n * 160px in a row without bars, and below 768px 128px in a column. The bars\n * and the text follow the section's text colour.\n */\nexport function Stats({\n header,\n items,\n titleRender,\n action,\n priceExample,\n render,\n ref,\n className,\n ...rest\n}: StatsProps) {\n return useRender({\n render,\n ref,\n defaultTagName: \"div\",\n props: {\n ...rest,\n className: mergeClassName(styles.Stats, className)({}),\n children: (\n <>\n {header}\n <ul className={styles.List}>\n {items.map((item, index) => (\n <Fragment key={index}>\n <li aria-hidden className={styles.Bar} />\n <StatsStat {...item} titleRender={titleRender} />\n </Fragment>\n ))}\n <li aria-hidden className={styles.Bar} />\n </ul>\n {(action != null || priceExample != null) && (\n <div className={styles.Actions}>\n {action}\n {priceExample != null && (\n <p className={styles.Price}>{priceExample}</p>\n )}\n </div>\n )}\n </>\n ),\n },\n });\n}\n\nfunction StatsStat({\n number,\n title,\n text,\n titleRender,\n}: StatsItem & { titleRender?: ReactElement }) {\n const titleElement = useRender({\n enabled: title != null,\n render: titleRender,\n defaultTagName: \"h3\",\n props: { className: styles.Title, children: title },\n });\n\n return (\n <li className={styles.Stat}>\n <p className={styles.Number}>{number}</p>\n {(titleElement != null || text != null) && (\n <div className={styles.Labels}>\n {titleElement}\n {text != null && <p className={styles.Text}>{text}</p>}\n </div>\n )}\n </li>\n );\n}\n","/*\n * The website's TestimonialsBlock: the header, the featured customer and the\n * carousel, 64px apart. The featured row is two equal columns 64px apart from\n * 768px, centred on each other: the video (a square, `huge` corners,\n * near-black until its still loads, dimmed with `bg.overlay` under the\n * elevated play button) and the quote (stars, 16px, the quote `body-lg` in\n * semibold, 32px, the person). Below 768px the video is on top, at most 560px\n * wide, 32px above the quote. No colour of its own: the text is the\n * section's, the line under the name the section's at 70 %, as `Stats`.\n */\n.Testimonials {\n display: flex;\n min-width: 0;\n flex-direction: column;\n gap: var(--lea-spacing-8);\n}\n\n.Featured {\n display: grid;\n align-items: center;\n gap: var(--lea-spacing-4);\n grid-template-columns: minmax(0, 1fr);\n}\n\n@media (min-width: 768px) {\n .Featured[data-video] {\n gap: var(--lea-spacing-8);\n grid-template-columns: repeat(2, minmax(0, 1fr));\n }\n\n .Featured[data-video-position=\"end\"] .Video {\n order: 1;\n }\n}\n\n.Quote {\n display: flex;\n min-width: 0;\n flex-direction: column;\n align-items: flex-start;\n margin: 0;\n}\n\n.Blockquote {\n display: flex;\n margin: 0;\n flex-direction: column;\n gap: var(--lea-spacing-1);\n font-size: var(--lea-text-body-lg-font-size);\n font-weight: var(--lea-font-weight-semibold);\n line-height: var(--lea-text-body-lg-line-height);\n letter-spacing: var(--lea-text-body-lg-letter-spacing);\n}\n\n.Quote > * + .Blockquote {\n margin-block-start: var(--lea-spacing-2);\n}\n\n.Blockquote > * {\n margin: 0;\n}\n\n/* The quotation marks around the whole quote, as on `TestimonialCard`. */\n.Blockquote > :first-child::before {\n content: \"\\201C\";\n}\n\n.Blockquote > :last-child::after {\n content: \"\\201D\";\n}\n\n.Person {\n display: flex;\n flex-direction: column;\n}\n\n.Quote > * + .Person {\n margin-block-start: var(--lea-spacing-4);\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: color-mix(in srgb, currentColor 70%, transparent);\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.Video {\n position: relative;\n display: grid;\n width: 100%;\n /* The website's 560px below 768px. */\n max-width: calc(var(--lea-spacing) * 70);\n order: -1;\n overflow: hidden;\n border-radius: var(--lea-radius-huge);\n aspect-ratio: 1;\n background-color: var(--lea-palette-bg-contrast);\n isolation: isolate;\n place-items: center;\n}\n\n@media (min-width: 768px) {\n .Video {\n max-width: none;\n }\n}\n\n.Video::after {\n position: absolute;\n background-color: var(--lea-palette-bg-overlay);\n content: \"\";\n inset: 0;\n}\n\n.Still {\n position: absolute;\n display: block;\n width: 100%;\n height: 100%;\n inset: 0;\n object-fit: cover;\n}\n\n.Play {\n position: relative;\n z-index: 1;\n}\n\n/*\n * The video dialog: a near-black popup the width of the large dialog, the\n * video filling it and the close button floating over its corner. Two\n * classes, so the dialog's own look loses whichever stylesheet loads last.\n */\n.DialogViewport .DialogPopup {\n position: relative;\n background-color: var(--lea-palette-bg-contrast);\n color: var(--lea-palette-text-contrast);\n}\n\n.DialogVideo {\n display: block;\n width: 100%;\n max-height: calc(100dvh - var(--lea-spacing-4));\n aspect-ratio: 16 / 9;\n background-color: var(--lea-palette-bg-contrast);\n}\n\n.Close {\n position: absolute;\n z-index: 1;\n inset-block-start: var(--lea-spacing-2);\n inset-inline-end: var(--lea-spacing-2);\n}\n","import { createTranslation } from \"../../index\";\n\n/**\n * The words of `Testimonials`: the carousel's name when the section has no\n * title, the video button and dialog, and the language of the captions\n * (`captionsLanguage`, a BCP 47 tag for the `<track>`, and its name).\n */\nexport const useTestimonialsLabels = createTranslation({\n reviews: {\n no: \"Kundeomtaler\",\n se: \"Kundomdömen\",\n fi: \"Asiakasarviot\",\n es: \"Opiniones de clientes\",\n },\n play: {\n no: \"Spill av video\",\n se: \"Spela upp video\",\n fi: \"Toista video\",\n es: \"Reproducir vídeo\",\n },\n video: {\n no: \"Video fra kunde\",\n se: \"Video från kund\",\n fi: \"Asiakkaan video\",\n es: \"Vídeo de un cliente\",\n },\n close: {\n no: \"Lukk video\",\n se: \"Stäng video\",\n fi: \"Sulje video\",\n es: \"Cerrar vídeo\",\n },\n captionsLanguage: { no: \"nb\", se: \"sv\", fi: \"fi\", es: \"es\" },\n captionsLabel: {\n no: \"Norsk\",\n se: \"Svenska\",\n fi: \"Suomi\",\n es: \"Español\",\n },\n});\n","\"use client\";\n\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { Play, X } from \"lucide-react\";\nimport {\n cloneElement,\n Children,\n useId,\n type ReactElement,\n type ReactNode,\n type Ref,\n} from \"react\";\n\nimport { Dialog, IconButton, StarRating } from \"../../index\";\nimport { mergeClassName } from \"../../utils/merge-class-name\";\nimport { Carousel } from \"../carousel/carousel\";\nimport { SectionHeader } from \"../section-header/section-header\";\nimport styles from \"./testimonials.module.css\";\nimport { useTestimonialsLabels } from \"./use-testimonials-labels\";\n\nexport type TestimonialsVideoPosition = \"start\" | \"end\";\n\n/** The video's other props only make sense with a video. */\ntype TestimonialsVideo =\n | {\n /**\n * A video of the featured customer, shown as a square still with a\n * play button that opens it in a dialog, with sound and controls.\n */\n videoSrc: string;\n /**\n * The still to show. Left out, the video's first frame is shown, which\n * loads the start of the video with the page.\n */\n videoPoster?: string;\n /**\n * Captions for the video, a WebVTT file in the `LeaProvider` language.\n * A video in which people speak needs them.\n */\n videoCaptions?: string;\n /**\n * Where the video sits from 768px: `\"start\"` (on the left of the quote)\n * or `\"end\"`. Below 768px it is always above the quote.\n * @default \"start\"\n */\n videoPosition?: TestimonialsVideoPosition;\n }\n | {\n videoSrc?: undefined;\n videoPoster?: undefined;\n videoCaptions?: undefined;\n videoPosition?: undefined;\n };\n\n/** A subtitle goes under a title. */\ntype TestimonialsHeader =\n | {\n /** The title of the section. It also names the carousel. */\n title: ReactNode;\n /**\n * The element for the title, an `<h2>` by default, as on\n * `SectionHeader`.\n */\n titleRender?: ReactElement;\n /** The text under the title. */\n subtitle?: ReactNode;\n }\n | { title?: undefined; titleRender?: undefined; subtitle?: undefined };\n\nexport type TestimonialsProps = Omit<\n useRender.ComponentProps<\"div\">,\n \"ref\" | \"title\" | \"children\"\n> &\n TestimonialsHeader &\n TestimonialsVideo & {\n /** The rendered element: the `<div>`, or the element passed to `render`. */\n ref?: Ref<HTMLElement>;\n /**\n * What the featured customer said, without quotation marks: the section\n * adds them. Shown large beside the video. A string becomes one\n * paragraph; pass `<p>` elements for several.\n */\n quote?: ReactNode;\n /** Who said the featured quote. */\n name?: ReactNode;\n /** A line under the name, such as the position and the company. */\n meta?: ReactNode;\n /** Stars above the featured quote, from 0 to 5. Left out, no stars. */\n rating?: number;\n /** The reviews in the carousel: `TestimonialCard`s. */\n children?: ReactNode;\n };\n\n/**\n * The website's block of customer reviews: a centred title, a featured\n * customer with a video, and a carousel of `TestimonialCard`s, as on the home\n * page of leabank.no.\n *\n * ```tsx\n * <Section surface=\"subtle\">\n * <Testimonials\n * title=\"Kundeanmeldelser\"\n * subtitle=\"Se hva andre synes om oss\"\n * rating={5}\n * quote=\"Lea Bank gjorde det enkelt å samle lånene mine.\"\n * name=\"Marte Olsen\"\n * meta=\"Kunde, Oslo\"\n * videoSrc=\"/video/marte.mp4\"\n * videoPoster=\"/video/marte.jpg\"\n * >\n * <TestimonialCard rating={5} quote=\"…\" name=\"Kari Nordmann\" meta=\"Kunde, bytt.no\" />\n * <TestimonialCard rating={5} quote=\"…\" name=\"Ola Hansen\" meta=\"Kunde, bytt.no\" />\n * </Testimonials>\n * </Section>\n * ```\n *\n * The parts are 64px apart. From 768px the video (a square with 64px\n * corners, dimmed under its play button) and the featured quote share the\n * width, 64px apart; below 768px the video is on top. The play button,\n * named \"Spill av video\", opens the video in a dialog with its controls and\n * captions, and the focus comes back to it when the dialog closes. The\n * reviews are a `Carousel`, named by the title (or \"Kundeomtaler\" without\n * one). It takes the text colour of its section; the carousel's controls\n * are made for the light surfaces.\n */\nexport function Testimonials({\n title,\n titleRender,\n subtitle,\n quote,\n name,\n meta,\n rating,\n videoSrc,\n videoPoster,\n videoCaptions,\n videoPosition = \"start\",\n render,\n ref,\n className,\n children,\n ...rest\n}: TestimonialsProps) {\n const labels = useTestimonialsLabels();\n const titleId = useId();\n const hasQuote = quote != null || name != null;\n const hasCards = Children.toArray(children).length > 0;\n\n return useRender({\n render,\n ref,\n defaultTagName: \"div\",\n props: {\n ...rest,\n className: mergeClassName(styles.Testimonials, className)({}),\n children: (\n <>\n {title != null && (\n <SectionHeader\n title={title}\n titleRender={cloneElement(titleRender ?? <h2 />, {\n id: titleId,\n })}\n subtitle={subtitle}\n />\n )}\n {(hasQuote || videoSrc != null) && (\n <div\n className={styles.Featured}\n data-video={videoSrc != null || undefined}\n data-video-position={videoPosition}\n >\n {hasQuote && (\n <figure className={styles.Quote}>\n {rating != null && <StarRating rating={rating} />}\n {quote != null && (\n <blockquote className={styles.Blockquote}>\n {typeof quote === \"string\" ||\n typeof quote === \"number\" ? (\n <p>{quote}</p>\n ) : (\n quote\n )}\n </blockquote>\n )}\n {(name != null || meta != null) && (\n <figcaption className={styles.Person}>\n {name != null && (\n <span className={styles.Name}>{name}</span>\n )}\n {meta != null && (\n <span className={styles.Meta}>{meta}</span>\n )}\n </figcaption>\n )}\n </figure>\n )}\n {videoSrc != null && (\n <div className={styles.Video}>\n {videoPoster != null ? (\n <img className={styles.Still} src={videoPoster} alt=\"\" />\n ) : (\n // The first frame as the still: a fragment just past the\n // start, which Safari also draws with `preload=\"metadata\"`.\n <video\n className={styles.Still}\n src={`${videoSrc}#t=0.1`}\n preload=\"metadata\"\n muted\n playsInline\n aria-hidden\n tabIndex={-1}\n />\n )}\n <Dialog.Root>\n <Dialog.Trigger\n render={\n <IconButton\n variant=\"elevated\"\n color=\"secondary\"\n aria-label={labels.play}\n className={styles.Play}\n >\n <Play aria-hidden />\n </IconButton>\n }\n />\n <Dialog.Portal>\n <Dialog.Backdrop />\n <Dialog.Viewport className={styles.DialogViewport}>\n <Dialog.Popup\n size=\"lg\"\n aria-label={labels.video}\n className={styles.DialogPopup}\n >\n <Dialog.Close\n render={\n <IconButton\n variant=\"elevated\"\n color=\"secondary\"\n aria-label={labels.close}\n className={styles.Close}\n >\n <X aria-hidden />\n </IconButton>\n }\n />\n <video\n className={styles.DialogVideo}\n src={videoSrc}\n controls\n autoPlay\n playsInline\n >\n {videoCaptions != null && (\n <track\n kind=\"captions\"\n src={videoCaptions}\n srcLang={labels.captionsLanguage}\n label={labels.captionsLabel}\n default\n />\n )}\n </video>\n </Dialog.Popup>\n </Dialog.Viewport>\n </Dialog.Portal>\n </Dialog.Root>\n </div>\n )}\n </div>\n )}\n {hasCards && (\n <Carousel\n {...(title != null\n ? { \"aria-labelledby\": titleId }\n : { \"aria-label\": labels.reviews })}\n >\n {children}\n </Carousel>\n )}\n </>\n ),\n },\n });\n}\n"],"mappings":"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"}
|