rei-kit 0.17.1 → 0.17.2
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/README.md +1 -1
- package/dist/index.js +1 -1
- package/dist/styles.css +19 -15
- package/dist/web.js +8 -10
- package/dist/web.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -44,7 +44,7 @@ the app, and not a matter of taste.
|
|
|
44
44
|
|
|
45
45
|
## Status
|
|
46
46
|
|
|
47
|
-
**v0.17.
|
|
47
|
+
**v0.17.2 — three consumers.**
|
|
48
48
|
|
|
49
49
|
| | |
|
|
50
50
|
| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
package/dist/index.js
CHANGED
|
@@ -1342,7 +1342,7 @@ function createI18nRuntime(options) {
|
|
|
1342
1342
|
*
|
|
1343
1343
|
* The fallback keeps `vitest` and `vite dev` honest, where no define runs.
|
|
1344
1344
|
*/
|
|
1345
|
-
var VERSION = "0.17.
|
|
1345
|
+
var VERSION = "0.17.2";
|
|
1346
1346
|
//#endregion
|
|
1347
1347
|
export { AppError, BaseAlert_default as BaseAlert, BaseBadge_default as BaseBadge, BaseButton_default as BaseButton, BaseCard_default as BaseCard, BaseCheckbox_default as BaseCheckbox, BaseInput_default as BaseInput, BaseRadioGroup_default as BaseRadioGroup, BaseSelect_default as BaseSelect, BaseSheet_default as BaseSheet, BaseTextarea_default as BaseTextarea, EmptyState_default as EmptyState, ErrorBoundary_default as ErrorBoundary, FormField_default as FormField, GoogleButton_default as GoogleButton, LocaleLinks_default as LocaleLinks, PageContainer_default as PageContainer, PageHeader_default as PageHeader, PriceCard_default as PriceCard, ProgressBar_default as ProgressBar, SectionHeading_default as SectionHeading, SegmentedControl_default as SegmentedControl, SettingsGroup_default as SettingsGroup, SettingsRow_default as SettingsRow, SkeletonList_default as SkeletonList, StatCard_default as StatCard, TabBar_default as TabBar, ToastHost_default as ToastHost, ToneDot_default as ToneDot, VERSION, addDays, applyTheme, createI18nRuntime, downloadJson, eachDayOfYear, formatDate, fromDateKey, isApplePortable, isInstalled, isThemePreference, lastNDays, leadingBlanks, needsIosInstall, readStoredTheme, registerErrorMapper, relativeDayLabel, safeRedirect, setFormatLocale, setThemeStorageKey, startOfWeek, tapFeedback, toAppError, toDateKey, toRedirectPath, todayKey, useDebouncedCallback, useDragScroll, useMediaQuery, useOnline, useTheme, useToast, useToday, useVisualViewport };
|
|
1348
1348
|
|
package/dist/styles.css
CHANGED
|
@@ -800,19 +800,23 @@
|
|
|
800
800
|
}
|
|
801
801
|
}
|
|
802
802
|
|
|
803
|
-
/*
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
803
|
+
/* The root carries no layout of its own, and that is the fix for a real bug
|
|
804
|
+
rather than a style preference.
|
|
805
|
+
|
|
806
|
+
A rule on the root loses to nothing: Vue's scoped CSS compiles `.rk-nav` to
|
|
807
|
+
`.rk-nav[data-v-hash]`, which outranks a utility class like Tailwind's
|
|
808
|
+
`hidden` outright — so `class="hidden sm:flex"` did nothing and the nav
|
|
809
|
+
appeared on a phone, on top of the call to action. `:where()` does not save
|
|
810
|
+
it either; the scoping attribute puts the specificity back.
|
|
811
|
+
|
|
812
|
+
So the flex row lives on an element the caller does not own, and the root is
|
|
813
|
+
left for them to style. `TabBar` is built the same way for the same reason. */
|
|
814
|
+
.rk-nav-inner[data-v-5e640548] {
|
|
811
815
|
display: flex;
|
|
812
816
|
align-items: center;
|
|
813
817
|
gap: 0.25rem;
|
|
814
818
|
}
|
|
815
|
-
.rk-nav-link[data-v-
|
|
819
|
+
.rk-nav-link[data-v-5e640548] {
|
|
816
820
|
position: relative;
|
|
817
821
|
border-radius: var(--radius-cell);
|
|
818
822
|
padding: 0.5rem 0.75rem;
|
|
@@ -820,18 +824,18 @@
|
|
|
820
824
|
color: var(--color-ink-soft);
|
|
821
825
|
transition: color 150ms;
|
|
822
826
|
}
|
|
823
|
-
.rk-nav-link[data-v-
|
|
827
|
+
.rk-nav-link[data-v-5e640548]:hover {
|
|
824
828
|
color: var(--color-ink);
|
|
825
829
|
}
|
|
826
|
-
.rk-nav-link[data-v-
|
|
830
|
+
.rk-nav-link[data-v-5e640548]:focus-visible {
|
|
827
831
|
outline: 2px solid var(--color-primary);
|
|
828
832
|
outline-offset: 2px;
|
|
829
833
|
}
|
|
830
|
-
.rk-nav-link.is-active[data-v-
|
|
834
|
+
.rk-nav-link.is-active[data-v-5e640548] {
|
|
831
835
|
color: var(--color-ink);
|
|
832
836
|
font-weight: 500;
|
|
833
837
|
}
|
|
834
|
-
.rk-nav-rule[data-v-
|
|
838
|
+
.rk-nav-rule[data-v-5e640548] {
|
|
835
839
|
position: absolute;
|
|
836
840
|
inset-inline: 0.75rem;
|
|
837
841
|
bottom: -1px;
|
|
@@ -842,11 +846,11 @@
|
|
|
842
846
|
transform-origin: left;
|
|
843
847
|
transition: transform 300ms;
|
|
844
848
|
}
|
|
845
|
-
.rk-nav-link.is-active .rk-nav-rule[data-v-
|
|
849
|
+
.rk-nav-link.is-active .rk-nav-rule[data-v-5e640548] {
|
|
846
850
|
transform: scaleX(1);
|
|
847
851
|
}
|
|
848
852
|
@media (prefers-reduced-motion: reduce) {
|
|
849
|
-
.rk-nav-rule[data-v-
|
|
853
|
+
.rk-nav-rule[data-v-5e640548] {
|
|
850
854
|
transition: none;
|
|
851
855
|
}
|
|
852
856
|
}
|
package/dist/web.js
CHANGED
|
@@ -3,7 +3,7 @@ import { Fragment, Teleport, Transition, createBlock, createCommentVNode, create
|
|
|
3
3
|
import { RouterLink } from "vue-router";
|
|
4
4
|
//#region src/web/BaseAccordion.vue?vue&type=script&setup=true&lang.ts
|
|
5
5
|
var _hoisted_1$2 = { class: "rk-accordion" };
|
|
6
|
-
var _hoisted_2$
|
|
6
|
+
var _hoisted_2$2 = [
|
|
7
7
|
"aria-expanded",
|
|
8
8
|
"aria-controls",
|
|
9
9
|
"onClick"
|
|
@@ -47,7 +47,7 @@ var BaseAccordion_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @
|
|
|
47
47
|
"aria-expanded": isOpen(item.key),
|
|
48
48
|
"aria-controls": `${unref(uid)}-${item.key}`,
|
|
49
49
|
onClick: ($event) => toggle(item.key)
|
|
50
|
-
}, [createElementVNode("span", _hoisted_3$1, toDisplayString(item.title), 1), createElementVNode("span", _hoisted_4$1, [_cache[0] || (_cache[0] = createElementVNode("span", { class: "rk-accordion-bar" }, null, -1)), createElementVNode("span", { class: normalizeClass(["rk-accordion-bar rk-accordion-bar-turn", { "is-open": isOpen(item.key) }]) }, null, 2)])], 8, _hoisted_2$
|
|
50
|
+
}, [createElementVNode("span", _hoisted_3$1, toDisplayString(item.title), 1), createElementVNode("span", _hoisted_4$1, [_cache[0] || (_cache[0] = createElementVNode("span", { class: "rk-accordion-bar" }, null, -1)), createElementVNode("span", { class: normalizeClass(["rk-accordion-bar rk-accordion-bar-turn", { "is-open": isOpen(item.key) }]) }, null, 2)])], 8, _hoisted_2$2)]),
|
|
51
51
|
_: 2
|
|
52
52
|
}, 1024)), createElementVNode("div", {
|
|
53
53
|
id: `${unref(uid)}-${item.key}`,
|
|
@@ -63,7 +63,7 @@ var BaseAccordion_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @
|
|
|
63
63
|
//#endregion
|
|
64
64
|
//#region src/web/BaseModal.vue?vue&type=script&setup=true&lang.ts
|
|
65
65
|
var _hoisted_1$1 = ["role", "aria-label"];
|
|
66
|
-
var _hoisted_2 = { class: "rk-modal-head" };
|
|
66
|
+
var _hoisted_2$1 = { class: "rk-modal-head" };
|
|
67
67
|
var _hoisted_3 = { class: "rk-modal-title" };
|
|
68
68
|
var _hoisted_4 = ["aria-label"];
|
|
69
69
|
var _hoisted_5 = { class: "rk-modal-body" };
|
|
@@ -180,7 +180,7 @@ var BaseModal_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PU
|
|
|
180
180
|
"aria-label": __props.title,
|
|
181
181
|
tabindex: "-1"
|
|
182
182
|
}, [
|
|
183
|
-
createElementVNode("div", _hoisted_2, [createElementVNode("h2", _hoisted_3, toDisplayString(__props.title), 1), __props.dismissible ? (openBlock(), createElementBlock("button", {
|
|
183
|
+
createElementVNode("div", _hoisted_2$1, [createElementVNode("h2", _hoisted_3, toDisplayString(__props.title), 1), __props.dismissible ? (openBlock(), createElementBlock("button", {
|
|
184
184
|
key: 0,
|
|
185
185
|
type: "button",
|
|
186
186
|
class: "rk-modal-close",
|
|
@@ -206,6 +206,7 @@ var BaseModal_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PU
|
|
|
206
206
|
//#endregion
|
|
207
207
|
//#region src/web/NavLinks.vue?vue&type=script&setup=true&lang.ts
|
|
208
208
|
var _hoisted_1 = ["aria-label"];
|
|
209
|
+
var _hoisted_2 = { class: "rk-nav-inner" };
|
|
209
210
|
//#endregion
|
|
210
211
|
//#region src/web/NavLinks.vue
|
|
211
212
|
var NavLinks_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
|
|
@@ -217,10 +218,7 @@ var NavLinks_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PUR
|
|
|
217
218
|
},
|
|
218
219
|
setup(__props) {
|
|
219
220
|
return (_ctx, _cache) => {
|
|
220
|
-
return openBlock(), createElementBlock("nav", {
|
|
221
|
-
class: "rk-nav",
|
|
222
|
-
"aria-label": __props.label || void 0
|
|
223
|
-
}, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.items, (item) => {
|
|
221
|
+
return openBlock(), createElementBlock("nav", { "aria-label": __props.label || void 0 }, [createElementVNode("div", _hoisted_2, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.items, (item) => {
|
|
224
222
|
return openBlock(), createBlock(unref(RouterLink), {
|
|
225
223
|
key: item.key,
|
|
226
224
|
to: item.to,
|
|
@@ -237,10 +235,10 @@ var NavLinks_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PUR
|
|
|
237
235
|
"class",
|
|
238
236
|
"aria-current"
|
|
239
237
|
]);
|
|
240
|
-
}), 128))], 8, _hoisted_1);
|
|
238
|
+
}), 128))])], 8, _hoisted_1);
|
|
241
239
|
};
|
|
242
240
|
}
|
|
243
|
-
}), [["__scopeId", "data-v-
|
|
241
|
+
}), [["__scopeId", "data-v-5e640548"]]);
|
|
244
242
|
//#endregion
|
|
245
243
|
export { BaseAccordion_default as BaseAccordion, BaseModal_default as BaseModal, NavLinks_default as NavLinks };
|
|
246
244
|
|
package/dist/web.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web.js","names":["$slots"],"sources":["../src/web/BaseAccordion.vue","../src/web/BaseAccordion.vue","../src/web/BaseModal.vue","../src/web/BaseModal.vue","../src/web/NavLinks.vue","../src/web/NavLinks.vue"],"sourcesContent":["<script setup lang=\"ts\" generic=\"K extends string\">\nimport { shallowRef, useId } from 'vue'\n\nexport interface AccordionItem<K extends string> {\n key: K\n /** The question. Already translated. */\n title: string\n}\n\n/**\n * A list that opens one section at a time.\n *\n * ## Why not `<details>`\n *\n * `<details>` is the honest element for this and it was the first choice. A\n * browser removes its content from layout entirely when closed, so there is\n * nothing to animate between — the panel can only appear. Faking it by leaving\n * `open` set and hiding with CSS tells a screen reader the section is expanded\n * while a sighted reader sees it shut, which is worse than no animation.\n *\n * So: a button carrying `aria-expanded`, the answer always in the markup — the\n * prerendered HTML carries every answer, which is what a crawler and a reader\n * without JavaScript get — and a grid row that travels from `0fr` to `1fr`,\n * which is the one way to animate to a height nobody has measured.\n *\n * The content is always present, so anything expensive in it is paid for on\n * first paint. That is the trade: a section a crawler can read costs a section\n * that is never lazy.\n */\nconst {\n items,\n multiple = false,\n headingLevel = 3,\n} = defineProps<{\n items: readonly AccordionItem<K>[]\n /** Allow several open at once. Off by default, which is what makes it an accordion. */\n multiple?: boolean | undefined\n /**\n * Which heading the control sits inside.\n *\n * A heading, always — it is how a screen reader user moves between sections\n * without opening any of them. The level is a prop because it depends on what\n * is above it on the page, and getting it wrong breaks the outline.\n */\n headingLevel?: 2 | 3 | 4 | undefined\n}>()\n\ndefineSlots<{\n /** The answer, per item. */\n default: (props: { item: AccordionItem<K>; open: boolean }) => unknown\n}>()\n\nconst uid = useId()\n/* `shallowRef` because the list is replaced wholesale rather than mutated, and\n because a deep ref unwraps a generic key type into something TypeScript can\n no longer match against `K`. */\nconst open = shallowRef<K[]>([])\n\nconst isOpen = (key: K) => open.value.includes(key)\n\nfunction toggle(key: K) {\n if (isOpen(key)) {\n open.value = open.value.filter((k) => k !== key)\n } else {\n open.value = multiple ? [...open.value, key] : [key]\n }\n}\n</script>\n\n<template>\n <ul class=\"rk-accordion\">\n <li v-for=\"item in items\" :key=\"item.key\" class=\"rk-accordion-item\">\n <component :is=\"`h${headingLevel}`\">\n <button\n type=\"button\"\n class=\"rk-accordion-control\"\n :aria-expanded=\"isOpen(item.key)\"\n :aria-controls=\"`${uid}-${item.key}`\"\n @click=\"toggle(item.key)\"\n >\n <span class=\"rk-accordion-title\">{{ item.title }}</span>\n\n <!-- A plus that becomes a minus: one stroke turns, so the control\n keeps its place while it changes meaning. -->\n <span class=\"rk-accordion-mark\" aria-hidden=\"true\">\n <span class=\"rk-accordion-bar\" />\n <span\n class=\"rk-accordion-bar rk-accordion-bar-turn\"\n :class=\"{ 'is-open': isOpen(item.key) }\"\n />\n </span>\n </button>\n </component>\n\n <div\n :id=\"`${uid}-${item.key}`\"\n class=\"rk-accordion-panel\"\n :class=\"{ 'is-open': isOpen(item.key) }\"\n >\n <div class=\"rk-accordion-clip\">\n <div class=\"rk-accordion-body\" :class=\"{ 'is-open': isOpen(item.key) }\">\n <slot :item=\"item\" :open=\"isOpen(item.key)\" />\n </div>\n </div>\n </div>\n </li>\n </ul>\n</template>\n\n<style scoped>\n.rk-accordion {\n border-top: 1px solid color-mix(in srgb, var(--color-hair) 70%, transparent);\n}\n\n.rk-accordion-item {\n border-bottom: 1px solid color-mix(in srgb, var(--color-hair) 70%, transparent);\n}\n\n.rk-accordion-control {\n display: flex;\n width: 100%;\n cursor: pointer;\n align-items: center;\n justify-content: space-between;\n gap: 1.5rem;\n padding: 1.25rem 0;\n text-align: left;\n transition: color 150ms;\n}\n\n.rk-accordion-control:hover {\n color: var(--color-primary);\n}\n\n.rk-accordion-control:focus-visible {\n outline: 2px solid var(--color-primary);\n outline-offset: 2px;\n}\n\n.rk-accordion-title {\n color: var(--color-ink);\n font-size: 1rem;\n font-weight: 500;\n}\n\n.rk-accordion-mark {\n position: relative;\n display: grid;\n place-items: center;\n flex-shrink: 0;\n width: 1.5rem;\n height: 1.5rem;\n}\n\n.rk-accordion-bar {\n position: absolute;\n height: 1px;\n width: 0.875rem;\n background: var(--color-ink-soft);\n}\n\n.rk-accordion-bar-turn {\n transform: rotate(90deg);\n transition: transform 420ms ease-out;\n}\n\n.rk-accordion-bar-turn.is-open {\n transform: rotate(0deg);\n}\n\n/* 0fr to 1fr is the whole trick: a grid row can be animated to a height that\n was never measured, which is what `height: auto` cannot do. */\n.rk-accordion-panel {\n display: grid;\n grid-template-rows: 0fr;\n transition: grid-template-rows 420ms ease-out;\n}\n\n.rk-accordion-panel.is-open {\n grid-template-rows: 1fr;\n}\n\n.rk-accordion-clip {\n overflow: hidden;\n}\n\n.rk-accordion-body {\n max-width: 68ch;\n padding-bottom: 1.5rem;\n color: var(--color-ink-soft);\n line-height: 1.625;\n opacity: 0;\n transition: opacity 300ms;\n}\n\n.rk-accordion-body.is-open {\n opacity: 1;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .rk-accordion-panel,\n .rk-accordion-bar-turn,\n .rk-accordion-body {\n transition: none;\n }\n}\n</style>\n","<script setup lang=\"ts\" generic=\"K extends string\">\nimport { shallowRef, useId } from 'vue'\n\nexport interface AccordionItem<K extends string> {\n key: K\n /** The question. Already translated. */\n title: string\n}\n\n/**\n * A list that opens one section at a time.\n *\n * ## Why not `<details>`\n *\n * `<details>` is the honest element for this and it was the first choice. A\n * browser removes its content from layout entirely when closed, so there is\n * nothing to animate between — the panel can only appear. Faking it by leaving\n * `open` set and hiding with CSS tells a screen reader the section is expanded\n * while a sighted reader sees it shut, which is worse than no animation.\n *\n * So: a button carrying `aria-expanded`, the answer always in the markup — the\n * prerendered HTML carries every answer, which is what a crawler and a reader\n * without JavaScript get — and a grid row that travels from `0fr` to `1fr`,\n * which is the one way to animate to a height nobody has measured.\n *\n * The content is always present, so anything expensive in it is paid for on\n * first paint. That is the trade: a section a crawler can read costs a section\n * that is never lazy.\n */\nconst {\n items,\n multiple = false,\n headingLevel = 3,\n} = defineProps<{\n items: readonly AccordionItem<K>[]\n /** Allow several open at once. Off by default, which is what makes it an accordion. */\n multiple?: boolean | undefined\n /**\n * Which heading the control sits inside.\n *\n * A heading, always — it is how a screen reader user moves between sections\n * without opening any of them. The level is a prop because it depends on what\n * is above it on the page, and getting it wrong breaks the outline.\n */\n headingLevel?: 2 | 3 | 4 | undefined\n}>()\n\ndefineSlots<{\n /** The answer, per item. */\n default: (props: { item: AccordionItem<K>; open: boolean }) => unknown\n}>()\n\nconst uid = useId()\n/* `shallowRef` because the list is replaced wholesale rather than mutated, and\n because a deep ref unwraps a generic key type into something TypeScript can\n no longer match against `K`. */\nconst open = shallowRef<K[]>([])\n\nconst isOpen = (key: K) => open.value.includes(key)\n\nfunction toggle(key: K) {\n if (isOpen(key)) {\n open.value = open.value.filter((k) => k !== key)\n } else {\n open.value = multiple ? [...open.value, key] : [key]\n }\n}\n</script>\n\n<template>\n <ul class=\"rk-accordion\">\n <li v-for=\"item in items\" :key=\"item.key\" class=\"rk-accordion-item\">\n <component :is=\"`h${headingLevel}`\">\n <button\n type=\"button\"\n class=\"rk-accordion-control\"\n :aria-expanded=\"isOpen(item.key)\"\n :aria-controls=\"`${uid}-${item.key}`\"\n @click=\"toggle(item.key)\"\n >\n <span class=\"rk-accordion-title\">{{ item.title }}</span>\n\n <!-- A plus that becomes a minus: one stroke turns, so the control\n keeps its place while it changes meaning. -->\n <span class=\"rk-accordion-mark\" aria-hidden=\"true\">\n <span class=\"rk-accordion-bar\" />\n <span\n class=\"rk-accordion-bar rk-accordion-bar-turn\"\n :class=\"{ 'is-open': isOpen(item.key) }\"\n />\n </span>\n </button>\n </component>\n\n <div\n :id=\"`${uid}-${item.key}`\"\n class=\"rk-accordion-panel\"\n :class=\"{ 'is-open': isOpen(item.key) }\"\n >\n <div class=\"rk-accordion-clip\">\n <div class=\"rk-accordion-body\" :class=\"{ 'is-open': isOpen(item.key) }\">\n <slot :item=\"item\" :open=\"isOpen(item.key)\" />\n </div>\n </div>\n </div>\n </li>\n </ul>\n</template>\n\n<style scoped>\n.rk-accordion {\n border-top: 1px solid color-mix(in srgb, var(--color-hair) 70%, transparent);\n}\n\n.rk-accordion-item {\n border-bottom: 1px solid color-mix(in srgb, var(--color-hair) 70%, transparent);\n}\n\n.rk-accordion-control {\n display: flex;\n width: 100%;\n cursor: pointer;\n align-items: center;\n justify-content: space-between;\n gap: 1.5rem;\n padding: 1.25rem 0;\n text-align: left;\n transition: color 150ms;\n}\n\n.rk-accordion-control:hover {\n color: var(--color-primary);\n}\n\n.rk-accordion-control:focus-visible {\n outline: 2px solid var(--color-primary);\n outline-offset: 2px;\n}\n\n.rk-accordion-title {\n color: var(--color-ink);\n font-size: 1rem;\n font-weight: 500;\n}\n\n.rk-accordion-mark {\n position: relative;\n display: grid;\n place-items: center;\n flex-shrink: 0;\n width: 1.5rem;\n height: 1.5rem;\n}\n\n.rk-accordion-bar {\n position: absolute;\n height: 1px;\n width: 0.875rem;\n background: var(--color-ink-soft);\n}\n\n.rk-accordion-bar-turn {\n transform: rotate(90deg);\n transition: transform 420ms ease-out;\n}\n\n.rk-accordion-bar-turn.is-open {\n transform: rotate(0deg);\n}\n\n/* 0fr to 1fr is the whole trick: a grid row can be animated to a height that\n was never measured, which is what `height: auto` cannot do. */\n.rk-accordion-panel {\n display: grid;\n grid-template-rows: 0fr;\n transition: grid-template-rows 420ms ease-out;\n}\n\n.rk-accordion-panel.is-open {\n grid-template-rows: 1fr;\n}\n\n.rk-accordion-clip {\n overflow: hidden;\n}\n\n.rk-accordion-body {\n max-width: 68ch;\n padding-bottom: 1.5rem;\n color: var(--color-ink-soft);\n line-height: 1.625;\n opacity: 0;\n transition: opacity 300ms;\n}\n\n.rk-accordion-body.is-open {\n opacity: 1;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .rk-accordion-panel,\n .rk-accordion-bar-turn,\n .rk-accordion-body {\n transition: none;\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { nextTick, onBeforeUnmount, ref, watch } from 'vue'\n\n/**\n * A dialog that arrives from nowhere.\n *\n * Not `BaseSheet` with different padding. A sheet slides up from the bottom\n * edge, is dismissed by dragging it back down, and belongs to a thumb; a modal\n * appears in the middle of what you were reading and is dismissed by leaving\n * it. They are two answers to two questions and merging them would give a\n * component that is wrong on a phone and wrong on a desktop.\n *\n * ## The parts that are easy to get wrong\n *\n * This is why it exists rather than being written per dialog, which is what the\n * one wide consumer was doing in two places. None of the following is visible\n * on screen when it is missing:\n *\n * - **Focus moves in and comes back.** Opening without moving focus leaves a\n * keyboard user behind the dialog, operating a page they cannot see. Closing\n * without restoring it drops them at the top of the document.\n * - **Tab does not leave.** A dialog you can Tab out of is a dialog that is\n * modal only to somebody using a mouse.\n * - **Escape closes.** Every dialog in every application does this.\n * - **The page underneath does not scroll.** Otherwise dismissing the dialog\n * returns you somewhere else.\n * - **The backdrop closes it, the panel does not.** A click that starts inside\n * the panel and ends on the backdrop is not a click on the backdrop, which is\n * why this listens for a press on the backdrop itself rather than any click\n * that reaches it.\n */\nconst {\n title,\n closeLabel,\n tone = 'default',\n dismissible = true,\n} = defineProps<{\n /** Names the dialog for assistive tech. Required; an unnamed dialog is a box. */\n title: string\n /** Accessible name for the close button. */\n closeLabel: string\n /**\n * `alert` marks it as a decision that interrupts — `role=\"alertdialog\"`,\n * which a screen reader announces immediately rather than on arrival.\n */\n tone?: 'default' | 'alert' | undefined\n /**\n * Whether Escape and the backdrop close it.\n *\n * Off for a dialog that must be answered — but never off as a way of forcing\n * a reader to a decision they have not been given the information for.\n */\n dismissible?: boolean | undefined\n}>()\n\nconst open = defineModel<boolean>({ default: false })\n\ndefineSlots<{\n /** The body. */\n default: () => unknown\n /** Buttons, at the foot. */\n actions?: () => unknown\n}>()\n\nconst panel = ref<HTMLElement | null>(null)\n/** Who had focus before this opened, so it can be given back. */\nlet restoreTo: HTMLElement | null = null\n\nconst FOCUSABLE =\n 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])'\n\nfunction focusable(): HTMLElement[] {\n if (!panel.value) return []\n\n return Array.from(panel.value.querySelectorAll<HTMLElement>(FOCUSABLE))\n}\n\nfunction onKeydown(event: KeyboardEvent) {\n if (event.key === 'Escape' && dismissible) {\n event.stopPropagation()\n open.value = false\n\n return\n }\n\n if (event.key !== 'Tab') return\n\n const items = focusable()\n if (items.length === 0) return event.preventDefault()\n\n const first = items[0]!\n const last = items[items.length - 1]!\n const active = document.activeElement\n\n // The wrap has to be done by hand: the browser's own Tab order is the whole\n // document, and the dialog is only part of it.\n if (event.shiftKey && (active === first || !panel.value?.contains(active))) {\n event.preventDefault()\n last.focus()\n } else if (!event.shiftKey && active === last) {\n event.preventDefault()\n first.focus()\n }\n}\n\nwatch(open, async (isOpen) => {\n if (isOpen) {\n restoreTo = document.activeElement as HTMLElement | null\n document.body.style.overflow = 'hidden'\n await nextTick()\n // The panel itself when it holds nothing focusable, so focus is at least\n // inside the dialog rather than behind it.\n ;(focusable()[0] ?? panel.value)?.focus()\n } else {\n document.body.style.overflow = ''\n restoreTo?.focus()\n restoreTo = null\n }\n})\n\n// A dialog unmounted while open would otherwise leave the page unscrollable,\n// with nothing on screen to explain why.\nonBeforeUnmount(() => {\n if (open.value) document.body.style.overflow = ''\n})\n</script>\n\n<template>\n <Teleport to=\"body\">\n <Transition name=\"rk-modal\">\n <div\n v-if=\"open\"\n class=\"rk-modal-scrim\"\n @keydown=\"onKeydown\"\n @mousedown.self=\"dismissible && (open = false)\"\n >\n <div\n ref=\"panel\"\n class=\"rk-modal-panel\"\n :role=\"tone === 'alert' ? 'alertdialog' : 'dialog'\"\n aria-modal=\"true\"\n :aria-label=\"title\"\n tabindex=\"-1\"\n >\n <div class=\"rk-modal-head\">\n <h2 class=\"rk-modal-title\">{{ title }}</h2>\n\n <button\n v-if=\"dismissible\"\n type=\"button\"\n class=\"rk-modal-close\"\n :aria-label=\"closeLabel\"\n @click=\"open = false\"\n >\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M18 6 6 18M6 6l12 12\" stroke-linecap=\"round\" />\n </svg>\n </button>\n </div>\n\n <div class=\"rk-modal-body\"><slot /></div>\n\n <div v-if=\"$slots.actions\" class=\"rk-modal-actions\"><slot name=\"actions\" /></div>\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n\n<style scoped>\n.rk-modal-scrim {\n position: fixed;\n inset: 0;\n z-index: 50;\n display: grid;\n place-items: center;\n padding: 1.5rem;\n background: color-mix(in srgb, var(--color-ink) 70%, transparent);\n backdrop-filter: blur(4px);\n}\n\n.rk-modal-panel {\n width: 100%;\n max-width: 28rem;\n max-height: 85vh;\n overflow-y: auto;\n border-radius: var(--radius-card);\n background: var(--color-surface);\n padding: 1.5rem;\n box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.2);\n}\n\n.rk-modal-panel:focus {\n outline: none;\n}\n\n.rk-modal-head {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 1rem;\n}\n\n.rk-modal-title {\n font-size: 1.0625rem;\n font-weight: 600;\n color: var(--color-ink);\n}\n\n.rk-modal-close {\n display: grid;\n place-items: center;\n flex-shrink: 0;\n width: 2rem;\n height: 2rem;\n border-radius: 9999px;\n color: var(--color-ink-soft);\n transition: background-color 150ms;\n}\n\n.rk-modal-close:hover {\n background: var(--color-muted);\n}\n\n.rk-modal-close:focus-visible {\n outline: 2px solid var(--color-primary);\n outline-offset: 2px;\n}\n\n.rk-modal-close svg {\n width: 1rem;\n height: 1rem;\n}\n\n.rk-modal-body {\n margin-top: 0.75rem;\n color: var(--color-ink-soft);\n font-size: 0.875rem;\n line-height: 1.625;\n}\n\n.rk-modal-actions {\n margin-top: 1.5rem;\n display: flex;\n justify-content: flex-end;\n gap: 0.5rem;\n}\n\n.rk-modal-enter-active,\n.rk-modal-leave-active {\n transition: opacity 180ms ease;\n}\n\n.rk-modal-enter-active .rk-modal-panel,\n.rk-modal-leave-active .rk-modal-panel {\n transition:\n transform 180ms cubic-bezier(0.32, 0.72, 0, 1),\n opacity 180ms ease;\n}\n\n.rk-modal-enter-from,\n.rk-modal-leave-to {\n opacity: 0;\n}\n\n/* From nowhere, not from an edge: a scale is what separates this from a sheet. */\n.rk-modal-enter-from .rk-modal-panel,\n.rk-modal-leave-to .rk-modal-panel {\n transform: scale(0.96);\n opacity: 0;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .rk-modal-enter-active .rk-modal-panel,\n .rk-modal-leave-active .rk-modal-panel {\n transition: opacity 180ms ease;\n transform: none;\n }\n\n .rk-modal-enter-from .rk-modal-panel,\n .rk-modal-leave-to .rk-modal-panel {\n transform: none;\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { nextTick, onBeforeUnmount, ref, watch } from 'vue'\n\n/**\n * A dialog that arrives from nowhere.\n *\n * Not `BaseSheet` with different padding. A sheet slides up from the bottom\n * edge, is dismissed by dragging it back down, and belongs to a thumb; a modal\n * appears in the middle of what you were reading and is dismissed by leaving\n * it. They are two answers to two questions and merging them would give a\n * component that is wrong on a phone and wrong on a desktop.\n *\n * ## The parts that are easy to get wrong\n *\n * This is why it exists rather than being written per dialog, which is what the\n * one wide consumer was doing in two places. None of the following is visible\n * on screen when it is missing:\n *\n * - **Focus moves in and comes back.** Opening without moving focus leaves a\n * keyboard user behind the dialog, operating a page they cannot see. Closing\n * without restoring it drops them at the top of the document.\n * - **Tab does not leave.** A dialog you can Tab out of is a dialog that is\n * modal only to somebody using a mouse.\n * - **Escape closes.** Every dialog in every application does this.\n * - **The page underneath does not scroll.** Otherwise dismissing the dialog\n * returns you somewhere else.\n * - **The backdrop closes it, the panel does not.** A click that starts inside\n * the panel and ends on the backdrop is not a click on the backdrop, which is\n * why this listens for a press on the backdrop itself rather than any click\n * that reaches it.\n */\nconst {\n title,\n closeLabel,\n tone = 'default',\n dismissible = true,\n} = defineProps<{\n /** Names the dialog for assistive tech. Required; an unnamed dialog is a box. */\n title: string\n /** Accessible name for the close button. */\n closeLabel: string\n /**\n * `alert` marks it as a decision that interrupts — `role=\"alertdialog\"`,\n * which a screen reader announces immediately rather than on arrival.\n */\n tone?: 'default' | 'alert' | undefined\n /**\n * Whether Escape and the backdrop close it.\n *\n * Off for a dialog that must be answered — but never off as a way of forcing\n * a reader to a decision they have not been given the information for.\n */\n dismissible?: boolean | undefined\n}>()\n\nconst open = defineModel<boolean>({ default: false })\n\ndefineSlots<{\n /** The body. */\n default: () => unknown\n /** Buttons, at the foot. */\n actions?: () => unknown\n}>()\n\nconst panel = ref<HTMLElement | null>(null)\n/** Who had focus before this opened, so it can be given back. */\nlet restoreTo: HTMLElement | null = null\n\nconst FOCUSABLE =\n 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])'\n\nfunction focusable(): HTMLElement[] {\n if (!panel.value) return []\n\n return Array.from(panel.value.querySelectorAll<HTMLElement>(FOCUSABLE))\n}\n\nfunction onKeydown(event: KeyboardEvent) {\n if (event.key === 'Escape' && dismissible) {\n event.stopPropagation()\n open.value = false\n\n return\n }\n\n if (event.key !== 'Tab') return\n\n const items = focusable()\n if (items.length === 0) return event.preventDefault()\n\n const first = items[0]!\n const last = items[items.length - 1]!\n const active = document.activeElement\n\n // The wrap has to be done by hand: the browser's own Tab order is the whole\n // document, and the dialog is only part of it.\n if (event.shiftKey && (active === first || !panel.value?.contains(active))) {\n event.preventDefault()\n last.focus()\n } else if (!event.shiftKey && active === last) {\n event.preventDefault()\n first.focus()\n }\n}\n\nwatch(open, async (isOpen) => {\n if (isOpen) {\n restoreTo = document.activeElement as HTMLElement | null\n document.body.style.overflow = 'hidden'\n await nextTick()\n // The panel itself when it holds nothing focusable, so focus is at least\n // inside the dialog rather than behind it.\n ;(focusable()[0] ?? panel.value)?.focus()\n } else {\n document.body.style.overflow = ''\n restoreTo?.focus()\n restoreTo = null\n }\n})\n\n// A dialog unmounted while open would otherwise leave the page unscrollable,\n// with nothing on screen to explain why.\nonBeforeUnmount(() => {\n if (open.value) document.body.style.overflow = ''\n})\n</script>\n\n<template>\n <Teleport to=\"body\">\n <Transition name=\"rk-modal\">\n <div\n v-if=\"open\"\n class=\"rk-modal-scrim\"\n @keydown=\"onKeydown\"\n @mousedown.self=\"dismissible && (open = false)\"\n >\n <div\n ref=\"panel\"\n class=\"rk-modal-panel\"\n :role=\"tone === 'alert' ? 'alertdialog' : 'dialog'\"\n aria-modal=\"true\"\n :aria-label=\"title\"\n tabindex=\"-1\"\n >\n <div class=\"rk-modal-head\">\n <h2 class=\"rk-modal-title\">{{ title }}</h2>\n\n <button\n v-if=\"dismissible\"\n type=\"button\"\n class=\"rk-modal-close\"\n :aria-label=\"closeLabel\"\n @click=\"open = false\"\n >\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M18 6 6 18M6 6l12 12\" stroke-linecap=\"round\" />\n </svg>\n </button>\n </div>\n\n <div class=\"rk-modal-body\"><slot /></div>\n\n <div v-if=\"$slots.actions\" class=\"rk-modal-actions\"><slot name=\"actions\" /></div>\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n\n<style scoped>\n.rk-modal-scrim {\n position: fixed;\n inset: 0;\n z-index: 50;\n display: grid;\n place-items: center;\n padding: 1.5rem;\n background: color-mix(in srgb, var(--color-ink) 70%, transparent);\n backdrop-filter: blur(4px);\n}\n\n.rk-modal-panel {\n width: 100%;\n max-width: 28rem;\n max-height: 85vh;\n overflow-y: auto;\n border-radius: var(--radius-card);\n background: var(--color-surface);\n padding: 1.5rem;\n box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.2);\n}\n\n.rk-modal-panel:focus {\n outline: none;\n}\n\n.rk-modal-head {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 1rem;\n}\n\n.rk-modal-title {\n font-size: 1.0625rem;\n font-weight: 600;\n color: var(--color-ink);\n}\n\n.rk-modal-close {\n display: grid;\n place-items: center;\n flex-shrink: 0;\n width: 2rem;\n height: 2rem;\n border-radius: 9999px;\n color: var(--color-ink-soft);\n transition: background-color 150ms;\n}\n\n.rk-modal-close:hover {\n background: var(--color-muted);\n}\n\n.rk-modal-close:focus-visible {\n outline: 2px solid var(--color-primary);\n outline-offset: 2px;\n}\n\n.rk-modal-close svg {\n width: 1rem;\n height: 1rem;\n}\n\n.rk-modal-body {\n margin-top: 0.75rem;\n color: var(--color-ink-soft);\n font-size: 0.875rem;\n line-height: 1.625;\n}\n\n.rk-modal-actions {\n margin-top: 1.5rem;\n display: flex;\n justify-content: flex-end;\n gap: 0.5rem;\n}\n\n.rk-modal-enter-active,\n.rk-modal-leave-active {\n transition: opacity 180ms ease;\n}\n\n.rk-modal-enter-active .rk-modal-panel,\n.rk-modal-leave-active .rk-modal-panel {\n transition:\n transform 180ms cubic-bezier(0.32, 0.72, 0, 1),\n opacity 180ms ease;\n}\n\n.rk-modal-enter-from,\n.rk-modal-leave-to {\n opacity: 0;\n}\n\n/* From nowhere, not from an edge: a scale is what separates this from a sheet. */\n.rk-modal-enter-from .rk-modal-panel,\n.rk-modal-leave-to .rk-modal-panel {\n transform: scale(0.96);\n opacity: 0;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .rk-modal-enter-active .rk-modal-panel,\n .rk-modal-leave-active .rk-modal-panel {\n transition: opacity 180ms ease;\n transform: none;\n }\n\n .rk-modal-enter-from .rk-modal-panel,\n .rk-modal-leave-to .rk-modal-panel {\n transform: none;\n }\n}\n</style>\n","<script setup lang=\"ts\" generic=\"K extends string\">\nimport { RouterLink } from 'vue-router'\n\nexport interface NavLinkItem<K extends string> {\n /** Identity, compared against `active`. */\n key: K\n /** Router destination. */\n to: string\n /** Already translated. */\n label: string\n}\n\n/**\n * The primary navigation of a wide site.\n *\n * The same contract as `TabBar` minus the icon, so moving an app between a\n * bottom bar and a top one is a change of component and not of data. That is\n * the whole reason the shapes match.\n *\n * A rule under the current section rather than a filled pill: at this size a\n * pill reads as a button, and these are not buttons — they are where you are.\n * The rule is a scaled element rather than a border so it can travel, and it is\n * `aria-hidden` because `aria-current` already says the same thing to anyone not\n * looking at it.\n */\nconst {\n items,\n active,\n label = '',\n} = defineProps<{\n items: readonly NavLinkItem<K>[]\n /** Which item is current. Usually derived from the route. */\n active?: K | undefined\n /** Accessible name for the navigation landmark. */\n label?: string | undefined\n}>()\n</script>\n\n<template>\n <nav class=\"rk-nav\" :aria-label=\"label || undefined\">\n <RouterLink\n v-for=\"item in items\"\n :key=\"item.key\"\n :to=\"item.to\"\n class=\"rk-nav-link\"\n :class=\"{ 'is-active': item.key === active }\"\n :aria-current=\"item.key === active ? 'page' : undefined\"\n >\n {{ item.label }}\n <span class=\"rk-nav-rule\" aria-hidden=\"true\" />\n </RouterLink>\n </nav>\n</template>\n\n<style scoped>\n/* `:where()` on purpose, and it is the whole fix for a real bug.\n A component that sets `display: flex` on its own root ties with a utility\n class like Tailwind's `hidden` on specificity, and the component's scoped\n stylesheet ships after the utilities -- so the nav won, and appeared on a\n phone on top of the call to action. `:where()` has zero specificity, so any\n class the caller puts on the root wins outright. The layout is a default\n here, not a rule. */\n:where(.rk-nav) {\n display: flex;\n align-items: center;\n gap: 0.25rem;\n}\n\n.rk-nav-link {\n position: relative;\n border-radius: var(--radius-cell);\n padding: 0.5rem 0.75rem;\n font-size: 0.875rem;\n color: var(--color-ink-soft);\n transition: color 150ms;\n}\n\n.rk-nav-link:hover {\n color: var(--color-ink);\n}\n\n.rk-nav-link:focus-visible {\n outline: 2px solid var(--color-primary);\n outline-offset: 2px;\n}\n\n.rk-nav-link.is-active {\n color: var(--color-ink);\n font-weight: 500;\n}\n\n.rk-nav-rule {\n position: absolute;\n inset-inline: 0.75rem;\n bottom: -1px;\n height: 2px;\n border-radius: 9999px;\n background: var(--color-primary);\n transform: scaleX(0);\n transform-origin: left;\n transition: transform 300ms;\n}\n\n.rk-nav-link.is-active .rk-nav-rule {\n transform: scaleX(1);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .rk-nav-rule {\n transition: none;\n }\n}\n</style>\n","<script setup lang=\"ts\" generic=\"K extends string\">\nimport { RouterLink } from 'vue-router'\n\nexport interface NavLinkItem<K extends string> {\n /** Identity, compared against `active`. */\n key: K\n /** Router destination. */\n to: string\n /** Already translated. */\n label: string\n}\n\n/**\n * The primary navigation of a wide site.\n *\n * The same contract as `TabBar` minus the icon, so moving an app between a\n * bottom bar and a top one is a change of component and not of data. That is\n * the whole reason the shapes match.\n *\n * A rule under the current section rather than a filled pill: at this size a\n * pill reads as a button, and these are not buttons — they are where you are.\n * The rule is a scaled element rather than a border so it can travel, and it is\n * `aria-hidden` because `aria-current` already says the same thing to anyone not\n * looking at it.\n */\nconst {\n items,\n active,\n label = '',\n} = defineProps<{\n items: readonly NavLinkItem<K>[]\n /** Which item is current. Usually derived from the route. */\n active?: K | undefined\n /** Accessible name for the navigation landmark. */\n label?: string | undefined\n}>()\n</script>\n\n<template>\n <nav class=\"rk-nav\" :aria-label=\"label || undefined\">\n <RouterLink\n v-for=\"item in items\"\n :key=\"item.key\"\n :to=\"item.to\"\n class=\"rk-nav-link\"\n :class=\"{ 'is-active': item.key === active }\"\n :aria-current=\"item.key === active ? 'page' : undefined\"\n >\n {{ item.label }}\n <span class=\"rk-nav-rule\" aria-hidden=\"true\" />\n </RouterLink>\n </nav>\n</template>\n\n<style scoped>\n/* `:where()` on purpose, and it is the whole fix for a real bug.\n A component that sets `display: flex` on its own root ties with a utility\n class like Tailwind's `hidden` on specificity, and the component's scoped\n stylesheet ships after the utilities -- so the nav won, and appeared on a\n phone on top of the call to action. `:where()` has zero specificity, so any\n class the caller puts on the root wins outright. The layout is a default\n here, not a rule. */\n:where(.rk-nav) {\n display: flex;\n align-items: center;\n gap: 0.25rem;\n}\n\n.rk-nav-link {\n position: relative;\n border-radius: var(--radius-cell);\n padding: 0.5rem 0.75rem;\n font-size: 0.875rem;\n color: var(--color-ink-soft);\n transition: color 150ms;\n}\n\n.rk-nav-link:hover {\n color: var(--color-ink);\n}\n\n.rk-nav-link:focus-visible {\n outline: 2px solid var(--color-primary);\n outline-offset: 2px;\n}\n\n.rk-nav-link.is-active {\n color: var(--color-ink);\n font-weight: 500;\n}\n\n.rk-nav-rule {\n position: absolute;\n inset-inline: 0.75rem;\n bottom: -1px;\n height: 2px;\n border-radius: 9999px;\n background: var(--color-primary);\n transform: scaleX(0);\n transform-origin: left;\n transition: transform 300ms;\n}\n\n.rk-nav-link.is-active .rk-nav-rule {\n transform: scaleX(1);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .rk-nav-rule {\n transition: none;\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoDA,MAAM,MAAM,MAAM;EAIlB,MAAM,OAAO,WAAgB,CAAC,CAAC;EAE/B,MAAM,UAAU,QAAW,KAAK,MAAM,SAAS,GAAG;EAElD,SAAS,OAAO,KAAQ;GACtB,IAAI,OAAO,GAAG,GACZ,KAAK,QAAQ,KAAK,MAAM,QAAQ,MAAM,MAAM,GAAG;QAE/C,KAAK,QAAQ,QAAA,WAAW,CAAC,GAAG,KAAK,OAAO,GAAG,IAAI,CAAC,GAAG;EAEvD;;GAIE,OAAA,UAAA,GAAA,mBAoCK,MApCL,cAoCK,EAnCH,UAAA,IAAA,GAAA,mBAkCK,UAAA,MAAA,WAlCc,QAAA,QAAR,SAAI;IAAf,OAAA,UAAA,GAAA,mBAkCK,MAAA;KAlCsB,KAAK,KAAK;KAAK,OAAM;IAC9C,GAAA,EAAA,UAAA,GAAA,YAoBY,wBAAA,IApBQ,QAAA,cAAY,GAAA,MAAA;KAC9B,SAAA,cAkBS,CAlBT,mBAkBS,UAAA;MAjBP,MAAK;MACL,OAAM;MACL,iBAAe,OAAO,KAAK,GAAG;MAC9B,iBAAa,GAAK,MAAA,GAAA,EAAG,GAAI,KAAK;MAC9B,UAAK,WAAE,OAAO,KAAK,GAAG;KAEvB,GAAA,CAAA,mBAAwD,QAAxD,cAAwD,gBAApB,KAAK,KAAK,GAAA,CAAA,GAI9C,mBAMO,QANP,cAMO,CALL,OAAA,OAAA,OAAA,KAAA,mBAAiC,QAAA,EAA3B,OAAM,mBAAkB,GAAA,MAAA,EAAA,IAC9B,mBAGE,QAAA,EAFA,OAAK,eAAA,CAAC,0CAAwC,EAAA,WACzB,OAAO,KAAK,GAAG,EAAA,CAAA,CAAA,EAAA,GAAA,MAAA,CAAA,CAAA,CAAA,CAAA,GAAA,GAAA,YAAA,CAAA,CAAA;;IAM5C,GAAA,IAAA,IAAA,mBAUM,OAAA;KATH,IAAE,GAAK,MAAA,GAAA,EAAG,GAAI,KAAK;KACpB,OAAK,eAAA,CAAC,sBAAoB,EAAA,WACL,OAAO,KAAK,GAAG,EAAA,CAAA,CAAA;IAEpC,GAAA,CAAA,mBAIM,OAJN,cAIM,CAHJ,mBAEM,OAAA,EAFD,OAAK,eAAA,CAAC,qBAAmB,EAAA,WAAsB,OAAO,KAAK,GAAG,EAAA,CAAA,CAAA,EAAA,GAAA,CACjE,WAA8C,KAAA,QAAA,WAAA;KAAjC;KAAO,MAAM,OAAO,KAAK,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AEjCrD,IAAM,YACJ;;;;;;;;;;;;;;;;;;;;;;EAdF,MAAM,OAAO,SAAoB,SAAA,YAAmB;EASpD,MAAM,QAAQ,IAAwB,IAAI;;EAE1C,IAAI,YAAgC;EAKpC,SAAS,YAA2B;GAClC,IAAI,CAAC,MAAM,OAAO,OAAO,CAAC;GAE1B,OAAO,MAAM,KAAK,MAAM,MAAM,iBAA8B,SAAS,CAAC;EACxE;EAEA,SAAS,UAAU,OAAsB;GACvC,IAAI,MAAM,QAAQ,YAAY,QAAA,aAAa;IACzC,MAAM,gBAAgB;IACtB,KAAK,QAAQ;IAEb;GACF;GAEA,IAAI,MAAM,QAAQ,OAAO;GAEzB,MAAM,QAAQ,UAAU;GACxB,IAAI,MAAM,WAAW,GAAG,OAAO,MAAM,eAAe;GAEpD,MAAM,QAAQ,MAAM;GACpB,MAAM,OAAO,MAAM,MAAM,SAAS;GAClC,MAAM,SAAS,SAAS;GAIxB,IAAI,MAAM,aAAa,WAAW,SAAS,CAAC,MAAM,OAAO,SAAS,MAAM,IAAI;IAC1E,MAAM,eAAe;IACrB,KAAK,MAAM;GACb,OAAO,IAAI,CAAC,MAAM,YAAY,WAAW,MAAM;IAC7C,MAAM,eAAe;IACrB,MAAM,MAAM;GACd;EACF;EAEA,MAAM,MAAM,OAAO,WAAW;GAC5B,IAAI,QAAQ;IACV,YAAY,SAAS;IACrB,SAAS,KAAK,MAAM,WAAW;IAC/B,MAAM,SAAS;IAGd,CAAC,UAAU,CAAC,CAAC,MAAM,MAAM,MAAA,EAAQ,MAAM;GAC1C,OAAO;IACL,SAAS,KAAK,MAAM,WAAW;IAC/B,WAAW,MAAM;IACjB,YAAY;GACd;EACF,CAAC;EAID,sBAAsB;GACpB,IAAI,KAAK,OAAO,SAAS,KAAK,MAAM,WAAW;EACjD,CAAC;;GAIC,OAAA,UAAA,GAAA,YAsCW,UAAA,EAtCD,IAAG,OAAM,GAAA,CACjB,YAoCa,YAAA,EApCD,MAAK,WAAU,GAAA;IACzB,SAAA,cAkCM,CAjCE,KAAA,SADR,UAAA,GAAA,mBAkCM,OAAA;;KAhCJ,OAAM;KACI;KACT,aAAS,OAAA,OAAA,OAAA,KAAA,eAAA,WAAO,QAAA,gBAAgB,KAAA,QAAI,QAAA,CAAA,MAAA,CAAA;IAErC,GAAA,CAAA,mBA2BM,OAAA;KA1BA,SAAA;KAAJ,KAAI;KACJ,OAAM;KACL,MAAM,QAAA,SAAI,UAAA,gBAAA;KACX,cAAW;KACV,cAAY,QAAA;KACb,UAAS;;KAET,mBAcM,OAdN,YAcM,CAbJ,mBAA2C,MAA3C,YAA2C,gBAAb,QAAA,KAAK,GAAA,CAAA,GAG3B,QAAA,eADR,UAAA,GAAA,mBAUS,UAAA;;MARP,MAAK;MACL,OAAM;MACL,cAAY,QAAA;MACZ,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAA,QAAI;KAEZ,GAAA,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CAAA,mBAEM,OAAA;MAFD,SAAQ;MAAY,MAAK;MAAO,QAAO;MAAe,gBAAa;KACtE,GAAA,CAAA,mBAAwD,QAAA;MAAlD,GAAE;MAAuB,kBAAe;;KAKpD,mBAAyC,OAAzC,YAAyC,CAAd,WAAQ,KAAA,QAAA,WAAA,CAAA,GAAA,KAAA,GAAA,IAAA,CAAA,CAAA;KAExBA,KAAAA,OAAO,WAAlB,UAAA,GAAA,mBAAiF,OAAjF,YAAiF,CAA7B,WAAuB,KAAA,QAAA,WAAA,CAAA,GAAA,KAAA,GAAA,IAAA,CAAA,CAAA,KAAA,mBAAA,IAAA,IAAA;;;;;;;;;;;;;;;;;;;;;GE3HnF,OAAA,UAAA,GAAA,mBAYM,OAAA;IAZD,OAAM;IAAU,cAAY,QAAA,SAAS,KAAA;GACxC,GAAA,EAAA,UAAA,IAAA,GAAA,mBAUa,UAAA,MAAA,WATI,QAAA,QAAR,SAAI;IADb,OAAA,UAAA,GAAA,YAUa,MAAA,UAAA,GAAA;KARV,KAAK,KAAK;KACV,IAAI,KAAK;KACV,OAAK,eAAA,CAAC,eAAa,EAAA,aACI,KAAK,QAAQ,QAAA,OAAM,CAAA,CAAA;KACzC,gBAAc,KAAK,QAAQ,QAAA,SAAM,SAAY,KAAA;;KAE9C,SAAA,cAAgB,CAAb,gBAAA,gBAAA,KAAK,KAAK,IAAG,KAChB,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,mBAA+C,QAAA;MAAzC,OAAM;MAAc,eAAY"}
|
|
1
|
+
{"version":3,"file":"web.js","names":["$slots"],"sources":["../src/web/BaseAccordion.vue","../src/web/BaseAccordion.vue","../src/web/BaseModal.vue","../src/web/BaseModal.vue","../src/web/NavLinks.vue","../src/web/NavLinks.vue"],"sourcesContent":["<script setup lang=\"ts\" generic=\"K extends string\">\nimport { shallowRef, useId } from 'vue'\n\nexport interface AccordionItem<K extends string> {\n key: K\n /** The question. Already translated. */\n title: string\n}\n\n/**\n * A list that opens one section at a time.\n *\n * ## Why not `<details>`\n *\n * `<details>` is the honest element for this and it was the first choice. A\n * browser removes its content from layout entirely when closed, so there is\n * nothing to animate between — the panel can only appear. Faking it by leaving\n * `open` set and hiding with CSS tells a screen reader the section is expanded\n * while a sighted reader sees it shut, which is worse than no animation.\n *\n * So: a button carrying `aria-expanded`, the answer always in the markup — the\n * prerendered HTML carries every answer, which is what a crawler and a reader\n * without JavaScript get — and a grid row that travels from `0fr` to `1fr`,\n * which is the one way to animate to a height nobody has measured.\n *\n * The content is always present, so anything expensive in it is paid for on\n * first paint. That is the trade: a section a crawler can read costs a section\n * that is never lazy.\n */\nconst {\n items,\n multiple = false,\n headingLevel = 3,\n} = defineProps<{\n items: readonly AccordionItem<K>[]\n /** Allow several open at once. Off by default, which is what makes it an accordion. */\n multiple?: boolean | undefined\n /**\n * Which heading the control sits inside.\n *\n * A heading, always — it is how a screen reader user moves between sections\n * without opening any of them. The level is a prop because it depends on what\n * is above it on the page, and getting it wrong breaks the outline.\n */\n headingLevel?: 2 | 3 | 4 | undefined\n}>()\n\ndefineSlots<{\n /** The answer, per item. */\n default: (props: { item: AccordionItem<K>; open: boolean }) => unknown\n}>()\n\nconst uid = useId()\n/* `shallowRef` because the list is replaced wholesale rather than mutated, and\n because a deep ref unwraps a generic key type into something TypeScript can\n no longer match against `K`. */\nconst open = shallowRef<K[]>([])\n\nconst isOpen = (key: K) => open.value.includes(key)\n\nfunction toggle(key: K) {\n if (isOpen(key)) {\n open.value = open.value.filter((k) => k !== key)\n } else {\n open.value = multiple ? [...open.value, key] : [key]\n }\n}\n</script>\n\n<template>\n <ul class=\"rk-accordion\">\n <li v-for=\"item in items\" :key=\"item.key\" class=\"rk-accordion-item\">\n <component :is=\"`h${headingLevel}`\">\n <button\n type=\"button\"\n class=\"rk-accordion-control\"\n :aria-expanded=\"isOpen(item.key)\"\n :aria-controls=\"`${uid}-${item.key}`\"\n @click=\"toggle(item.key)\"\n >\n <span class=\"rk-accordion-title\">{{ item.title }}</span>\n\n <!-- A plus that becomes a minus: one stroke turns, so the control\n keeps its place while it changes meaning. -->\n <span class=\"rk-accordion-mark\" aria-hidden=\"true\">\n <span class=\"rk-accordion-bar\" />\n <span\n class=\"rk-accordion-bar rk-accordion-bar-turn\"\n :class=\"{ 'is-open': isOpen(item.key) }\"\n />\n </span>\n </button>\n </component>\n\n <div\n :id=\"`${uid}-${item.key}`\"\n class=\"rk-accordion-panel\"\n :class=\"{ 'is-open': isOpen(item.key) }\"\n >\n <div class=\"rk-accordion-clip\">\n <div class=\"rk-accordion-body\" :class=\"{ 'is-open': isOpen(item.key) }\">\n <slot :item=\"item\" :open=\"isOpen(item.key)\" />\n </div>\n </div>\n </div>\n </li>\n </ul>\n</template>\n\n<style scoped>\n.rk-accordion {\n border-top: 1px solid color-mix(in srgb, var(--color-hair) 70%, transparent);\n}\n\n.rk-accordion-item {\n border-bottom: 1px solid color-mix(in srgb, var(--color-hair) 70%, transparent);\n}\n\n.rk-accordion-control {\n display: flex;\n width: 100%;\n cursor: pointer;\n align-items: center;\n justify-content: space-between;\n gap: 1.5rem;\n padding: 1.25rem 0;\n text-align: left;\n transition: color 150ms;\n}\n\n.rk-accordion-control:hover {\n color: var(--color-primary);\n}\n\n.rk-accordion-control:focus-visible {\n outline: 2px solid var(--color-primary);\n outline-offset: 2px;\n}\n\n.rk-accordion-title {\n color: var(--color-ink);\n font-size: 1rem;\n font-weight: 500;\n}\n\n.rk-accordion-mark {\n position: relative;\n display: grid;\n place-items: center;\n flex-shrink: 0;\n width: 1.5rem;\n height: 1.5rem;\n}\n\n.rk-accordion-bar {\n position: absolute;\n height: 1px;\n width: 0.875rem;\n background: var(--color-ink-soft);\n}\n\n.rk-accordion-bar-turn {\n transform: rotate(90deg);\n transition: transform 420ms ease-out;\n}\n\n.rk-accordion-bar-turn.is-open {\n transform: rotate(0deg);\n}\n\n/* 0fr to 1fr is the whole trick: a grid row can be animated to a height that\n was never measured, which is what `height: auto` cannot do. */\n.rk-accordion-panel {\n display: grid;\n grid-template-rows: 0fr;\n transition: grid-template-rows 420ms ease-out;\n}\n\n.rk-accordion-panel.is-open {\n grid-template-rows: 1fr;\n}\n\n.rk-accordion-clip {\n overflow: hidden;\n}\n\n.rk-accordion-body {\n max-width: 68ch;\n padding-bottom: 1.5rem;\n color: var(--color-ink-soft);\n line-height: 1.625;\n opacity: 0;\n transition: opacity 300ms;\n}\n\n.rk-accordion-body.is-open {\n opacity: 1;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .rk-accordion-panel,\n .rk-accordion-bar-turn,\n .rk-accordion-body {\n transition: none;\n }\n}\n</style>\n","<script setup lang=\"ts\" generic=\"K extends string\">\nimport { shallowRef, useId } from 'vue'\n\nexport interface AccordionItem<K extends string> {\n key: K\n /** The question. Already translated. */\n title: string\n}\n\n/**\n * A list that opens one section at a time.\n *\n * ## Why not `<details>`\n *\n * `<details>` is the honest element for this and it was the first choice. A\n * browser removes its content from layout entirely when closed, so there is\n * nothing to animate between — the panel can only appear. Faking it by leaving\n * `open` set and hiding with CSS tells a screen reader the section is expanded\n * while a sighted reader sees it shut, which is worse than no animation.\n *\n * So: a button carrying `aria-expanded`, the answer always in the markup — the\n * prerendered HTML carries every answer, which is what a crawler and a reader\n * without JavaScript get — and a grid row that travels from `0fr` to `1fr`,\n * which is the one way to animate to a height nobody has measured.\n *\n * The content is always present, so anything expensive in it is paid for on\n * first paint. That is the trade: a section a crawler can read costs a section\n * that is never lazy.\n */\nconst {\n items,\n multiple = false,\n headingLevel = 3,\n} = defineProps<{\n items: readonly AccordionItem<K>[]\n /** Allow several open at once. Off by default, which is what makes it an accordion. */\n multiple?: boolean | undefined\n /**\n * Which heading the control sits inside.\n *\n * A heading, always — it is how a screen reader user moves between sections\n * without opening any of them. The level is a prop because it depends on what\n * is above it on the page, and getting it wrong breaks the outline.\n */\n headingLevel?: 2 | 3 | 4 | undefined\n}>()\n\ndefineSlots<{\n /** The answer, per item. */\n default: (props: { item: AccordionItem<K>; open: boolean }) => unknown\n}>()\n\nconst uid = useId()\n/* `shallowRef` because the list is replaced wholesale rather than mutated, and\n because a deep ref unwraps a generic key type into something TypeScript can\n no longer match against `K`. */\nconst open = shallowRef<K[]>([])\n\nconst isOpen = (key: K) => open.value.includes(key)\n\nfunction toggle(key: K) {\n if (isOpen(key)) {\n open.value = open.value.filter((k) => k !== key)\n } else {\n open.value = multiple ? [...open.value, key] : [key]\n }\n}\n</script>\n\n<template>\n <ul class=\"rk-accordion\">\n <li v-for=\"item in items\" :key=\"item.key\" class=\"rk-accordion-item\">\n <component :is=\"`h${headingLevel}`\">\n <button\n type=\"button\"\n class=\"rk-accordion-control\"\n :aria-expanded=\"isOpen(item.key)\"\n :aria-controls=\"`${uid}-${item.key}`\"\n @click=\"toggle(item.key)\"\n >\n <span class=\"rk-accordion-title\">{{ item.title }}</span>\n\n <!-- A plus that becomes a minus: one stroke turns, so the control\n keeps its place while it changes meaning. -->\n <span class=\"rk-accordion-mark\" aria-hidden=\"true\">\n <span class=\"rk-accordion-bar\" />\n <span\n class=\"rk-accordion-bar rk-accordion-bar-turn\"\n :class=\"{ 'is-open': isOpen(item.key) }\"\n />\n </span>\n </button>\n </component>\n\n <div\n :id=\"`${uid}-${item.key}`\"\n class=\"rk-accordion-panel\"\n :class=\"{ 'is-open': isOpen(item.key) }\"\n >\n <div class=\"rk-accordion-clip\">\n <div class=\"rk-accordion-body\" :class=\"{ 'is-open': isOpen(item.key) }\">\n <slot :item=\"item\" :open=\"isOpen(item.key)\" />\n </div>\n </div>\n </div>\n </li>\n </ul>\n</template>\n\n<style scoped>\n.rk-accordion {\n border-top: 1px solid color-mix(in srgb, var(--color-hair) 70%, transparent);\n}\n\n.rk-accordion-item {\n border-bottom: 1px solid color-mix(in srgb, var(--color-hair) 70%, transparent);\n}\n\n.rk-accordion-control {\n display: flex;\n width: 100%;\n cursor: pointer;\n align-items: center;\n justify-content: space-between;\n gap: 1.5rem;\n padding: 1.25rem 0;\n text-align: left;\n transition: color 150ms;\n}\n\n.rk-accordion-control:hover {\n color: var(--color-primary);\n}\n\n.rk-accordion-control:focus-visible {\n outline: 2px solid var(--color-primary);\n outline-offset: 2px;\n}\n\n.rk-accordion-title {\n color: var(--color-ink);\n font-size: 1rem;\n font-weight: 500;\n}\n\n.rk-accordion-mark {\n position: relative;\n display: grid;\n place-items: center;\n flex-shrink: 0;\n width: 1.5rem;\n height: 1.5rem;\n}\n\n.rk-accordion-bar {\n position: absolute;\n height: 1px;\n width: 0.875rem;\n background: var(--color-ink-soft);\n}\n\n.rk-accordion-bar-turn {\n transform: rotate(90deg);\n transition: transform 420ms ease-out;\n}\n\n.rk-accordion-bar-turn.is-open {\n transform: rotate(0deg);\n}\n\n/* 0fr to 1fr is the whole trick: a grid row can be animated to a height that\n was never measured, which is what `height: auto` cannot do. */\n.rk-accordion-panel {\n display: grid;\n grid-template-rows: 0fr;\n transition: grid-template-rows 420ms ease-out;\n}\n\n.rk-accordion-panel.is-open {\n grid-template-rows: 1fr;\n}\n\n.rk-accordion-clip {\n overflow: hidden;\n}\n\n.rk-accordion-body {\n max-width: 68ch;\n padding-bottom: 1.5rem;\n color: var(--color-ink-soft);\n line-height: 1.625;\n opacity: 0;\n transition: opacity 300ms;\n}\n\n.rk-accordion-body.is-open {\n opacity: 1;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .rk-accordion-panel,\n .rk-accordion-bar-turn,\n .rk-accordion-body {\n transition: none;\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { nextTick, onBeforeUnmount, ref, watch } from 'vue'\n\n/**\n * A dialog that arrives from nowhere.\n *\n * Not `BaseSheet` with different padding. A sheet slides up from the bottom\n * edge, is dismissed by dragging it back down, and belongs to a thumb; a modal\n * appears in the middle of what you were reading and is dismissed by leaving\n * it. They are two answers to two questions and merging them would give a\n * component that is wrong on a phone and wrong on a desktop.\n *\n * ## The parts that are easy to get wrong\n *\n * This is why it exists rather than being written per dialog, which is what the\n * one wide consumer was doing in two places. None of the following is visible\n * on screen when it is missing:\n *\n * - **Focus moves in and comes back.** Opening without moving focus leaves a\n * keyboard user behind the dialog, operating a page they cannot see. Closing\n * without restoring it drops them at the top of the document.\n * - **Tab does not leave.** A dialog you can Tab out of is a dialog that is\n * modal only to somebody using a mouse.\n * - **Escape closes.** Every dialog in every application does this.\n * - **The page underneath does not scroll.** Otherwise dismissing the dialog\n * returns you somewhere else.\n * - **The backdrop closes it, the panel does not.** A click that starts inside\n * the panel and ends on the backdrop is not a click on the backdrop, which is\n * why this listens for a press on the backdrop itself rather than any click\n * that reaches it.\n */\nconst {\n title,\n closeLabel,\n tone = 'default',\n dismissible = true,\n} = defineProps<{\n /** Names the dialog for assistive tech. Required; an unnamed dialog is a box. */\n title: string\n /** Accessible name for the close button. */\n closeLabel: string\n /**\n * `alert` marks it as a decision that interrupts — `role=\"alertdialog\"`,\n * which a screen reader announces immediately rather than on arrival.\n */\n tone?: 'default' | 'alert' | undefined\n /**\n * Whether Escape and the backdrop close it.\n *\n * Off for a dialog that must be answered — but never off as a way of forcing\n * a reader to a decision they have not been given the information for.\n */\n dismissible?: boolean | undefined\n}>()\n\nconst open = defineModel<boolean>({ default: false })\n\ndefineSlots<{\n /** The body. */\n default: () => unknown\n /** Buttons, at the foot. */\n actions?: () => unknown\n}>()\n\nconst panel = ref<HTMLElement | null>(null)\n/** Who had focus before this opened, so it can be given back. */\nlet restoreTo: HTMLElement | null = null\n\nconst FOCUSABLE =\n 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])'\n\nfunction focusable(): HTMLElement[] {\n if (!panel.value) return []\n\n return Array.from(panel.value.querySelectorAll<HTMLElement>(FOCUSABLE))\n}\n\nfunction onKeydown(event: KeyboardEvent) {\n if (event.key === 'Escape' && dismissible) {\n event.stopPropagation()\n open.value = false\n\n return\n }\n\n if (event.key !== 'Tab') return\n\n const items = focusable()\n if (items.length === 0) return event.preventDefault()\n\n const first = items[0]!\n const last = items[items.length - 1]!\n const active = document.activeElement\n\n // The wrap has to be done by hand: the browser's own Tab order is the whole\n // document, and the dialog is only part of it.\n if (event.shiftKey && (active === first || !panel.value?.contains(active))) {\n event.preventDefault()\n last.focus()\n } else if (!event.shiftKey && active === last) {\n event.preventDefault()\n first.focus()\n }\n}\n\nwatch(open, async (isOpen) => {\n if (isOpen) {\n restoreTo = document.activeElement as HTMLElement | null\n document.body.style.overflow = 'hidden'\n await nextTick()\n // The panel itself when it holds nothing focusable, so focus is at least\n // inside the dialog rather than behind it.\n ;(focusable()[0] ?? panel.value)?.focus()\n } else {\n document.body.style.overflow = ''\n restoreTo?.focus()\n restoreTo = null\n }\n})\n\n// A dialog unmounted while open would otherwise leave the page unscrollable,\n// with nothing on screen to explain why.\nonBeforeUnmount(() => {\n if (open.value) document.body.style.overflow = ''\n})\n</script>\n\n<template>\n <Teleport to=\"body\">\n <Transition name=\"rk-modal\">\n <div\n v-if=\"open\"\n class=\"rk-modal-scrim\"\n @keydown=\"onKeydown\"\n @mousedown.self=\"dismissible && (open = false)\"\n >\n <div\n ref=\"panel\"\n class=\"rk-modal-panel\"\n :role=\"tone === 'alert' ? 'alertdialog' : 'dialog'\"\n aria-modal=\"true\"\n :aria-label=\"title\"\n tabindex=\"-1\"\n >\n <div class=\"rk-modal-head\">\n <h2 class=\"rk-modal-title\">{{ title }}</h2>\n\n <button\n v-if=\"dismissible\"\n type=\"button\"\n class=\"rk-modal-close\"\n :aria-label=\"closeLabel\"\n @click=\"open = false\"\n >\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M18 6 6 18M6 6l12 12\" stroke-linecap=\"round\" />\n </svg>\n </button>\n </div>\n\n <div class=\"rk-modal-body\"><slot /></div>\n\n <div v-if=\"$slots.actions\" class=\"rk-modal-actions\"><slot name=\"actions\" /></div>\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n\n<style scoped>\n.rk-modal-scrim {\n position: fixed;\n inset: 0;\n z-index: 50;\n display: grid;\n place-items: center;\n padding: 1.5rem;\n background: color-mix(in srgb, var(--color-ink) 70%, transparent);\n backdrop-filter: blur(4px);\n}\n\n.rk-modal-panel {\n width: 100%;\n max-width: 28rem;\n max-height: 85vh;\n overflow-y: auto;\n border-radius: var(--radius-card);\n background: var(--color-surface);\n padding: 1.5rem;\n box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.2);\n}\n\n.rk-modal-panel:focus {\n outline: none;\n}\n\n.rk-modal-head {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 1rem;\n}\n\n.rk-modal-title {\n font-size: 1.0625rem;\n font-weight: 600;\n color: var(--color-ink);\n}\n\n.rk-modal-close {\n display: grid;\n place-items: center;\n flex-shrink: 0;\n width: 2rem;\n height: 2rem;\n border-radius: 9999px;\n color: var(--color-ink-soft);\n transition: background-color 150ms;\n}\n\n.rk-modal-close:hover {\n background: var(--color-muted);\n}\n\n.rk-modal-close:focus-visible {\n outline: 2px solid var(--color-primary);\n outline-offset: 2px;\n}\n\n.rk-modal-close svg {\n width: 1rem;\n height: 1rem;\n}\n\n.rk-modal-body {\n margin-top: 0.75rem;\n color: var(--color-ink-soft);\n font-size: 0.875rem;\n line-height: 1.625;\n}\n\n.rk-modal-actions {\n margin-top: 1.5rem;\n display: flex;\n justify-content: flex-end;\n gap: 0.5rem;\n}\n\n.rk-modal-enter-active,\n.rk-modal-leave-active {\n transition: opacity 180ms ease;\n}\n\n.rk-modal-enter-active .rk-modal-panel,\n.rk-modal-leave-active .rk-modal-panel {\n transition:\n transform 180ms cubic-bezier(0.32, 0.72, 0, 1),\n opacity 180ms ease;\n}\n\n.rk-modal-enter-from,\n.rk-modal-leave-to {\n opacity: 0;\n}\n\n/* From nowhere, not from an edge: a scale is what separates this from a sheet. */\n.rk-modal-enter-from .rk-modal-panel,\n.rk-modal-leave-to .rk-modal-panel {\n transform: scale(0.96);\n opacity: 0;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .rk-modal-enter-active .rk-modal-panel,\n .rk-modal-leave-active .rk-modal-panel {\n transition: opacity 180ms ease;\n transform: none;\n }\n\n .rk-modal-enter-from .rk-modal-panel,\n .rk-modal-leave-to .rk-modal-panel {\n transform: none;\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { nextTick, onBeforeUnmount, ref, watch } from 'vue'\n\n/**\n * A dialog that arrives from nowhere.\n *\n * Not `BaseSheet` with different padding. A sheet slides up from the bottom\n * edge, is dismissed by dragging it back down, and belongs to a thumb; a modal\n * appears in the middle of what you were reading and is dismissed by leaving\n * it. They are two answers to two questions and merging them would give a\n * component that is wrong on a phone and wrong on a desktop.\n *\n * ## The parts that are easy to get wrong\n *\n * This is why it exists rather than being written per dialog, which is what the\n * one wide consumer was doing in two places. None of the following is visible\n * on screen when it is missing:\n *\n * - **Focus moves in and comes back.** Opening without moving focus leaves a\n * keyboard user behind the dialog, operating a page they cannot see. Closing\n * without restoring it drops them at the top of the document.\n * - **Tab does not leave.** A dialog you can Tab out of is a dialog that is\n * modal only to somebody using a mouse.\n * - **Escape closes.** Every dialog in every application does this.\n * - **The page underneath does not scroll.** Otherwise dismissing the dialog\n * returns you somewhere else.\n * - **The backdrop closes it, the panel does not.** A click that starts inside\n * the panel and ends on the backdrop is not a click on the backdrop, which is\n * why this listens for a press on the backdrop itself rather than any click\n * that reaches it.\n */\nconst {\n title,\n closeLabel,\n tone = 'default',\n dismissible = true,\n} = defineProps<{\n /** Names the dialog for assistive tech. Required; an unnamed dialog is a box. */\n title: string\n /** Accessible name for the close button. */\n closeLabel: string\n /**\n * `alert` marks it as a decision that interrupts — `role=\"alertdialog\"`,\n * which a screen reader announces immediately rather than on arrival.\n */\n tone?: 'default' | 'alert' | undefined\n /**\n * Whether Escape and the backdrop close it.\n *\n * Off for a dialog that must be answered — but never off as a way of forcing\n * a reader to a decision they have not been given the information for.\n */\n dismissible?: boolean | undefined\n}>()\n\nconst open = defineModel<boolean>({ default: false })\n\ndefineSlots<{\n /** The body. */\n default: () => unknown\n /** Buttons, at the foot. */\n actions?: () => unknown\n}>()\n\nconst panel = ref<HTMLElement | null>(null)\n/** Who had focus before this opened, so it can be given back. */\nlet restoreTo: HTMLElement | null = null\n\nconst FOCUSABLE =\n 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])'\n\nfunction focusable(): HTMLElement[] {\n if (!panel.value) return []\n\n return Array.from(panel.value.querySelectorAll<HTMLElement>(FOCUSABLE))\n}\n\nfunction onKeydown(event: KeyboardEvent) {\n if (event.key === 'Escape' && dismissible) {\n event.stopPropagation()\n open.value = false\n\n return\n }\n\n if (event.key !== 'Tab') return\n\n const items = focusable()\n if (items.length === 0) return event.preventDefault()\n\n const first = items[0]!\n const last = items[items.length - 1]!\n const active = document.activeElement\n\n // The wrap has to be done by hand: the browser's own Tab order is the whole\n // document, and the dialog is only part of it.\n if (event.shiftKey && (active === first || !panel.value?.contains(active))) {\n event.preventDefault()\n last.focus()\n } else if (!event.shiftKey && active === last) {\n event.preventDefault()\n first.focus()\n }\n}\n\nwatch(open, async (isOpen) => {\n if (isOpen) {\n restoreTo = document.activeElement as HTMLElement | null\n document.body.style.overflow = 'hidden'\n await nextTick()\n // The panel itself when it holds nothing focusable, so focus is at least\n // inside the dialog rather than behind it.\n ;(focusable()[0] ?? panel.value)?.focus()\n } else {\n document.body.style.overflow = ''\n restoreTo?.focus()\n restoreTo = null\n }\n})\n\n// A dialog unmounted while open would otherwise leave the page unscrollable,\n// with nothing on screen to explain why.\nonBeforeUnmount(() => {\n if (open.value) document.body.style.overflow = ''\n})\n</script>\n\n<template>\n <Teleport to=\"body\">\n <Transition name=\"rk-modal\">\n <div\n v-if=\"open\"\n class=\"rk-modal-scrim\"\n @keydown=\"onKeydown\"\n @mousedown.self=\"dismissible && (open = false)\"\n >\n <div\n ref=\"panel\"\n class=\"rk-modal-panel\"\n :role=\"tone === 'alert' ? 'alertdialog' : 'dialog'\"\n aria-modal=\"true\"\n :aria-label=\"title\"\n tabindex=\"-1\"\n >\n <div class=\"rk-modal-head\">\n <h2 class=\"rk-modal-title\">{{ title }}</h2>\n\n <button\n v-if=\"dismissible\"\n type=\"button\"\n class=\"rk-modal-close\"\n :aria-label=\"closeLabel\"\n @click=\"open = false\"\n >\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M18 6 6 18M6 6l12 12\" stroke-linecap=\"round\" />\n </svg>\n </button>\n </div>\n\n <div class=\"rk-modal-body\"><slot /></div>\n\n <div v-if=\"$slots.actions\" class=\"rk-modal-actions\"><slot name=\"actions\" /></div>\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n\n<style scoped>\n.rk-modal-scrim {\n position: fixed;\n inset: 0;\n z-index: 50;\n display: grid;\n place-items: center;\n padding: 1.5rem;\n background: color-mix(in srgb, var(--color-ink) 70%, transparent);\n backdrop-filter: blur(4px);\n}\n\n.rk-modal-panel {\n width: 100%;\n max-width: 28rem;\n max-height: 85vh;\n overflow-y: auto;\n border-radius: var(--radius-card);\n background: var(--color-surface);\n padding: 1.5rem;\n box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.2);\n}\n\n.rk-modal-panel:focus {\n outline: none;\n}\n\n.rk-modal-head {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 1rem;\n}\n\n.rk-modal-title {\n font-size: 1.0625rem;\n font-weight: 600;\n color: var(--color-ink);\n}\n\n.rk-modal-close {\n display: grid;\n place-items: center;\n flex-shrink: 0;\n width: 2rem;\n height: 2rem;\n border-radius: 9999px;\n color: var(--color-ink-soft);\n transition: background-color 150ms;\n}\n\n.rk-modal-close:hover {\n background: var(--color-muted);\n}\n\n.rk-modal-close:focus-visible {\n outline: 2px solid var(--color-primary);\n outline-offset: 2px;\n}\n\n.rk-modal-close svg {\n width: 1rem;\n height: 1rem;\n}\n\n.rk-modal-body {\n margin-top: 0.75rem;\n color: var(--color-ink-soft);\n font-size: 0.875rem;\n line-height: 1.625;\n}\n\n.rk-modal-actions {\n margin-top: 1.5rem;\n display: flex;\n justify-content: flex-end;\n gap: 0.5rem;\n}\n\n.rk-modal-enter-active,\n.rk-modal-leave-active {\n transition: opacity 180ms ease;\n}\n\n.rk-modal-enter-active .rk-modal-panel,\n.rk-modal-leave-active .rk-modal-panel {\n transition:\n transform 180ms cubic-bezier(0.32, 0.72, 0, 1),\n opacity 180ms ease;\n}\n\n.rk-modal-enter-from,\n.rk-modal-leave-to {\n opacity: 0;\n}\n\n/* From nowhere, not from an edge: a scale is what separates this from a sheet. */\n.rk-modal-enter-from .rk-modal-panel,\n.rk-modal-leave-to .rk-modal-panel {\n transform: scale(0.96);\n opacity: 0;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .rk-modal-enter-active .rk-modal-panel,\n .rk-modal-leave-active .rk-modal-panel {\n transition: opacity 180ms ease;\n transform: none;\n }\n\n .rk-modal-enter-from .rk-modal-panel,\n .rk-modal-leave-to .rk-modal-panel {\n transform: none;\n }\n}\n</style>\n","<script setup lang=\"ts\" generic=\"K extends string\">\nimport { RouterLink } from 'vue-router'\n\nexport interface NavLinkItem<K extends string> {\n /** Identity, compared against `active`. */\n key: K\n /** Router destination. */\n to: string\n /** Already translated. */\n label: string\n}\n\n/**\n * The primary navigation of a wide site.\n *\n * The same contract as `TabBar` minus the icon, so moving an app between a\n * bottom bar and a top one is a change of component and not of data. That is\n * the whole reason the shapes match.\n *\n * A rule under the current section rather than a filled pill: at this size a\n * pill reads as a button, and these are not buttons — they are where you are.\n * The rule is a scaled element rather than a border so it can travel, and it is\n * `aria-hidden` because `aria-current` already says the same thing to anyone not\n * looking at it.\n */\nconst {\n items,\n active,\n label = '',\n} = defineProps<{\n items: readonly NavLinkItem<K>[]\n /** Which item is current. Usually derived from the route. */\n active?: K | undefined\n /** Accessible name for the navigation landmark. */\n label?: string | undefined\n}>()\n</script>\n\n<template>\n <nav :aria-label=\"label || undefined\">\n <div class=\"rk-nav-inner\">\n <RouterLink\n v-for=\"item in items\"\n :key=\"item.key\"\n :to=\"item.to\"\n class=\"rk-nav-link\"\n :class=\"{ 'is-active': item.key === active }\"\n :aria-current=\"item.key === active ? 'page' : undefined\"\n >\n {{ item.label }}\n <span class=\"rk-nav-rule\" aria-hidden=\"true\" />\n </RouterLink>\n </div>\n </nav>\n</template>\n\n<style scoped>\n/* The root carries no layout of its own, and that is the fix for a real bug\n rather than a style preference.\n\n A rule on the root loses to nothing: Vue's scoped CSS compiles `.rk-nav` to\n `.rk-nav[data-v-hash]`, which outranks a utility class like Tailwind's\n `hidden` outright — so `class=\"hidden sm:flex\"` did nothing and the nav\n appeared on a phone, on top of the call to action. `:where()` does not save\n it either; the scoping attribute puts the specificity back.\n\n So the flex row lives on an element the caller does not own, and the root is\n left for them to style. `TabBar` is built the same way for the same reason. */\n.rk-nav-inner {\n display: flex;\n align-items: center;\n gap: 0.25rem;\n}\n\n.rk-nav-link {\n position: relative;\n border-radius: var(--radius-cell);\n padding: 0.5rem 0.75rem;\n font-size: 0.875rem;\n color: var(--color-ink-soft);\n transition: color 150ms;\n}\n\n.rk-nav-link:hover {\n color: var(--color-ink);\n}\n\n.rk-nav-link:focus-visible {\n outline: 2px solid var(--color-primary);\n outline-offset: 2px;\n}\n\n.rk-nav-link.is-active {\n color: var(--color-ink);\n font-weight: 500;\n}\n\n.rk-nav-rule {\n position: absolute;\n inset-inline: 0.75rem;\n bottom: -1px;\n height: 2px;\n border-radius: 9999px;\n background: var(--color-primary);\n transform: scaleX(0);\n transform-origin: left;\n transition: transform 300ms;\n}\n\n.rk-nav-link.is-active .rk-nav-rule {\n transform: scaleX(1);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .rk-nav-rule {\n transition: none;\n }\n}\n</style>\n","<script setup lang=\"ts\" generic=\"K extends string\">\nimport { RouterLink } from 'vue-router'\n\nexport interface NavLinkItem<K extends string> {\n /** Identity, compared against `active`. */\n key: K\n /** Router destination. */\n to: string\n /** Already translated. */\n label: string\n}\n\n/**\n * The primary navigation of a wide site.\n *\n * The same contract as `TabBar` minus the icon, so moving an app between a\n * bottom bar and a top one is a change of component and not of data. That is\n * the whole reason the shapes match.\n *\n * A rule under the current section rather than a filled pill: at this size a\n * pill reads as a button, and these are not buttons — they are where you are.\n * The rule is a scaled element rather than a border so it can travel, and it is\n * `aria-hidden` because `aria-current` already says the same thing to anyone not\n * looking at it.\n */\nconst {\n items,\n active,\n label = '',\n} = defineProps<{\n items: readonly NavLinkItem<K>[]\n /** Which item is current. Usually derived from the route. */\n active?: K | undefined\n /** Accessible name for the navigation landmark. */\n label?: string | undefined\n}>()\n</script>\n\n<template>\n <nav :aria-label=\"label || undefined\">\n <div class=\"rk-nav-inner\">\n <RouterLink\n v-for=\"item in items\"\n :key=\"item.key\"\n :to=\"item.to\"\n class=\"rk-nav-link\"\n :class=\"{ 'is-active': item.key === active }\"\n :aria-current=\"item.key === active ? 'page' : undefined\"\n >\n {{ item.label }}\n <span class=\"rk-nav-rule\" aria-hidden=\"true\" />\n </RouterLink>\n </div>\n </nav>\n</template>\n\n<style scoped>\n/* The root carries no layout of its own, and that is the fix for a real bug\n rather than a style preference.\n\n A rule on the root loses to nothing: Vue's scoped CSS compiles `.rk-nav` to\n `.rk-nav[data-v-hash]`, which outranks a utility class like Tailwind's\n `hidden` outright — so `class=\"hidden sm:flex\"` did nothing and the nav\n appeared on a phone, on top of the call to action. `:where()` does not save\n it either; the scoping attribute puts the specificity back.\n\n So the flex row lives on an element the caller does not own, and the root is\n left for them to style. `TabBar` is built the same way for the same reason. */\n.rk-nav-inner {\n display: flex;\n align-items: center;\n gap: 0.25rem;\n}\n\n.rk-nav-link {\n position: relative;\n border-radius: var(--radius-cell);\n padding: 0.5rem 0.75rem;\n font-size: 0.875rem;\n color: var(--color-ink-soft);\n transition: color 150ms;\n}\n\n.rk-nav-link:hover {\n color: var(--color-ink);\n}\n\n.rk-nav-link:focus-visible {\n outline: 2px solid var(--color-primary);\n outline-offset: 2px;\n}\n\n.rk-nav-link.is-active {\n color: var(--color-ink);\n font-weight: 500;\n}\n\n.rk-nav-rule {\n position: absolute;\n inset-inline: 0.75rem;\n bottom: -1px;\n height: 2px;\n border-radius: 9999px;\n background: var(--color-primary);\n transform: scaleX(0);\n transform-origin: left;\n transition: transform 300ms;\n}\n\n.rk-nav-link.is-active .rk-nav-rule {\n transform: scaleX(1);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .rk-nav-rule {\n transition: none;\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoDA,MAAM,MAAM,MAAM;EAIlB,MAAM,OAAO,WAAgB,CAAC,CAAC;EAE/B,MAAM,UAAU,QAAW,KAAK,MAAM,SAAS,GAAG;EAElD,SAAS,OAAO,KAAQ;GACtB,IAAI,OAAO,GAAG,GACZ,KAAK,QAAQ,KAAK,MAAM,QAAQ,MAAM,MAAM,GAAG;QAE/C,KAAK,QAAQ,QAAA,WAAW,CAAC,GAAG,KAAK,OAAO,GAAG,IAAI,CAAC,GAAG;EAEvD;;GAIE,OAAA,UAAA,GAAA,mBAoCK,MApCL,cAoCK,EAnCH,UAAA,IAAA,GAAA,mBAkCK,UAAA,MAAA,WAlCc,QAAA,QAAR,SAAI;IAAf,OAAA,UAAA,GAAA,mBAkCK,MAAA;KAlCsB,KAAK,KAAK;KAAK,OAAM;IAC9C,GAAA,EAAA,UAAA,GAAA,YAoBY,wBAAA,IApBQ,QAAA,cAAY,GAAA,MAAA;KAC9B,SAAA,cAkBS,CAlBT,mBAkBS,UAAA;MAjBP,MAAK;MACL,OAAM;MACL,iBAAe,OAAO,KAAK,GAAG;MAC9B,iBAAa,GAAK,MAAA,GAAA,EAAG,GAAI,KAAK;MAC9B,UAAK,WAAE,OAAO,KAAK,GAAG;KAEvB,GAAA,CAAA,mBAAwD,QAAxD,cAAwD,gBAApB,KAAK,KAAK,GAAA,CAAA,GAI9C,mBAMO,QANP,cAMO,CALL,OAAA,OAAA,OAAA,KAAA,mBAAiC,QAAA,EAA3B,OAAM,mBAAkB,GAAA,MAAA,EAAA,IAC9B,mBAGE,QAAA,EAFA,OAAK,eAAA,CAAC,0CAAwC,EAAA,WACzB,OAAO,KAAK,GAAG,EAAA,CAAA,CAAA,EAAA,GAAA,MAAA,CAAA,CAAA,CAAA,CAAA,GAAA,GAAA,YAAA,CAAA,CAAA;;IAM5C,GAAA,IAAA,IAAA,mBAUM,OAAA;KATH,IAAE,GAAK,MAAA,GAAA,EAAG,GAAI,KAAK;KACpB,OAAK,eAAA,CAAC,sBAAoB,EAAA,WACL,OAAO,KAAK,GAAG,EAAA,CAAA,CAAA;IAEpC,GAAA,CAAA,mBAIM,OAJN,cAIM,CAHJ,mBAEM,OAAA,EAFD,OAAK,eAAA,CAAC,qBAAmB,EAAA,WAAsB,OAAO,KAAK,GAAG,EAAA,CAAA,CAAA,EAAA,GAAA,CACjE,WAA8C,KAAA,QAAA,WAAA;KAAjC;KAAO,MAAM,OAAO,KAAK,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AEjCrD,IAAM,YACJ;;;;;;;;;;;;;;;;;;;;;;EAdF,MAAM,OAAO,SAAoB,SAAA,YAAmB;EASpD,MAAM,QAAQ,IAAwB,IAAI;;EAE1C,IAAI,YAAgC;EAKpC,SAAS,YAA2B;GAClC,IAAI,CAAC,MAAM,OAAO,OAAO,CAAC;GAE1B,OAAO,MAAM,KAAK,MAAM,MAAM,iBAA8B,SAAS,CAAC;EACxE;EAEA,SAAS,UAAU,OAAsB;GACvC,IAAI,MAAM,QAAQ,YAAY,QAAA,aAAa;IACzC,MAAM,gBAAgB;IACtB,KAAK,QAAQ;IAEb;GACF;GAEA,IAAI,MAAM,QAAQ,OAAO;GAEzB,MAAM,QAAQ,UAAU;GACxB,IAAI,MAAM,WAAW,GAAG,OAAO,MAAM,eAAe;GAEpD,MAAM,QAAQ,MAAM;GACpB,MAAM,OAAO,MAAM,MAAM,SAAS;GAClC,MAAM,SAAS,SAAS;GAIxB,IAAI,MAAM,aAAa,WAAW,SAAS,CAAC,MAAM,OAAO,SAAS,MAAM,IAAI;IAC1E,MAAM,eAAe;IACrB,KAAK,MAAM;GACb,OAAO,IAAI,CAAC,MAAM,YAAY,WAAW,MAAM;IAC7C,MAAM,eAAe;IACrB,MAAM,MAAM;GACd;EACF;EAEA,MAAM,MAAM,OAAO,WAAW;GAC5B,IAAI,QAAQ;IACV,YAAY,SAAS;IACrB,SAAS,KAAK,MAAM,WAAW;IAC/B,MAAM,SAAS;IAGd,CAAC,UAAU,CAAC,CAAC,MAAM,MAAM,MAAA,EAAQ,MAAM;GAC1C,OAAO;IACL,SAAS,KAAK,MAAM,WAAW;IAC/B,WAAW,MAAM;IACjB,YAAY;GACd;EACF,CAAC;EAID,sBAAsB;GACpB,IAAI,KAAK,OAAO,SAAS,KAAK,MAAM,WAAW;EACjD,CAAC;;GAIC,OAAA,UAAA,GAAA,YAsCW,UAAA,EAtCD,IAAG,OAAM,GAAA,CACjB,YAoCa,YAAA,EApCD,MAAK,WAAU,GAAA;IACzB,SAAA,cAkCM,CAjCE,KAAA,SADR,UAAA,GAAA,mBAkCM,OAAA;;KAhCJ,OAAM;KACI;KACT,aAAS,OAAA,OAAA,OAAA,KAAA,eAAA,WAAO,QAAA,gBAAgB,KAAA,QAAI,QAAA,CAAA,MAAA,CAAA;IAErC,GAAA,CAAA,mBA2BM,OAAA;KA1BA,SAAA;KAAJ,KAAI;KACJ,OAAM;KACL,MAAM,QAAA,SAAI,UAAA,gBAAA;KACX,cAAW;KACV,cAAY,QAAA;KACb,UAAS;;KAET,mBAcM,OAdN,cAcM,CAbJ,mBAA2C,MAA3C,YAA2C,gBAAb,QAAA,KAAK,GAAA,CAAA,GAG3B,QAAA,eADR,UAAA,GAAA,mBAUS,UAAA;;MARP,MAAK;MACL,OAAM;MACL,cAAY,QAAA;MACZ,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAA,QAAI;KAEZ,GAAA,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CAAA,mBAEM,OAAA;MAFD,SAAQ;MAAY,MAAK;MAAO,QAAO;MAAe,gBAAa;KACtE,GAAA,CAAA,mBAAwD,QAAA;MAAlD,GAAE;MAAuB,kBAAe;;KAKpD,mBAAyC,OAAzC,YAAyC,CAAd,WAAQ,KAAA,QAAA,WAAA,CAAA,GAAA,KAAA,GAAA,IAAA,CAAA,CAAA;KAExBA,KAAAA,OAAO,WAAlB,UAAA,GAAA,mBAAiF,OAAjF,YAAiF,CAA7B,WAAuB,KAAA,QAAA,WAAA,CAAA,GAAA,KAAA,GAAA,IAAA,CAAA,CAAA,KAAA,mBAAA,IAAA,IAAA;;;;;;;;;;;;;;;;;;;;;;GE3HnF,OAAA,UAAA,GAAA,mBAcM,OAAA,EAdA,cAAY,QAAA,SAAS,KAAA,EAAA,GAAA,CACzB,mBAYM,OAZN,YAYM,EAXJ,UAAA,IAAA,GAAA,mBAUa,UAAA,MAAA,WATI,QAAA,QAAR,SAAI;IADb,OAAA,UAAA,GAAA,YAUa,MAAA,UAAA,GAAA;KARV,KAAK,KAAK;KACV,IAAI,KAAK;KACV,OAAK,eAAA,CAAC,eAAa,EAAA,aACI,KAAK,QAAQ,QAAA,OAAM,CAAA,CAAA;KACzC,gBAAc,KAAK,QAAQ,QAAA,SAAM,SAAY,KAAA;;KAE9C,SAAA,cAAgB,CAAb,gBAAA,gBAAA,KAAK,KAAK,IAAG,KAChB,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,mBAA+C,QAAA;MAAzC,OAAM;MAAc,eAAY"}
|