@0001-labs/design-system-1 0.3.0-alpha.19 → 0.3.0-alpha.21

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.
Files changed (45) hide show
  1. package/DS1/0-face/app-banner.ts +1 -1
  2. package/DS1/0-face/bento.ts +84 -0
  3. package/DS1/0-face/calendar.ts +2 -2
  4. package/DS1/1-root/bento.css +97 -0
  5. package/DS1/1-root/card.css +6 -1
  6. package/DS1/1-root/cjk.css +1 -1
  7. package/DS1/1-root/controls.css +1 -0
  8. package/DS1/1-root/fonts.css +1 -1
  9. package/DS1/1-root/one.css +1 -1
  10. package/DS1/1-root/one.semantic.css +1 -1
  11. package/DS1/3-unit/calendar-base.ts +31 -22
  12. package/DS1/3-unit/ds-bento.ts +67 -0
  13. package/DS1/3-unit/ds-calendar.ts +477 -22
  14. package/DS1/3-unit/styles/ds-bento.css +66 -0
  15. package/DS1/3-unit/styles/ds-calendar.css +119 -1
  16. package/DS1/index.ts +1 -1
  17. package/dist/0-face/app-banner.js +1 -1
  18. package/dist/0-face/bento.d.ts +41 -0
  19. package/dist/0-face/bento.d.ts.map +1 -0
  20. package/dist/0-face/bento.js +77 -0
  21. package/dist/0-face/calendar.js +2 -2
  22. package/dist/3-unit/calendar-base.d.ts +17 -12
  23. package/dist/3-unit/calendar-base.d.ts.map +1 -1
  24. package/dist/3-unit/calendar-base.js +21 -16
  25. package/dist/3-unit/ds-bento.d.ts +31 -0
  26. package/dist/3-unit/ds-bento.d.ts.map +1 -0
  27. package/dist/3-unit/ds-bento.js +47 -0
  28. package/dist/3-unit/ds-calendar-large.d.ts +3 -4
  29. package/dist/3-unit/ds-calendar-large.d.ts.map +1 -1
  30. package/dist/3-unit/ds-calendar.d.ts +139 -12
  31. package/dist/3-unit/ds-calendar.d.ts.map +1 -1
  32. package/dist/3-unit/ds-calendar.js +464 -23
  33. package/dist/design-system-1.bundle.js +1125 -1095
  34. package/dist/design-system-1.bundle.js.map +1 -1
  35. package/dist/design-system-1.bundle.min.js +2484 -2457
  36. package/dist/design-system-1.bundle.min.js.map +1 -1
  37. package/dist/index.d.ts +1 -1
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +1 -1
  40. package/package.json +1 -1
  41. package/DS1/3-unit/ds-date-picker.ts +0 -540
  42. package/DS1/3-unit/styles/ds-date-picker.css +0 -124
  43. package/dist/3-unit/ds-date-picker.d.ts +0 -203
  44. package/dist/3-unit/ds-date-picker.d.ts.map +0 -1
  45. package/dist/3-unit/ds-date-picker.js +0 -499
@@ -6644,7 +6644,7 @@ _ColorInput.styles = [r$3(component_metrics_default), i$4`
6644
6644
  define("ds-color", ColorInput);
6645
6645
  //#endregion
6646
6646
  //#region DS1/1-root/card.css?inline
6647
- var card_default = "/* Shape is a surface preference. Keep these base lengths unscaled: each\n * component resolves them with its own --ds-sf in component-metrics.css. */\n[data-ds-shape=\"square\"],\n:host([data-ds-shape=\"square\"]) {\n --ds-corner-size: 0px;\n --ds-corner-shape: round;\n --ds-button-inset: 0.5px;\n}\n\n[data-ds-shape=\"rounded\"],\n:host([data-ds-shape=\"rounded\"]) {\n --ds-corner-size: 4px;\n --ds-corner-shape: round;\n --ds-button-inset: 2.5px;\n}\n\n[data-ds-shape=\"circular\"],\n:host([data-ds-shape=\"circular\"]) {\n --ds-corner-size: 999px;\n --ds-corner-shape: round;\n --ds-button-inset: 5px;\n}\n\n/* A squircle needs twice an arc's radius for the same apparent corner.\n * Circular controls keep true semicircles even in a squircle browser. */\n@supports (corner-shape: squircle) {\n [data-ds-shape=\"rounded\"],\n :host([data-ds-shape=\"rounded\"]) {\n --ds-corner-size: 8px;\n --ds-corner-shape: squircle;\n }\n}\n\n/* Resolve dimensions where a component is drawn. Inherited calc() values have\n * already used their ancestor's scale. Base tokens stay unscaled and inherit;\n * resolved tokens can still be overridden directly on a component.\n * data-ds-surface gives ordinary layout elements the same scaled measures.\n */\n:root,\n:host,\n:where([data-ds-surface]) {\n --ds-unit: calc(var(--ds-1, 20px) * var(--ds-sf, 1));\n --ds-control-height: calc(var(--ds-control-size, var(--ds-08, 16px)) * var(--ds-sf, 1));\n --ds-row-height: calc(var(--ds-row-size, var(--ds-1, 20px)) * var(--ds-sf, 1));\n --ds-type-size-default: calc(var(--ds-text-size, 14px) * var(--ds-sf, 1));\n --ds-type-size-small: calc(var(--ds-small-text-size, 10px) * var(--ds-sf, 1));\n --ds-type-size-heading: calc(var(--ds-heading-text-size, 13px) * var(--ds-sf, 1));\n --ds-type-size-display: calc(var(--ds-display-text-size, 20px) * var(--ds-sf, 1));\n --ds-type-lineheight-default: calc(var(--ds-text-leading, 16px) * var(--ds-sf, 1));\n --ds-type-letterspacing-default: calc(var(--ds-text-tracking, -0.3px) * var(--ds-sf, 1));\n --ds-radius: calc(var(--ds-corner-size, 4px) * var(--ds-sf, 1));\n --ds-radius-card: calc(var(--ds-card-corner-size, 4px) * var(--ds-sf, 1));\n}\n\n/*\n * A card, as plain classes on native elements.\n *\n * A card holds one thing on a fill of its own: its name in the muted ink on\n * one square, a title in the display size on lines of a square and a half, writing,\n * and a picture of it (`ds-card__media`) a square below, which can run off\n * the card's foot and end edge and be cut there, never framed. Its size is\n * given in whole squares, its padding one square, its corners a card's\n * (`--ds-radius-card`). A card that opens something is one press target, an\n * `<a class=\"ds-card\" href>`: the pointer tints it, keyboard focus rings it\n * 2px inside and its picture rises a little. Ordinary writing still sits\n * straight on the paper; a card is for a thing shown among others, as in a\n * slide. The `ds-card` element draws the same parts.\n *\n * ds-card the card; `data-tone` names a palette colour\n * for its fill (the colour's pastel, dim in dark\n * mode, from palette.css, which one.css imports),\n * `ink` for the ink with the paper's words\n * ds-card--outlined a 1px line and no fill\n * ds-card__label its name, muted, on one square\n * ds-card__title the title, display size, a square and a half a line\n * ds-card__body writing, one square a line\n * ds-card__media the picture, a square below the words\n * ds-card__media--center standing in the card's middle\n * ds-card__media--bleed running on past the padding to the end edge\n *\n * Tokens: `--ds-card-width` and `--ds-card-height` (whole squares; the\n * content's size by default), `--ds-card-padding` (one square), and\n * `--ds-card-fill`, `--ds-card-ink` and `--ds-card-muted`.\n */\n\n.ds-card {\n --ds-card-fill: var(--ds-fill);\n --ds-card-ink: var(--ds-text-color);\n --ds-card-muted: var(--ds-text-color-muted);\n --ds-card-padding: var(--ds-unit);\n position: relative;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n align-items: stretch;\n width: var(--ds-card-width, auto);\n height: var(--ds-card-height, auto);\n min-width: 0;\n margin: 0;\n padding: var(--ds-card-padding) var(--ds-card-padding) 0;\n overflow: hidden;\n border-radius: var(--ds-radius-card);\n background: var(--ds-card-fill);\n color: var(--ds-card-ink);\n font-family: var(--ds-typeface-regular);\n font-size: var(--ds-type-size-default);\n text-decoration: none;\n -webkit-tap-highlight-color: transparent;\n}\n\n.ds-card[hidden] {\n display: none;\n}\n\n.ds-card--outlined {\n --ds-card-fill: transparent;\n box-shadow: inset 0 0 0 1px var(--ds-border-color);\n}\n\n.ds-card[data-tone=\"apple-green\"] { --ds-card-fill: var(--one-apple-green-pastel); }\n.ds-card[data-tone=\"chartreuse\"] { --ds-card-fill: var(--one-chartreuse-pastel); }\n.ds-card[data-tone=\"every-green\"] { --ds-card-fill: var(--one-every-green-pastel); }\n.ds-card[data-tone=\"yellow\"] { --ds-card-fill: var(--one-yellow-pastel); }\n.ds-card[data-tone=\"orange\"] { --ds-card-fill: var(--one-orange-pastel); }\n.ds-card[data-tone=\"tuned-red\"] { --ds-card-fill: var(--one-tuned-red-pastel); }\n.ds-card[data-tone=\"pink\"] { --ds-card-fill: var(--one-pink-pastel); }\n.ds-card[data-tone=\"sharp-blue\"] { --ds-card-fill: var(--one-sharp-blue-pastel); }\n.ds-card[data-tone=\"winning-blue\"] { --ds-card-fill: var(--one-winning-blue-pastel); }\n.ds-card[data-tone=\"hysteric-blue\"] { --ds-card-fill: var(--one-hysteric-blue-pastel); }\n.ds-card[data-tone=\"purple\"] { --ds-card-fill: var(--one-purple-pastel); }\n.ds-card[data-tone=\"metal-green\"] { --ds-card-fill: var(--one-metal-green-pastel); }\n.ds-card[data-tone=\"drip-yellow\"] { --ds-card-fill: var(--one-drip-yellow-pastel); }\n.ds-card[data-tone=\"sand\"] { --ds-card-fill: var(--one-sand-pastel); }\n.ds-card[data-tone=\"old-gold\"] { --ds-card-fill: var(--one-old-gold-pastel); }\n.ds-card[data-tone=\"red-for-blood\"] { --ds-card-fill: var(--one-red-for-blood-pastel); }\n.ds-card[data-tone=\"dark-red-variant\"] { --ds-card-fill: var(--one-dark-red-variant-pastel); }\n.ds-card[data-tone=\"signal-red\"] { --ds-card-fill: var(--one-signal-red-pastel); }\n.ds-card[data-tone=\"ink\"] {\n --ds-card-fill: var(--ds-black);\n --ds-card-ink: var(--ds-white);\n --ds-card-muted: color-mix(in srgb, var(--ds-white) 60%, transparent);\n box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ds-white) 8%, transparent);\n}\n\n/* Words: each line on the lower sixteen of its square. They keep their room\n in a card of a set height; only the picture is cut at the foot. */\n.ds-card__label,\n.ds-card__title,\n.ds-card__body {\n flex: none;\n}\n\n.ds-card__label {\n margin: 0;\n padding-top: calc(4px * var(--ds-sf, 1));\n color: var(--ds-card-muted);\n font: inherit;\n line-height: var(--ds-type-lineheight-default, calc(16px * var(--ds-sf, 1)));\n}\n\n.ds-card__title {\n margin: 0;\n color: var(--ds-card-ink);\n font-family: var(--ds-typeface-medium);\n font-size: var(--ds-type-size-display);\n font-weight: var(--ds-type-weight-bold);\n /* A title that wraps keeps its lines together: a square and a half each. */\n line-height: calc(1.5 * var(--ds-unit));\n letter-spacing: calc(var(--ds-display-text-tracking, -0.4px) * var(--ds-sf, 1));\n text-wrap: balance;\n}\n\n.ds-card__body {\n margin: 0;\n line-height: var(--ds-unit);\n}\n\n.ds-card__media {\n display: flex;\n flex: none;\n align-items: flex-start;\n justify-content: flex-start;\n min-width: 0;\n margin-top: var(--ds-unit);\n}\n\n.ds-card__media--center {\n justify-content: center;\n margin-inline: calc(-1 * var(--ds-card-padding));\n}\n\n.ds-card__media--bleed {\n margin-inline-end: calc(-1 * var(--ds-card-padding));\n}\n\n.ds-card__media > * {\n flex: none;\n}\n\n/* A card that opens something. */\na.ds-card,\n.ds-card[data-interactive] {\n cursor: pointer;\n}\n\na.ds-card::after,\n.ds-card[data-interactive]::after {\n content: \"\";\n position: absolute;\n inset: 0;\n border-radius: inherit;\n background: transparent;\n pointer-events: none;\n}\n\na.ds-card:focus-visible,\n.ds-card[data-interactive]:focus-visible {\n outline: 2px solid var(--ds-focus-ring);\n outline-offset: -2px;\n}\n\n@media (hover: hover) {\n a.ds-card:hover::after,\n .ds-card[data-interactive]:hover::after {\n background: color-mix(in srgb, var(--ds-card-ink) 4%, transparent);\n }\n}\n\na.ds-card:active::after,\n.ds-card[data-interactive]:active::after {\n background: color-mix(in srgb, var(--ds-card-ink) 8%, transparent);\n}\n\n@media (hover: hover) and (prefers-reduced-motion: no-preference) {\n a.ds-card > .ds-card__media,\n .ds-card[data-interactive] > .ds-card__media {\n transition: transform 240ms cubic-bezier(0.23, 1, 0.32, 1);\n }\n\n a.ds-card:hover > .ds-card__media,\n .ds-card[data-interactive]:hover > .ds-card__media {\n transform: translateY(calc(-0.25 * var(--ds-unit)));\n }\n}\n";
6647
+ var card_default = "/* Shape is a surface preference. Keep these base lengths unscaled: each\n * component resolves them with its own --ds-sf in component-metrics.css. */\n[data-ds-shape=\"square\"],\n:host([data-ds-shape=\"square\"]) {\n --ds-corner-size: 0px;\n --ds-corner-shape: round;\n --ds-button-inset: 0.5px;\n}\n\n[data-ds-shape=\"rounded\"],\n:host([data-ds-shape=\"rounded\"]) {\n --ds-corner-size: 4px;\n --ds-corner-shape: round;\n --ds-button-inset: 2.5px;\n}\n\n[data-ds-shape=\"circular\"],\n:host([data-ds-shape=\"circular\"]) {\n --ds-corner-size: 999px;\n --ds-corner-shape: round;\n --ds-button-inset: 5px;\n}\n\n/* A squircle needs twice an arc's radius for the same apparent corner.\n * Circular controls keep true semicircles even in a squircle browser. */\n@supports (corner-shape: squircle) {\n [data-ds-shape=\"rounded\"],\n :host([data-ds-shape=\"rounded\"]) {\n --ds-corner-size: 8px;\n --ds-corner-shape: squircle;\n }\n}\n\n/* Resolve dimensions where a component is drawn. Inherited calc() values have\n * already used their ancestor's scale. Base tokens stay unscaled and inherit;\n * resolved tokens can still be overridden directly on a component.\n * data-ds-surface gives ordinary layout elements the same scaled measures.\n */\n:root,\n:host,\n:where([data-ds-surface]) {\n --ds-unit: calc(var(--ds-1, 20px) * var(--ds-sf, 1));\n --ds-control-height: calc(var(--ds-control-size, var(--ds-08, 16px)) * var(--ds-sf, 1));\n --ds-row-height: calc(var(--ds-row-size, var(--ds-1, 20px)) * var(--ds-sf, 1));\n --ds-type-size-default: calc(var(--ds-text-size, 14px) * var(--ds-sf, 1));\n --ds-type-size-small: calc(var(--ds-small-text-size, 10px) * var(--ds-sf, 1));\n --ds-type-size-heading: calc(var(--ds-heading-text-size, 13px) * var(--ds-sf, 1));\n --ds-type-size-display: calc(var(--ds-display-text-size, 20px) * var(--ds-sf, 1));\n --ds-type-lineheight-default: calc(var(--ds-text-leading, 16px) * var(--ds-sf, 1));\n --ds-type-letterspacing-default: calc(var(--ds-text-tracking, -0.3px) * var(--ds-sf, 1));\n --ds-radius: calc(var(--ds-corner-size, 4px) * var(--ds-sf, 1));\n --ds-radius-card: calc(var(--ds-card-corner-size, 4px) * var(--ds-sf, 1));\n}\n\n/*\n * A card, as plain classes on native elements.\n *\n * A card holds one thing on a fill of its own: its name in the muted ink on\n * one square, a title in the display size on lines of a square and a half, writing,\n * and a picture of it (`ds-card__media`) a square below, which can run off\n * the card's foot and end edge and be cut there, never framed. Its size is\n * given in whole squares, its padding one square, its corners a card's\n * (`--ds-radius-card`). A card that opens something is one press target, an\n * `<a class=\"ds-card\" href>`: the pointer tints it, keyboard focus rings it\n * 2px inside and its picture rises a little. Ordinary writing still sits\n * straight on the paper; a card is for a thing shown among others, as in a\n * slide. The `ds-card` element draws the same parts.\n *\n * ds-card the card; `data-tone` names a palette colour\n * for its fill (the colour's pastel, dim in dark\n * mode, from palette.css, which one.css imports),\n * `ink` for the ink with the paper's words\n * ds-card--outlined a 1px line and no fill\n * ds-card__label its name, muted, on one square\n * ds-card__title the title, display size, a square and a half a line\n * ds-card__body writing, one square a line\n * ds-card__media the picture, a square below the words\n * ds-card__media--center standing in the card's middle\n * ds-card__media--bleed running on past the padding to the end edge\n *\n * Tokens: `--ds-card-width` and `--ds-card-height` (whole squares; the\n * content's size by default), `--ds-card-padding` (one square), and\n * `--ds-card-fill`, `--ds-card-ink` and `--ds-card-muted`.\n */\n\n.ds-card {\n --ds-card-fill: var(--ds-fill);\n --ds-card-ink: var(--ds-text-color);\n --ds-card-muted: var(--ds-text-color-muted);\n --ds-card-padding: var(--ds-unit);\n position: relative;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n align-items: stretch;\n width: var(--ds-card-width, auto);\n height: var(--ds-card-height, auto);\n min-width: 0;\n margin: 0;\n /* In a squares bento the surface reaches a quarter square towards its neighbours (`--ds-bento-inset-*`, 0 elsewhere), and the padding grows by as much so what it holds stays on the squares. */\n padding:\n calc(var(--ds-card-padding) + var(--ds-bento-inset-top, 0px))\n calc(var(--ds-card-padding) + var(--ds-bento-inset-end, 0px))\n var(--ds-bento-inset-bottom, 0px)\n calc(var(--ds-card-padding) + var(--ds-bento-inset-start, 0px));\n overflow: hidden;\n border-radius: var(--ds-radius-card);\n background: var(--ds-card-fill);\n color: var(--ds-card-ink);\n font-family: var(--ds-typeface-regular);\n font-size: var(--ds-type-size-default);\n text-decoration: none;\n -webkit-tap-highlight-color: transparent;\n}\n\n.ds-card[hidden] {\n display: none;\n}\n\n.ds-card--outlined {\n --ds-card-fill: transparent;\n box-shadow: inset 0 0 0 1px var(--ds-border-color);\n}\n\n.ds-card[data-tone=\"apple-green\"] { --ds-card-fill: var(--one-apple-green-pastel); }\n.ds-card[data-tone=\"chartreuse\"] { --ds-card-fill: var(--one-chartreuse-pastel); }\n.ds-card[data-tone=\"every-green\"] { --ds-card-fill: var(--one-every-green-pastel); }\n.ds-card[data-tone=\"yellow\"] { --ds-card-fill: var(--one-yellow-pastel); }\n.ds-card[data-tone=\"orange\"] { --ds-card-fill: var(--one-orange-pastel); }\n.ds-card[data-tone=\"tuned-red\"] { --ds-card-fill: var(--one-tuned-red-pastel); }\n.ds-card[data-tone=\"pink\"] { --ds-card-fill: var(--one-pink-pastel); }\n.ds-card[data-tone=\"sharp-blue\"] { --ds-card-fill: var(--one-sharp-blue-pastel); }\n.ds-card[data-tone=\"winning-blue\"] { --ds-card-fill: var(--one-winning-blue-pastel); }\n.ds-card[data-tone=\"hysteric-blue\"] { --ds-card-fill: var(--one-hysteric-blue-pastel); }\n.ds-card[data-tone=\"purple\"] { --ds-card-fill: var(--one-purple-pastel); }\n.ds-card[data-tone=\"metal-green\"] { --ds-card-fill: var(--one-metal-green-pastel); }\n.ds-card[data-tone=\"drip-yellow\"] { --ds-card-fill: var(--one-drip-yellow-pastel); }\n.ds-card[data-tone=\"sand\"] { --ds-card-fill: var(--one-sand-pastel); }\n.ds-card[data-tone=\"old-gold\"] { --ds-card-fill: var(--one-old-gold-pastel); }\n.ds-card[data-tone=\"red-for-blood\"] { --ds-card-fill: var(--one-red-for-blood-pastel); }\n.ds-card[data-tone=\"dark-red-variant\"] { --ds-card-fill: var(--one-dark-red-variant-pastel); }\n.ds-card[data-tone=\"signal-red\"] { --ds-card-fill: var(--one-signal-red-pastel); }\n.ds-card[data-tone=\"ink\"] {\n --ds-card-fill: var(--ds-black);\n --ds-card-ink: var(--ds-white);\n --ds-card-muted: color-mix(in srgb, var(--ds-white) 60%, transparent);\n box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ds-white) 8%, transparent);\n}\n\n/* Words: each line on the lower sixteen of its square. They keep their room\n in a card of a set height; only the picture is cut at the foot. */\n.ds-card__label,\n.ds-card__title,\n.ds-card__body {\n flex: none;\n}\n\n.ds-card__label {\n margin: 0;\n padding-top: calc(4px * var(--ds-sf, 1));\n color: var(--ds-card-muted);\n font: inherit;\n line-height: var(--ds-type-lineheight-default, calc(16px * var(--ds-sf, 1)));\n}\n\n.ds-card__title {\n margin: 0;\n color: var(--ds-card-ink);\n font-family: var(--ds-typeface-medium);\n font-size: var(--ds-type-size-display);\n font-weight: var(--ds-type-weight-bold);\n /* A title that wraps keeps its lines together: a square and a half each. */\n line-height: calc(1.5 * var(--ds-unit));\n letter-spacing: calc(var(--ds-display-text-tracking, -0.4px) * var(--ds-sf, 1));\n text-wrap: balance;\n}\n\n.ds-card__body {\n margin: 0;\n line-height: var(--ds-unit);\n}\n\n.ds-card__media {\n display: flex;\n flex: none;\n align-items: flex-start;\n justify-content: flex-start;\n min-width: 0;\n margin-top: var(--ds-unit);\n}\n\n.ds-card__media--center {\n justify-content: center;\n margin-inline: calc(-1 * var(--ds-card-padding));\n}\n\n.ds-card__media--bleed {\n margin-inline-end: calc(-1 * var(--ds-card-padding));\n}\n\n.ds-card__media > * {\n flex: none;\n}\n\n/* A card that opens something. */\na.ds-card,\n.ds-card[data-interactive] {\n cursor: pointer;\n}\n\na.ds-card::after,\n.ds-card[data-interactive]::after {\n content: \"\";\n position: absolute;\n inset: 0;\n border-radius: inherit;\n background: transparent;\n pointer-events: none;\n}\n\na.ds-card:focus-visible,\n.ds-card[data-interactive]:focus-visible {\n outline: 2px solid var(--ds-focus-ring);\n outline-offset: -2px;\n}\n\n@media (hover: hover) {\n a.ds-card:hover::after,\n .ds-card[data-interactive]:hover::after {\n background: color-mix(in srgb, var(--ds-card-ink) 4%, transparent);\n }\n}\n\na.ds-card:active::after,\n.ds-card[data-interactive]:active::after {\n background: color-mix(in srgb, var(--ds-card-ink) 8%, transparent);\n}\n\n@media (hover: hover) and (prefers-reduced-motion: no-preference) {\n a.ds-card > .ds-card__media,\n .ds-card[data-interactive] > .ds-card__media {\n transition: transform 240ms cubic-bezier(0.23, 1, 0.32, 1);\n }\n\n a.ds-card:hover > .ds-card__media,\n .ds-card[data-interactive]:hover > .ds-card__media {\n transform: translateY(calc(-0.25 * var(--ds-unit)));\n }\n}\n";
6648
6648
  //#endregion
6649
6649
  //#region DS1/2-core/styles/ds-card.css?inline
6650
6650
  var ds_card_default = "/* ds-card.css */\n/* The element places the card; 1-root/card.css draws it. The host takes the\n card's size, so a card is measured where it stands, in a slide too. */\n\n:host {\n display: flex;\n box-sizing: border-box;\n width: var(--ds-card-width, auto);\n height: var(--ds-card-height, auto);\n min-width: 0;\n}\n\n:host([hidden]) {\n display: none !important;\n}\n\n:host([disabled]) {\n opacity: 0.5;\n pointer-events: none;\n}\n\n.ds-card {\n flex: 1;\n width: auto;\n height: auto;\n}\n\n[hidden] {\n display: none !important;\n}\n\n::slotted([slot=\"media\"]) {\n flex: none;\n}\n";
@@ -8800,7 +8800,7 @@ function ensureSmartAppBanner(options) {
8800
8800
  const meta = document.createElement("meta");
8801
8801
  meta.name = "apple-itunes-app";
8802
8802
  meta.content = [`app-id=${options.appId}`, options.open ? `app-argument=${options.open}` : ""].filter(Boolean).join(", ");
8803
- document.head.append(meta);
8803
+ document.head.appendChild(meta);
8804
8804
  }
8805
8805
  /**
8806
8806
  * Opens the app: the deep link first, then the App Store when the page is
@@ -8828,7 +8828,7 @@ function openApp(options, wait = 1500) {
8828
8828
  }
8829
8829
  //#endregion
8830
8830
  //#region DS1/1-root/controls.css?inline
8831
- var controls_default = "/* Shape is a surface preference. Keep these base lengths unscaled: each\n * component resolves them with its own --ds-sf in component-metrics.css. */\n[data-ds-shape=\"square\"],\n:host([data-ds-shape=\"square\"]) {\n --ds-corner-size: 0px;\n --ds-corner-shape: round;\n --ds-button-inset: 0.5px;\n}\n\n[data-ds-shape=\"rounded\"],\n:host([data-ds-shape=\"rounded\"]) {\n --ds-corner-size: 4px;\n --ds-corner-shape: round;\n --ds-button-inset: 2.5px;\n}\n\n[data-ds-shape=\"circular\"],\n:host([data-ds-shape=\"circular\"]) {\n --ds-corner-size: 999px;\n --ds-corner-shape: round;\n --ds-button-inset: 5px;\n}\n\n/* A squircle needs twice an arc's radius for the same apparent corner.\n * Circular controls keep true semicircles even in a squircle browser. */\n@supports (corner-shape: squircle) {\n [data-ds-shape=\"rounded\"],\n :host([data-ds-shape=\"rounded\"]) {\n --ds-corner-size: 8px;\n --ds-corner-shape: squircle;\n }\n}\n\n/* Resolve dimensions where a component is drawn. Inherited calc() values have\n * already used their ancestor's scale. Base tokens stay unscaled and inherit;\n * resolved tokens can still be overridden directly on a component.\n * data-ds-surface gives ordinary layout elements the same scaled measures.\n */\n:root,\n:host,\n:where([data-ds-surface]) {\n --ds-unit: calc(var(--ds-1, 20px) * var(--ds-sf, 1));\n --ds-control-height: calc(var(--ds-control-size, var(--ds-08, 16px)) * var(--ds-sf, 1));\n --ds-row-height: calc(var(--ds-row-size, var(--ds-1, 20px)) * var(--ds-sf, 1));\n --ds-type-size-default: calc(var(--ds-text-size, 14px) * var(--ds-sf, 1));\n --ds-type-size-small: calc(var(--ds-small-text-size, 10px) * var(--ds-sf, 1));\n --ds-type-size-heading: calc(var(--ds-heading-text-size, 13px) * var(--ds-sf, 1));\n --ds-type-size-display: calc(var(--ds-display-text-size, 20px) * var(--ds-sf, 1));\n --ds-type-lineheight-default: calc(var(--ds-text-leading, 16px) * var(--ds-sf, 1));\n --ds-type-letterspacing-default: calc(var(--ds-text-tracking, -0.3px) * var(--ds-sf, 1));\n --ds-radius: calc(var(--ds-corner-size, 4px) * var(--ds-sf, 1));\n --ds-radius-card: calc(var(--ds-card-corner-size, 4px) * var(--ds-sf, 1));\n}\n/*\n * A table: words straight on the paper, one square a row. No box and no\n * column lines: each row stands on a hairline at its foot, its words set on\n * that line like a caption: the sixteen of words against the 1px line, three\n * spare above, a 20px pitch, as Ezo sets every row.\n * Column headers keep the text's weight in the title grey; the first column\n * of a body row is a row header in the text colour. Nothing is filled with\n * the accent except a cell someone has selected.\n *\n * `.ds-table` dresses a native <table>; `<ds-table>` draws the same table\n * from data. Put a wide table in `.ds-table-scroll` to scroll it sideways,\n * and give the table `data-sticky` to keep its first column in place.\n *\n * <th data-numeric>, <td data-numeric> numbers, set to the column's end\n * <tfoot> totals, under a line of their own\n * <th colspan=\"2\"> a column group, in a header row\n * <th scope=\"rowgroup\"> a row group, a heading row in grey\n * <td aria-selected=\"true\"> a selected cell\n *\n * `data-sheet` makes it a spreadsheet: every cell four squares by one (80 by\n * 20), its 1px lines at its end and foot so the words have 79 by 19, set 4px\n * in on the sixteen, as an Ezo item's are. Column letters and the row numbers\n * (`.ds-table__letters`, `.ds-table__number`, two squares wide) stay in place\n * while the sheet scrolls both ways inside `.ds-table-scroll`; give the table\n * `--ds-table-columns` (the number of data columns) and the scroll a height\n * with `--ds-table-height`.\n */\n\n.ds-table {\n --ds-table-row: var(--ds-row-height, calc(20px * var(--ds-sf, 1)));\n --ds-table-gap: calc(var(--ds-table-row) / 2);\n --ds-table-rule: var(--ds-border-color, light-dark(#d8d8d8, #3d3d3d));\n --ds-table-paper: var(--ds-background, Canvas);\n width: 100%;\n border-collapse: collapse;\n border-spacing: 0;\n color: var(--ds-text-color, inherit);\n font-family: var(--ds-typeface-regular, inherit);\n font-feature-settings: var(--ds-font-features, normal);\n font-size: var(--ds-type-size-default, 14px);\n letter-spacing: var(--ds-type-letterspacing-default, normal);\n}\n\n.ds-table :is(th, td) {\n box-sizing: border-box;\n height: var(--ds-table-row);\n padding: 0 var(--ds-table-gap) 0 0;\n border: 0;\n border-bottom: 1px solid var(--ds-table-rule);\n background: transparent;\n line-height: var(--ds-type-lineheight-default);\n text-align: start;\n vertical-align: bottom;\n font-weight: inherit;\n white-space: nowrap;\n}\n\n.ds-table :is(th, td):last-child {\n padding-inline-end: 0;\n}\n\n.ds-table thead th,\n.ds-table th[scope=\"rowgroup\"] {\n color: var(--ds-title-color, light-dark(#797979, #9a9a9a));\n}\n\n.ds-table tbody th:not([scope=\"rowgroup\"]) {\n color: inherit;\n}\n\n.ds-table [data-numeric] {\n text-align: end;\n font-variant-numeric: tabular-nums;\n}\n\n/* Totals: the rule above them is the last body row's; theirs closes the table. */\n.ds-table tfoot :is(th, td) {\n color: inherit;\n}\n\n.ds-table [aria-selected=\"true\"] {\n background: var(--ds-accent-color, Highlight);\n color: var(--ds-button-text-color-on-accent, inherit);\n}\n\n.ds-table :is(th, td):has(> :is(a, button):only-child) {\n cursor: pointer;\n}\n\n.ds-table caption {\n box-sizing: border-box;\n height: var(--ds-table-row);\n padding-top: calc(var(--ds-table-row) - var(--ds-type-lineheight-default));\n line-height: var(--ds-type-lineheight-default);\n text-align: start;\n color: var(--ds-title-color, light-dark(#797979, #9a9a9a));\n}\n\n.ds-table-scroll {\n max-width: 100%;\n overflow-x: auto;\n overscroll-behavior-x: contain;\n}\n\n.ds-table-scroll > .ds-table {\n width: max-content;\n min-width: 100%;\n}\n\n.ds-table[data-sticky] :is(th, td):first-child {\n position: sticky;\n inset-inline-start: 0;\n z-index: 1;\n background: var(--ds-table-paper);\n}\n\n.ds-table[data-sticky] [aria-selected=\"true\"]:first-child {\n background: var(--ds-accent-color, Highlight);\n}\n\n/* Keep the row whose details are open visible, including its sticky first cell.\n Cell selection remains independent and keeps its accent. */\n.ds-table tbody > tr[data-open]:not([data-open=\"false\"]) > :is(th, td):not([aria-selected=\"true\"]) {\n background: var(--ds-row-fill-current, var(--ds-fill-hover));\n}\n@media (forced-colors: active) {\n .ds-table tbody > tr[data-open]:not([data-open=\"false\"]) > :is(th, td):not([aria-selected=\"true\"]) {\n background: Highlight;\n color: HighlightText;\n }\n}\n\n/* A spreadsheet: cells four squares by one, on lines, lettered and numbered. */\n.ds-table[data-sheet] {\n --ds-table-cell: calc(4 * var(--ds-table-row));\n --ds-table-index: calc(2 * var(--ds-table-row));\n --ds-table-text: calc(4px * var(--ds-sf, 1));\n table-layout: fixed;\n width: calc(var(--ds-table-index) + var(--ds-table-columns, 8) * var(--ds-table-cell));\n min-width: 0;\n border-collapse: separate;\n border-spacing: 0;\n}\n.ds-table-scroll > .ds-table[data-sheet] {\n width: calc(var(--ds-table-index) + var(--ds-table-columns, 8) * var(--ds-table-cell));\n min-width: 0;\n}\n.ds-table[data-sheet] :is(th, td) {\n width: var(--ds-table-cell);\n max-width: var(--ds-table-cell);\n height: var(--ds-table-row);\n padding: 0 var(--ds-table-text);\n border-bottom: 1px solid var(--ds-table-rule);\n border-inline-end: 1px solid var(--ds-table-rule);\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ds-table[data-sheet] :is(th, td):last-child {\n padding-inline-end: var(--ds-table-text);\n}\n.ds-table[data-sheet] .ds-table__number {\n width: var(--ds-table-index);\n max-width: var(--ds-table-index);\n position: sticky;\n inset-inline-start: 0;\n z-index: 1;\n background: var(--ds-table-paper);\n color: var(--ds-title-color, light-dark(#797979, #9a9a9a));\n text-align: center;\n font-variant-numeric: tabular-nums;\n}\n.ds-table[data-sheet] thead :is(th, td) {\n position: sticky;\n top: 0;\n z-index: 2;\n background: var(--ds-table-paper);\n}\n.ds-table[data-sheet] thead tr + tr :is(th, td) {\n top: var(--ds-table-row);\n}\n.ds-table[data-sheet] thead .ds-table__number {\n z-index: 3;\n}\n.ds-table[data-sheet] .ds-table__letters td {\n color: var(--ds-title-color, light-dark(#797979, #9a9a9a));\n text-align: center;\n}\n.ds-table[data-sheet] tbody td:not([data-numeric]) {\n color: inherit;\n}\n.ds-table[data-sheet] [aria-selected=\"true\"] {\n background: transparent;\n color: inherit;\n box-shadow: inset 0 0 0 2px var(--ds-focus-ring, Highlight);\n}\n.ds-table-scroll:has(> .ds-table[data-sheet]) {\n max-height: var(--ds-table-height, none);\n overflow: auto;\n overscroll-behavior: contain;\n}\n/*\n * Text on the grid, as plain classes on native text.\n *\n * Interface copy on a page without Lit: a label, a message, a title. Each line\n * is one square (`--ds-unit`), so a paragraph of any length stands on whole\n * squares, and a display title takes two. The `ds-text` element draws the same\n * words when they come from a translation key.\n *\n * one.css and controls.css both import this file, so a page that loads either\n * of them, or both, has one definition of these classes.\n *\n * ds-text 14 on a one-square line, the text colour\n * ds-text--small 10\n * ds-text--heading 13, medium\n * ds-text--display 20 on two squares, medium\n * ds-text--strong medium\n * ds-text--muted, --title, --danger tones\n */\n\n.ds-text {\n margin: 0;\n color: var(--ds-text-color);\n font-family: var(--ds-typeface-regular);\n font-feature-settings: var(--ds-font-features);\n font-size: var(--ds-type-size-default);\n font-weight: var(--ds-type-weight-default);\n line-height: var(--ds-unit);\n letter-spacing: var(--ds-type-letterspacing-default);\n}\n\n.ds-text--small {\n font-size: var(--ds-type-size-small);\n}\n\n.ds-text--heading {\n font-family: var(--ds-typeface-medium);\n font-size: var(--ds-type-size-heading);\n font-weight: var(--ds-type-weight-bold);\n}\n\n.ds-text--display {\n font-family: var(--ds-typeface-medium);\n font-size: var(--ds-type-size-display);\n font-weight: var(--ds-type-weight-bold);\n line-height: calc(2 * var(--ds-unit));\n letter-spacing: calc(var(--ds-display-text-tracking, -0.4px) * var(--ds-sf, 1));\n}\n\n.ds-text--strong {\n font-family: var(--ds-typeface-medium);\n font-weight: var(--ds-type-weight-bold);\n}\n\n.ds-text--muted {\n color: var(--ds-text-color-muted);\n}\n\n.ds-text--title {\n color: var(--ds-title-color);\n}\n\n.ds-text--danger {\n color: var(--ds-danger-color);\n}\n\n.ds-text--warning {\n color: var(--ds-warning-color);\n}\n/*\n * An empty state, as plain classes on native elements.\n *\n * It says that a place has nothing in it yet and what to do about it: a title,\n * a line of text and, when there is one, the action that fills it. The words\n * sit on the paper with no box, no illustration and no colour; every line is\n * one square, so the whole stands on whole squares. The `ds-empty` element\n * draws the same parts from a translation key, with the shared words' \"Nothing\n * here yet\" as its title when none is given.\n *\n * ds-empty the block, at most sixteen squares wide\n * ds-empty__icon an icon from the library, on its own square, muted\n * ds-empty__title one line in the medium face\n * ds-empty__text lines of one square, muted\n * ds-empty__actions buttons or links on one row, one square below\n * ds-empty--centered centred in the space it stands in\n * ds-empty--done all done: the check on a mark in the success\n * green, the title in that green\n *\n * Tokens: `--ds-empty-measure` (the widest it grows, sixteen squares) and\n * `--ds-empty-padding` (the room above and below, none by default).\n */\n\n.ds-empty {\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n width: 100%;\n max-width: var(--ds-empty-measure, calc(16 * var(--ds-unit)));\n margin: 0;\n padding-block: var(--ds-empty-padding, 0);\n color: var(--ds-text-color);\n font-family: var(--ds-typeface-regular);\n font-feature-settings: var(--ds-font-features);\n font-size: var(--ds-type-size-default);\n font-weight: var(--ds-type-weight-default);\n line-height: var(--ds-unit);\n letter-spacing: var(--ds-type-letterspacing-default);\n text-transform: none;\n}\n\n.ds-empty__icon {\n display: flex;\n align-items: center;\n height: var(--ds-unit);\n color: var(--ds-text-color-muted);\n}\n\n.ds-empty__title,\n.ds-empty__text {\n margin: 0;\n font: inherit;\n letter-spacing: inherit;\n}\n\n.ds-empty__title {\n font-family: var(--ds-typeface-medium);\n font-weight: var(--ds-type-weight-bold);\n}\n\n.ds-empty__text {\n color: var(--ds-text-color-muted);\n}\n\n.ds-empty__actions {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 0 calc(var(--ds-unit) / 2);\n min-height: var(--ds-row-height);\n margin-top: var(--ds-unit);\n}\n\n.ds-empty--centered {\n align-items: center;\n margin-inline: auto;\n text-align: center;\n}\n\n.ds-empty--centered .ds-empty__actions {\n justify-content: center;\n}\n\n/* Done: a place empty because everything in it is finished (every receipt\n found, nothing left to review). The icon, the check by default, stands on a\n round mark one control high in the success green, on the sixteen, and the\n title takes the same green; the line under it stays muted. */\n.ds-empty--done .ds-empty__icon {\n box-sizing: border-box;\n justify-content: center;\n width: var(--ds-control-height);\n height: var(--ds-control-height);\n margin-top: calc(var(--ds-unit) - var(--ds-control-height));\n border-radius: 50%;\n background: var(--ds-success-color);\n color: light-dark(var(--ds-white), var(--ds-black));\n --ds-icon-color: currentColor;\n}\n\n.ds-empty--done .ds-empty__title {\n color: var(--ds-success-color);\n}\n/*\n * An inline notice, as plain classes on native elements.\n *\n * A notice stands in the flow of the page, where its words apply: a failure\n * beside the form that failed, a warning above the list it is about. It is not\n * positioned and takes no box or shadow. A square mark, one control high and\n * filled in the tone's colour, carries the tone; the words sit on the paper\n * beside it, each line one square, with the actions on the row below and the\n * dismiss button at the end of the first line. Danger turns the words into\n * the danger colour. The `ds-notice` element draws the same parts.\n *\n * ds-notice the block: mark, words, dismiss\n * ds-notice__mark the square mark; holds an icon in its ink\n * ds-notice__title one line in the medium face\n * ds-notice__text lines of one square\n * ds-notice__actions buttons or links on one row\n * ds-notice__dismiss an icon button at the end of the first line\n * ds-notice--success, --warning, --danger tones; neutral without one\n * ds-notice--banner a strip across the top of its positioning\n * context instead: one square high, filled in\n * the tone's colour, everything on one line\n * ds-notice--app a row at the top of the page leading to the\n * site's iPhone app: its picture\n * (ds-notice__picture, two squares), its name\n * and one line about it, Open\n * (a.ds-notice__open) and the close; three\n * squares high with a hairline at its foot.\n * startAppBanners() from 0-face/app-banner.ts\n * shows it only where it belongs\n *\n * Give the block role=\"status\", or role=\"alert\" for danger, so assistive\n * technology reads it when it appears.\n *\n * Tokens: `--ds-notice-mark`, `--ds-notice-mark-ink` and `--ds-notice-ink`,\n * which the tones set.\n */\n\n.ds-notice {\n --ds-notice-mark: var(--ds-fill);\n --ds-notice-mark-ink: var(--ds-text-color);\n --ds-notice-ink: var(--ds-text-color);\n box-sizing: border-box;\n display: grid;\n grid-template-columns: var(--ds-unit) minmax(0, 1fr) auto;\n align-items: start;\n margin: 0;\n color: var(--ds-notice-ink);\n font-family: var(--ds-typeface-regular);\n font-feature-settings: var(--ds-font-features);\n font-size: var(--ds-type-size-default);\n font-weight: var(--ds-type-weight-default);\n line-height: var(--ds-unit);\n letter-spacing: var(--ds-type-letterspacing-default);\n text-transform: none;\n}\n\n.ds-notice--success {\n --ds-notice-mark: var(--ds-apple-green);\n --ds-notice-mark-ink: var(--ds-black);\n}\n\n.ds-notice--warning {\n --ds-notice-mark: var(--ds-orange);\n --ds-notice-mark-ink: var(--ds-black);\n}\n\n.ds-notice--danger {\n --ds-notice-mark: var(--ds-danger-color);\n --ds-notice-mark-ink: light-dark(var(--ds-white), var(--ds-black));\n --ds-notice-ink: var(--ds-danger-color);\n}\n\n/* The mark stands on the first line, one control high in a one-square cell. */\n.ds-notice__mark {\n box-sizing: border-box;\n grid-column: 1;\n grid-row: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n width: var(--ds-control-height);\n height: var(--ds-control-height);\n margin-block: calc((var(--ds-unit) - var(--ds-control-height)) / 2);\n overflow: hidden;\n background: var(--ds-notice-mark);\n color: var(--ds-notice-mark-ink);\n --ds-icon-color: var(--ds-notice-mark-ink);\n}\n\n.ds-notice__title,\n.ds-notice__text,\n.ds-notice__actions {\n grid-column: 2;\n min-width: 0;\n margin: 0;\n}\n\n.ds-notice__title,\n.ds-notice__text {\n font: inherit;\n letter-spacing: inherit;\n overflow-wrap: anywhere;\n}\n\n.ds-notice__title {\n font-family: var(--ds-typeface-medium);\n font-weight: var(--ds-type-weight-bold);\n}\n\n.ds-notice__actions {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 0 calc(var(--ds-unit) / 2);\n min-height: var(--ds-row-height);\n}\n\n/* The dismiss button closes the first line: a one-square cell at the end.\n Two classes, so the button's own reset of its margin does not undo it. */\n.ds-notice .ds-notice__dismiss {\n grid-column: 3;\n grid-row: 1;\n margin-block: calc((var(--ds-unit) - var(--ds-control-height)) / 2);\n margin-inline-start: calc(var(--ds-unit) - var(--ds-control-height));\n}\n\n/* A banner: the same parts on one line, one square high, across the top of\n the nearest positioned ancestor (or the page), on a fill of the tone's\n colour. It takes no room in the flow; leave a square for it where words\n stand under it. The actions stand at the end of the line, half a square\n apart; danger words keep the text colour on the danger fill. */\n.ds-notice--banner {\n position: absolute;\n top: 0;\n inset-inline: 0;\n z-index: 9999;\n display: flex;\n align-items: center;\n gap: calc(var(--ds-unit) / 2);\n height: var(--ds-unit);\n padding-inline: var(--ds-unit);\n background: color-mix(in srgb, var(--ds-notice-mark) 40%, transparent);\n --ds-notice-ink: var(--ds-text-color);\n}\n\n.ds-notice--banner .ds-notice__mark {\n flex: none;\n margin-block: 0;\n}\n\n.ds-notice--banner .ds-notice__title,\n.ds-notice--banner .ds-notice__text {\n min-width: 0;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.ds-notice--banner .ds-notice__text {\n flex: 1;\n}\n\n.ds-notice--banner .ds-notice__actions {\n flex: none;\n min-height: 0;\n}\n\n.ds-notice--banner .ds-notice__dismiss {\n margin-block: 0;\n margin-inline-start: 0;\n}\n\n/* The app banner: a row at the top of the page, in the flow, three squares\n high: half a square above and below two squares of content, a hairline at\n its foot. The app's picture fills two squares with a picture's corners; the\n name and the line about it take a square each, on the sixteen; Open and the\n close stand on the sixteen of the square in the middle of the two. */\n.ds-notice--app {\n grid-template-columns: calc(2 * var(--ds-unit)) minmax(0, 1fr) auto var(--ds-unit);\n grid-template-rows: var(--ds-unit) var(--ds-unit);\n column-gap: calc(var(--ds-unit) / 2);\n align-items: end;\n padding: calc(var(--ds-unit) / 2) var(--ds-unit);\n background: var(--ds-paper);\n box-shadow: inset 0 -1px 0 var(--ds-border-color);\n line-height: var(--ds-type-lineheight-default);\n}\n\n.ds-notice--app[hidden] {\n display: none;\n}\n\n.ds-notice__picture {\n box-sizing: border-box;\n grid-column: 1;\n grid-row: 1 / 3;\n align-self: stretch;\n overflow: hidden;\n border-radius: var(--ds-radius);\n background: var(--ds-fill);\n outline: 1px solid var(--ds-border-color);\n outline-offset: -1px;\n}\n\n.ds-notice__picture img {\n display: block;\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n\n.ds-notice--app .ds-notice__title,\n.ds-notice--app .ds-notice__text {\n grid-column: 2;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.ds-notice--app .ds-notice__title { grid-row: 1; }\n\n.ds-notice--app .ds-notice__text {\n grid-row: 2;\n color: var(--ds-text-color-muted);\n}\n\n/* Open and the close: their square starts half a square into the two, its\n control on the lower sixteen of it. */\n.ds-notice--app .ds-notice__open,\n.ds-notice--app .ds-notice__dismiss {\n grid-row: 1 / 3;\n align-self: start;\n margin: calc(1.5 * var(--ds-unit) - var(--ds-control-height)) 0 0;\n}\n\n.ds-notice--app .ds-notice__open {\n grid-column: 3;\n text-decoration: none;\n}\n\n.ds-notice--app .ds-notice__dismiss {\n grid-column: 4;\n justify-self: end;\n}\n/*\n * Overlay: everything that stands over the page, one component with a type.\n * A `modal` stands in the centre (controls.css and the element's frame); this\n * sheet draws the others, for the `ds-overlay` element (`type=\"sheet\"`,\n * `\"drawer\"` with `side`, `\"screen\"`, which mark its native dialog\n * `data-ds-type` and `data-ds-edge`) and the native frame\n * (`dialog.ds-overlay.ds-overlay--sheet`, `--drawer` with `--start`,\n * `--screen`); controls.css imports it. A popover is placed by the element.\n *\n * - A sheet is as wide as the screen up to 32 squares and centred beyond\n * that, as high as its content up to two squares below the top, with no\n * line at its foot; its one line, at the top, is drawn inside its first\n * square (`--ds-overlay-line`), so it stands on whole squares. A handle two squares wide stands in the middle of its\n * first square and stays there while the content scrolls.\n * - A drawer is the screen's height and 16 squares wide (`--ds-overlay-width`),\n * leaving at least two squares of the page beside it; its one line is on\n * the side that faces the page. `end` is the right in a left-to-right page.\n * - Each keeps the square of padding inside the screen's safe area\n * (`env(safe-area-inset-*)`, with `viewport-fit=cover` on the page).\n * - Each slides in from its edge and back out to it, in transform alone,\n * while the backdrop fades; with reduced motion it fades in place. Dragging\n * it towards its edge (0-face/overlay-swipe.ts) moves it with the finger and\n * fades the backdrop as it goes.\n * - The page behind a modal native sheet or drawer does not scroll.\n */\n\n:is(ds-overlay > dialog[data-ds-edge=\"bottom\"], dialog.ds-overlay.ds-overlay--sheet),\n:is(ds-overlay > dialog[data-ds-edge=\"end\"], dialog.ds-overlay.ds-overlay--drawer:not(.ds-overlay--start)),\n:is(ds-overlay > dialog[data-ds-edge=\"start\"], dialog.ds-overlay.ds-overlay--drawer.ds-overlay--start) {\n --ds-overlay-gap: var(--ds-unit);\n --ds-overlay-inner: var(--ds-overlay-padding, var(--ds-unit));\n /* The line it faces the page with is drawn inside its first square. */\n --ds-overlay-within: max(0px, var(--ds-overlay-inner) - var(--ds-overlay-line, 1px));\n --ds-overlay-motion: var(--ds-overlay-enter, 380ms);\n --ds-overlay-curve: var(--ds-overlay-easing, cubic-bezier(0.32, 0.72, 0, 1));\n margin: 0;\n gap: var(--ds-overlay-gap);\n transform: var(--ds-overlay-held);\n transition:\n transform var(--ds-overlay-motion) var(--ds-overlay-curve),\n opacity var(--ds-overlay-motion) var(--ds-overlay-curve),\n overlay var(--ds-overlay-motion) allow-discrete,\n display var(--ds-overlay-motion) allow-discrete;\n}\n\n/* Content that supplies its own room. */\n:is(ds-overlay > dialog[data-ds-edge], dialog.ds-overlay:is(.ds-overlay--sheet, .ds-overlay--drawer))[data-ds-overlay-flush] {\n --ds-overlay-inner: 0px;\n --ds-overlay-gap: 0px;\n}\n\n:is(ds-overlay > dialog[data-ds-edge=\"bottom\"], dialog.ds-overlay.ds-overlay--sheet) {\n --ds-overlay-held: translateY(var(--ds-overlay-drag, 0px));\n --ds-overlay-away: translateY(100%);\n inset: auto 0 0;\n width: var(--ds-overlay-width, 100%);\n max-width: min(100%, var(--ds-overlay-sheet-width, calc(32 * var(--ds-unit))));\n height: var(--ds-overlay-height, fit-content);\n max-height: calc(100dvh - var(--ds-overlay-sheet-gap, calc(2 * var(--ds-unit))) - env(safe-area-inset-top, 0px));\n margin: 0 auto;\n padding:\n var(--ds-overlay-within)\n calc(var(--ds-overlay-inner) + env(safe-area-inset-right, 0px))\n calc(var(--ds-overlay-inner) + env(safe-area-inset-bottom, 0px))\n calc(var(--ds-overlay-inner) + env(safe-area-inset-left, 0px));\n border-bottom: 0;\n}\n\n/* The handle: two squares wide, in the middle of the first square, above the\n * content as it scrolls. It takes no room: the content starts where it would. */\n:is(ds-overlay > dialog[data-ds-edge=\"bottom\"], dialog.ds-overlay.ds-overlay--sheet)::before {\n --ds-overlay-handle-height: calc(4px * var(--ds-sf, 1));\n content: \"\";\n display: var(--ds-overlay-handle, block);\n position: sticky;\n z-index: 1;\n /* A sticky offset counts from the content edge, under the sheet's top padding: take that\n padding off, so the handle is held where it already stands, in the first square. */\n top: calc((var(--ds-unit) - var(--ds-overlay-handle-height)) / 2 - var(--ds-overlay-line, 1px) - var(--ds-overlay-within));\n flex: 0 0 auto;\n align-self: center;\n width: calc(2 * var(--ds-unit));\n height: var(--ds-overlay-handle-height);\n margin-top: calc(var(--ds-overlay-inner) * -1 + (var(--ds-unit) - var(--ds-overlay-handle-height)) / 2);\n margin-bottom: calc(var(--ds-overlay-inner) - var(--ds-overlay-gap) - var(--ds-overlay-handle-height) - (var(--ds-unit) - var(--ds-overlay-handle-height)) / 2);\n border-radius: calc(var(--ds-overlay-handle-height) / 2);\n background: var(--ds-overlay-handle-color, var(--ds-border-color));\n pointer-events: none;\n}\n\n:is(ds-overlay > dialog[data-ds-edge=\"end\"], dialog.ds-overlay.ds-overlay--drawer:not(.ds-overlay--start)),\n:is(ds-overlay > dialog[data-ds-edge=\"start\"], dialog.ds-overlay.ds-overlay--drawer.ds-overlay--start) {\n --ds-overlay-held: translateX(var(--ds-overlay-drag, 0px));\n width: calc(var(--ds-overlay-width, calc(16 * var(--ds-unit))) + var(--ds-overlay-safe-side, 0px));\n max-width: calc(100vw - 2 * var(--ds-unit));\n height: 100dvh;\n max-height: none;\n border-top: 0;\n border-bottom: 0;\n}\n\n/* At the right: the end of a left-to-right page, the start of a right-to-left one. */\n:is(ds-overlay > dialog[data-ds-edge=\"end\"], dialog.ds-overlay.ds-overlay--drawer:not(.ds-overlay--start)):not(:dir(rtl)),\n:is(ds-overlay > dialog[data-ds-edge=\"start\"], dialog.ds-overlay.ds-overlay--drawer.ds-overlay--start):dir(rtl) {\n --ds-overlay-away: translateX(100%);\n --ds-overlay-safe-side: env(safe-area-inset-right, 0px);\n inset: 0 0 0 auto;\n padding:\n calc(var(--ds-overlay-inner) + env(safe-area-inset-top, 0px))\n calc(var(--ds-overlay-inner) + env(safe-area-inset-right, 0px))\n calc(var(--ds-overlay-inner) + env(safe-area-inset-bottom, 0px))\n var(--ds-overlay-within);\n border-right: 0;\n}\n\n:is(ds-overlay > dialog[data-ds-edge=\"start\"], dialog.ds-overlay.ds-overlay--drawer.ds-overlay--start):not(:dir(rtl)),\n:is(ds-overlay > dialog[data-ds-edge=\"end\"], dialog.ds-overlay.ds-overlay--drawer:not(.ds-overlay--start)):dir(rtl) {\n --ds-overlay-away: translateX(-100%);\n --ds-overlay-safe-side: env(safe-area-inset-left, 0px);\n inset: 0 auto 0 0;\n padding:\n calc(var(--ds-overlay-inner) + env(safe-area-inset-top, 0px))\n var(--ds-overlay-within)\n calc(var(--ds-overlay-inner) + env(safe-area-inset-bottom, 0px))\n calc(var(--ds-overlay-inner) + env(safe-area-inset-left, 0px));\n border-left: 0;\n}\n\n/* The page behind a native sheet or drawer holds still while it is open, as\n * the element holds it (the element saves and restores the scroll itself). */\n:root:has(dialog.ds-overlay:is(.ds-overlay--sheet, .ds-overlay--drawer):modal) {\n overflow: hidden;\n overscroll-behavior: none;\n}\n\n/* Out to its edge, a little quicker than it came; and in from it. */\n:is(ds-overlay > dialog[data-ds-edge], dialog.ds-overlay:is(.ds-overlay--sheet, .ds-overlay--drawer)):not([open]) {\n --ds-overlay-motion: var(--ds-overlay-exit, 260ms);\n transform: var(--ds-overlay-away);\n}\n\n@starting-style {\n :is(ds-overlay > dialog[data-ds-edge], dialog.ds-overlay:is(.ds-overlay--sheet, .ds-overlay--drawer))[open] {\n transform: var(--ds-overlay-away);\n }\n}\n\n:is(ds-overlay > dialog[data-ds-edge], dialog.ds-overlay:is(.ds-overlay--sheet, .ds-overlay--drawer))::backdrop {\n opacity: calc(1 - var(--ds-overlay-progress, 0));\n transition:\n opacity var(--ds-overlay-motion, 380ms) ease,\n overlay var(--ds-overlay-motion, 380ms) allow-discrete,\n display var(--ds-overlay-motion, 380ms) allow-discrete;\n}\n\n:is(ds-overlay > dialog[data-ds-edge], dialog.ds-overlay:is(.ds-overlay--sheet, .ds-overlay--drawer)):not([open])::backdrop {\n opacity: 0;\n}\n\n@starting-style {\n :is(ds-overlay > dialog[data-ds-edge], dialog.ds-overlay:is(.ds-overlay--sheet, .ds-overlay--drawer))[open]::backdrop {\n opacity: 0;\n }\n}\n\n/* While a finger holds it, it follows the finger alone. */\n:is(ds-overlay > dialog[data-ds-edge], dialog.ds-overlay:is(.ds-overlay--sheet, .ds-overlay--drawer))[data-ds-overlay-dragging],\n:is(ds-overlay > dialog[data-ds-edge], dialog.ds-overlay:is(.ds-overlay--sheet, .ds-overlay--drawer))[data-ds-overlay-dragging]::backdrop {\n transition: none;\n}\n\n/* Reduced motion: no slide. It fades where it stands, a dragged one too. */\n@media (prefers-reduced-motion: reduce) {\n :is(ds-overlay > dialog[data-ds-edge], dialog.ds-overlay:is(.ds-overlay--sheet, .ds-overlay--drawer)) {\n --ds-overlay-motion: 150ms;\n transition:\n opacity var(--ds-overlay-motion) ease,\n overlay var(--ds-overlay-motion) allow-discrete,\n display var(--ds-overlay-motion) allow-discrete;\n }\n\n :is(ds-overlay > dialog[data-ds-edge], dialog.ds-overlay:is(.ds-overlay--sheet, .ds-overlay--drawer)):not([open]) {\n --ds-overlay-motion: 150ms;\n transform: var(--ds-overlay-held);\n opacity: 0;\n }\n\n @starting-style {\n :is(ds-overlay > dialog[data-ds-edge], dialog.ds-overlay:is(.ds-overlay--sheet, .ds-overlay--drawer))[open] {\n transform: var(--ds-overlay-held);\n opacity: 0;\n }\n }\n}\n\n/*\n * The screen: an overlay over the whole screen, for navigation folded away on\n * a narrow screen (`ds-header`, `ds-page drawer`, `attachOverlay()`).\n *\n * The panel is the dialog frame (`dialog.ds-overlay`) laid over the whole\n * screen, on the page's own paper, a square of room on every side. Its first\n * row holds the close button where the menu button stood; what the page\n * folded away (its navigation and actions) follows, every link one square\n * high. The page script `0-face/attach-overlay.ts` opens it: it moves the folded parts\n * into the panel and back, keeps focus inside, closes on Escape, on a link and\n * when the screen widens, and holds the page's scroll while it is open.\n * `ds-header` and `ds-page drawer` use the same script and these rules.\n *\n * ds-overlay--screen on a <dialog class=\"ds-overlay\">, the panel\n * ds-overlay__bar the first row: the close button at its end\n * ds-overlay__close the close button, an icon button\n * ds-overlay__toggle the button that opens it: it carries\n * two icons, the first shown while closed and the\n * second (close) while open\n *\n * Tokens: `--ds-overlay-paper` (the page's paper by default).\n */\n\n:is(ds-overlay > dialog[data-ds-type=\"screen\"], dialog.ds-overlay.ds-overlay--screen) {\n --ds-overlay-width: 100vw;\n width: 100vw;\n max-width: none;\n height: 100dvh;\n max-height: none;\n margin: 0;\n padding: var(--ds-unit);\n gap: 0;\n border: 0;\n background: var(--ds-overlay-paper, var(--background, Canvas));\n overflow-y: auto;\n}\n\n:is(ds-overlay > dialog[data-ds-type=\"screen\"], dialog.ds-overlay.ds-overlay--screen)[open] {\n display: flex;\n flex-direction: column;\n align-items: stretch;\n gap: var(--ds-unit);\n}\n\n/* Content that supplies its own room, as the documentation's navigation does. */\n:is(ds-overlay > dialog[data-ds-type=\"screen\"], dialog.ds-overlay.ds-overlay--screen)[data-ds-overlay-flush] {\n padding: 0;\n gap: 0;\n}\n\n:is(ds-overlay > dialog[data-ds-type=\"screen\"], dialog.ds-overlay.ds-overlay--screen)::backdrop {\n background: transparent;\n}\n\n/* It arrives by fading in and settling half a square, and leaves at once. */\n@media (prefers-reduced-motion: no-preference) {\n :is(ds-overlay > dialog[data-ds-type=\"screen\"], dialog.ds-overlay.ds-overlay--screen)[open] {\n transition: opacity 160ms ease-out, transform 160ms ease-out;\n }\n\n @starting-style {\n :is(ds-overlay > dialog[data-ds-type=\"screen\"], dialog.ds-overlay.ds-overlay--screen)[open] {\n opacity: 0;\n transform: translateY(calc(var(--ds-unit) / -2));\n }\n }\n}\n\n.ds-overlay__bar {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n min-height: var(--ds-row-height);\n}\n\n/* Centred in the last square of the row, as the menu button is. Two classes,\n so the button's own margin does not undo it. */\n.ds-overlay__bar > .ds-overlay__close {\n margin-inline-end: calc((var(--ds-unit) - var(--ds-control-height)) / 2);\n}\n\n/* The toggle shows its first icon while closed and its second while open; the\n close button, a copy of it, shows the second. */\n.ds-overlay__toggle:not([aria-expanded=\"true\"]) > :nth-child(2),\n.ds-overlay__toggle[aria-expanded=\"true\"] > :first-child:not(:only-child),\n.ds-overlay__close > :first-child:not(:only-child) {\n display: none;\n}\n/*\n * A site header, as plain classes on native elements.\n *\n * One row of one square, a square of room around it: the brand at the start\n * in twelve squares (as a page's navigation column is, so the links stand\n * over the work), the navigation two squares after it, the actions at the end. Every link is\n * one square high, a square apart, in the text's ink; the page you are on\n * (`aria-current=\"page\"`) stands on a hairline of the full ink, the others\n * show a faint one under the pointer. Below thirty squares of width the\n * navigation and the actions fold into a drawer: the menu button takes their\n * place at the end of the row, and `0-face/header.ts` opens them over the page.\n * The `ds-header` element draws the same parts.\n *\n * ds-header the row, a <header>\n * ds-header__brand the mark or name, a link home; a picture in it is one square high\n * ds-header__nav the links, a <nav>\n * ds-header__actions buttons or links at the end\n * ds-header__toggle the menu button: `.ds-button.ds-button--icon.ds-overlay__toggle`\n * with a menu icon and a close icon, shown only on a narrow screen\n * ds-header--sticky stays at the top of the page as it scrolls, on the paper\n *\n * Tokens: `--ds-header-brand` (the brand's width, at least twelve squares),\n * `--ds-header-paper` (the sticky header's paper) and `--ds-header-line` (the\n * hairline under a sticky header, none by default).\n */\n\n.ds-header {\n box-sizing: border-box;\n container-type: inline-size;\n display: flex;\n align-items: center;\n gap: calc(2 * var(--ds-unit));\n min-width: 0;\n margin: 0;\n padding: var(--ds-unit);\n color: var(--ds-text-color);\n font-family: var(--ds-typeface-regular);\n font-feature-settings: var(--ds-font-features);\n font-size: var(--ds-type-size-default);\n font-weight: var(--ds-type-weight-default);\n line-height: var(--ds-unit);\n letter-spacing: var(--ds-type-letterspacing-default);\n text-transform: none;\n}\n\n.ds-header--sticky {\n position: sticky;\n top: 0;\n z-index: 1;\n background: var(--ds-header-paper, var(--background, Canvas));\n box-shadow: inset 0 -1px var(--ds-header-line, transparent);\n}\n\n.ds-header__brand {\n box-sizing: border-box;\n display: inline-flex;\n flex: 0 0 auto;\n min-width: var(--ds-header-brand, calc(12 * var(--ds-unit)));\n align-items: center;\n gap: calc(var(--ds-unit) / 2);\n height: var(--ds-unit);\n color: inherit;\n font-family: var(--ds-typeface-medium);\n font-weight: var(--ds-type-weight-bold);\n text-decoration: none;\n white-space: nowrap;\n}\n\n.ds-header__brand :is(img, svg) {\n display: block;\n width: auto;\n height: var(--ds-unit);\n}\n\n.ds-header__nav,\n.ds-header__actions {\n display: flex;\n align-items: center;\n gap: var(--ds-unit);\n min-width: 0;\n margin: 0;\n padding: 0;\n}\n\n.ds-header__nav {\n flex: 1 1 auto;\n flex-wrap: wrap;\n row-gap: 0;\n}\n\n.ds-header__actions {\n flex: 0 0 auto;\n justify-content: flex-end;\n gap: calc(var(--ds-unit) / 2);\n margin-inline-start: auto;\n}\n\n/* A link in the navigation: the text's ink on a one-square line. */\n.ds-header__nav a {\n display: block;\n height: var(--ds-unit);\n color: inherit;\n text-decoration-line: underline;\n text-decoration-thickness: 1px;\n text-decoration-color: transparent;\n text-underline-offset: calc(2px * var(--ds-sf));\n white-space: nowrap;\n}\n\n.ds-header__nav a:hover {\n text-decoration-color: color-mix(in srgb, currentColor 40%, transparent);\n}\n\n.ds-header__nav a[aria-current]:not([aria-current=\"false\"]) {\n text-decoration-color: currentColor;\n}\n\n.ds-header__nav a:focus-visible,\n.ds-header__brand:focus-visible {\n outline: 2px solid var(--ds-focus-ring);\n outline-offset: 1px;\n}\n\n/* The menu button stands at the end of the row, and only on a narrow screen.\n Two classes, so the button's own display and margin do not undo it. */\n.ds-header > .ds-header__toggle {\n display: none;\n margin-inline-start: auto;\n /* Centred in the last square of the row, where the close button stands too. */\n margin-inline-end: calc((var(--ds-unit) - var(--ds-control-height)) / 2);\n}\n\n@container (max-width: 599px) {\n .ds-header > :is(.ds-header__nav, .ds-header__actions) {\n display: none;\n }\n\n .ds-header > .ds-header__toggle {\n display: inline-flex;\n }\n\n .ds-header > .ds-header__brand {\n min-width: 0;\n }\n}\n\n/* In the drawer the navigation is a column of one-square links, and the\n actions a row one square below it. */\n.ds-overlay--screen :is(.ds-header__nav, .ds-header__actions) {\n flex: 0 0 auto;\n flex-direction: column;\n align-items: flex-start;\n flex-wrap: nowrap;\n gap: 0;\n margin: 0;\n}\n\n.ds-overlay--screen .ds-header__actions {\n flex-direction: row;\n flex-wrap: wrap;\n gap: calc(var(--ds-unit) / 2);\n}\n/*\n * A site footer, as plain classes on native elements.\n *\n * The brand and its line at the start, twelve squares wide; the columns of\n * links beside it, eight squares each, two squares apart; then, two squares\n * below, the legal line at the start and the preferences (language and theme,\n * as `ds-cycle` or native buttons) at the end. Every line is one square, the\n * column headings in the title grey, the links in the text's ink with a faint\n * hairline under the pointer. A hairline runs along its top. Columns that do\n * not fit beside the brand go below it, and on a narrow screen everything\n * stands in one column. The `ds-footer` element draws the same parts from data.\n *\n * ds-footer the block, a <footer>\n * ds-footer__brand the mark and its line\n * ds-footer__mark the mark or name, a link home; a picture in it is one square high\n * ds-footer__tagline a line about the site, muted\n * ds-footer__columns the columns, a <nav> or <div>\n * ds-footer__column one column: a heading and its links\n * ds-footer__heading the column's heading, one line in the title grey\n * ds-footer__links the links, a <ul> with one link per <li>\n * ds-footer__base the last row, two squares below\n * ds-footer__legal in it, the copyright and legal line, muted\n * ds-footer__preferences in it, the language and theme controls, at its end\n *\n * Tokens: `--ds-footer-line` (the hairline along the top).\n */\n\n.ds-footer {\n box-sizing: border-box;\n container-type: inline-size;\n display: flex;\n flex-wrap: wrap;\n align-items: flex-start;\n gap: calc(2 * var(--ds-unit));\n min-width: 0;\n margin: 0;\n padding: calc(2 * var(--ds-unit)) var(--ds-unit) var(--ds-unit);\n box-shadow: inset 0 1px var(--ds-footer-line, var(--ds-border-color));\n color: var(--ds-text-color);\n font-family: var(--ds-typeface-regular);\n font-feature-settings: var(--ds-font-features);\n font-size: var(--ds-type-size-default);\n font-weight: var(--ds-type-weight-default);\n line-height: var(--ds-unit);\n letter-spacing: var(--ds-type-letterspacing-default);\n text-transform: none;\n}\n\n.ds-footer :is(p, h2, h3, h4, ul) {\n margin: 0;\n font: inherit;\n letter-spacing: inherit;\n}\n\n.ds-footer__brand {\n display: flex;\n flex: 0 0 calc(12 * var(--ds-unit));\n flex-direction: column;\n max-width: 100%;\n min-width: 0;\n}\n\n.ds-footer__mark {\n display: inline-flex;\n align-items: center;\n gap: calc(var(--ds-unit) / 2);\n align-self: flex-start;\n height: var(--ds-unit);\n color: inherit;\n font-family: var(--ds-typeface-medium);\n font-weight: var(--ds-type-weight-bold);\n text-decoration: none;\n}\n\n.ds-footer__mark :is(img, svg) {\n display: block;\n width: auto;\n height: var(--ds-unit);\n}\n\n.ds-footer__tagline,\n.ds-footer__legal {\n color: var(--ds-text-color-muted);\n}\n\n.ds-footer__columns {\n display: flex;\n /* Never narrower than one column: below that the columns go under the brand. */\n flex: 1 1 calc(8 * var(--ds-unit));\n flex-wrap: wrap;\n gap: var(--ds-unit) calc(2 * var(--ds-unit));\n min-width: min(100%, calc(8 * var(--ds-unit)));\n}\n\n.ds-footer__column {\n display: flex;\n flex: 0 0 calc(8 * var(--ds-unit));\n flex-direction: column;\n max-width: 100%;\n min-width: 0;\n}\n\n.ds-footer .ds-footer__heading {\n color: var(--ds-title-color);\n}\n\n.ds-footer .ds-footer__links {\n padding: 0;\n list-style: none;\n}\n\n.ds-footer__links li {\n margin: 0;\n padding: 0;\n}\n\n.ds-footer__links a {\n display: inline-block;\n max-width: 100%;\n overflow: hidden;\n color: inherit;\n text-decoration-line: underline;\n text-decoration-thickness: 1px;\n text-decoration-color: transparent;\n text-overflow: ellipsis;\n text-underline-offset: calc(2px * var(--ds-sf));\n vertical-align: top;\n white-space: nowrap;\n}\n\n.ds-footer__links a:hover {\n text-decoration-color: color-mix(in srgb, currentColor 40%, transparent);\n}\n\n.ds-footer__links a[aria-current]:not([aria-current=\"false\"]) {\n text-decoration-color: currentColor;\n}\n\n:is(.ds-footer__links a, .ds-footer__mark):focus-visible {\n outline: 2px solid var(--ds-focus-ring);\n outline-offset: 1px;\n}\n\n/* The last row, two squares below: the legal line at the start, the\n preferences at the end; on a narrow screen the preferences go first. */\n.ds-footer__base {\n display: flex;\n flex: 0 0 100%;\n flex-wrap: wrap;\n align-items: flex-start;\n gap: var(--ds-unit) calc(2 * var(--ds-unit));\n min-width: 0;\n}\n\n.ds-footer__legal {\n flex: 1 1 auto;\n min-width: 0;\n}\n\n.ds-footer__preferences {\n display: flex;\n flex: 0 0 auto;\n flex-wrap: wrap;\n align-items: center;\n gap: 0 calc(var(--ds-unit) / 2);\n min-height: var(--ds-unit);\n margin-inline-start: auto;\n}\n\n@container (max-width: 599px) {\n .ds-footer__brand,\n .ds-footer__columns {\n flex-basis: 100%;\n }\n\n .ds-footer__base {\n flex-direction: column;\n }\n\n .ds-footer__preferences {\n order: -1;\n margin-inline-start: 0;\n }\n}\n/*\n * A document page: terms, a privacy policy, a legal notice, a help article.\n *\n * The contents stand at the start, twelve squares wide, and stay in view as\n * the writing scrolls past; the writing, two squares after them, is a reading\n * column of at most thirty-two squares, native headings and paragraphs marked\n * `data-ds-prose` (prose.css draws them). Where the two do not fit side by\n * side the contents stand above the writing. Every line is one square; the\n * contents' links are in the text's ink, the section in view (`aria-current`)\n * on a hairline of the full ink, a third level half a square in. The\n * `ds-document` element builds the contents from the headings and marks the\n * one in view.\n *\n * ds-document the page's two columns\n * ds-document__toc the contents, a <nav>, as tall as the writing\n * ds-document__contents inside it, what stays in view: the title and the list\n * ds-document__toc-title \"Contents\", one line in the title grey\n * ds-document__toc-list the links, an <ol> with one link per <li>\n * ds-document__toc-sub an <li> for a third-level heading\n * ds-document__body the writing, marked data-ds-prose\n *\n * A definition table (`ds-definitions` on a <dl>) sets terms beside what they\n * mean, as a legal notice such as 特定商取引法に基づく表記 does: the term eight\n * squares wide in the muted ink, its definition beside it, each pair on a\n * hairline at its foot with half a square above and below, as a prose table's\n * rows are. Pairs may stand in a <div> each. Below twenty-four squares the\n * definition goes under its term. It works inside or outside prose.\n *\n * Tokens: `--ds-document-top` (where the contents stop below the top of the\n * view, one square; add a sticky header's height), `--ds-definitions-term`\n * (the term column, eight squares).\n */\n\n.ds-document {\n box-sizing: border-box;\n display: flex;\n flex-wrap: wrap;\n align-items: stretch;\n gap: var(--ds-unit) calc(2 * var(--ds-unit));\n min-width: 0;\n color: var(--ds-text-color);\n font-family: var(--ds-typeface-regular);\n font-feature-settings: var(--ds-font-features);\n font-size: var(--ds-type-size-default);\n font-weight: var(--ds-type-weight-default);\n line-height: var(--ds-unit);\n letter-spacing: var(--ds-type-letterspacing-default);\n}\n\n/* The contents column stretches to the writing's height when the two stand\n side by side, so what is inside it can stay in view; standing above the\n writing it is only as tall as itself, and nothing sticks. */\n.ds-document__toc {\n flex: 0 0 calc(12 * var(--ds-unit));\n max-width: 100%;\n min-width: 0;\n}\n\n.ds-document__contents {\n position: sticky;\n top: var(--ds-document-top, var(--ds-unit));\n}\n\n.ds-document__toc-title {\n margin: 0;\n color: var(--ds-title-color);\n font: inherit;\n letter-spacing: inherit;\n}\n\n.ds-document__toc-list,\n.ds-document__toc-list ol {\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.ds-document__toc-list li {\n margin: 0;\n padding: 0;\n}\n\n.ds-document__toc-list .ds-document__toc-sub {\n padding-inline-start: calc(var(--ds-unit) / 2);\n}\n\n.ds-document__toc-list a {\n display: block;\n overflow: hidden;\n color: inherit;\n text-decoration-line: underline;\n text-decoration-thickness: 1px;\n text-decoration-color: transparent;\n text-overflow: ellipsis;\n text-underline-offset: calc(2px * var(--ds-sf));\n white-space: nowrap;\n}\n\n.ds-document__toc-list a:hover {\n text-decoration-color: color-mix(in srgb, currentColor 40%, transparent);\n}\n\n.ds-document__toc-list a[aria-current]:not([aria-current=\"false\"]) {\n text-decoration-color: currentColor;\n}\n\n.ds-document__toc-list a:focus-visible {\n outline: 2px solid var(--ds-focus-ring);\n outline-offset: -1px;\n}\n\n/* The writing: at least twenty squares beside the contents, else below them. */\n.ds-document__body {\n flex: 1 1 0;\n min-width: min(100%, calc(20 * var(--ds-unit)));\n max-width: calc(32 * var(--ds-unit));\n}\n\n/* A heading the contents jump to stops where the contents do. */\n.ds-document__body :is(h1, h2, h3, h4, h5, h6) {\n scroll-margin-top: var(--ds-document-top, var(--ds-unit));\n}\n\n/* A definition table. */\n\n.ds-definitions {\n box-sizing: border-box;\n container-type: inline-size;\n display: grid;\n grid-template-columns: minmax(0, var(--ds-definitions-term, calc(8 * var(--ds-unit)))) minmax(0, 1fr);\n margin: 0;\n padding: 0;\n color: var(--ds-text-color);\n font-family: var(--ds-typeface-regular);\n font-size: var(--ds-type-size-default);\n line-height: var(--ds-unit);\n letter-spacing: var(--ds-type-letterspacing-default);\n}\n\n.ds-definitions > div {\n display: contents;\n}\n\n.ds-definitions :is(dt, dd) {\n box-sizing: border-box;\n min-width: 0;\n margin: 0;\n padding-block: calc(var(--ds-unit) / 2);\n box-shadow: inset 0 -1px var(--ds-border-color);\n font: inherit;\n letter-spacing: inherit;\n line-height: var(--ds-unit);\n overflow-wrap: anywhere;\n}\n\n.ds-definitions dt {\n grid-column: 1;\n padding-inline-end: calc(var(--ds-unit) / 2);\n color: var(--ds-text-color-muted);\n}\n\n.ds-definitions dd {\n grid-column: 2;\n}\n\n@container (max-width: 479px) {\n .ds-definitions dt,\n .ds-definitions dd {\n grid-column: 1 / -1;\n }\n\n .ds-definitions dt {\n padding-block-end: 0;\n padding-inline-end: 0;\n box-shadow: none;\n }\n\n .ds-definitions dd {\n padding-block-start: 0;\n }\n}\n/*\n * A carousel, as plain classes on native elements.\n *\n * A carousel is a row of slides that snaps sideways, one slide to a stop,\n * with a row of controls one square high under it: optional previous and\n * next buttons and a dot for each slide. Slides are the track's width, or\n * whole squares wide, a whole number of squares apart (one by default). The\n * controls stand one square below the track, on the grid from its start.\n * The `ds-carousel` element draws the same parts, and\n * `0-face/carousel.ts` gives the native markup its behaviour.\n *\n * ds-carousel the region: track, controls, status;\n * `data-slides=\"auto\"` for slides of their\n * own width, `data-bleed` `end` or `both`,\n * `data-snap=\"none\"` to slide freely\n * ds-carousel__track the scroller; every child is a slide\n * ds-carousel__controls the row of buttons and dots\n * ds-carousel__previous, __next, icon buttons, on `.ds-button.ds-button--icon`\n * __pause\n * ds-carousel__status the polite announcement, kept out of sight\n *\n * The dots are `ds-progress--dots` from progress.css.\n *\n * A strip of slides of their own width can run on past its box to the\n * window's end edge, or to both edges, while its first slide keeps to the\n * box's start, so it lines up with the page's column; `--ds-carousel-before`\n * and `--ds-carousel-after` are the room to the edges, which the script\n * measures.\n *\n * Tokens: `--ds-carousel-slide` (a slide's width, the track's by default)\n * and `--ds-carousel-gap` (between slides, one square by default).\n */\n\n.ds-carousel {\n box-sizing: border-box;\n display: grid;\n grid-template-columns: minmax(0, 1fr);\n row-gap: var(--ds-unit);\n position: relative;\n min-width: 0;\n margin: 0;\n}\n\n.ds-carousel__track {\n box-sizing: border-box;\n display: grid;\n grid-auto-flow: column;\n grid-auto-columns: var(--ds-carousel-slide, 100%);\n column-gap: var(--ds-carousel-gap, var(--ds-unit));\n min-width: 0;\n margin: 0;\n padding: 0;\n list-style: none;\n overflow-x: auto;\n overflow-y: hidden;\n overscroll-behavior-x: contain;\n scroll-snap-type: x mandatory;\n scrollbar-width: none;\n}\n\n.ds-carousel__track::-webkit-scrollbar {\n display: none;\n}\n\n/* Slides of their own width, several in view. */\n.ds-carousel[data-slides=\"auto\"] > .ds-carousel__track {\n grid-auto-columns: max-content;\n}\n\n.ds-carousel[data-snap=\"none\"] > .ds-carousel__track {\n scroll-snap-type: none;\n}\n\n/* To the window's end edge: the track reaches out over the room after the\n box and keeps a gap at its end, so the last slide stops short of the edge. */\n.ds-carousel[data-bleed=\"end\"] > .ds-carousel__track {\n margin-inline-end: calc(-1 * var(--ds-carousel-after, 0px));\n padding-inline-end: var(--ds-carousel-gap, var(--ds-unit));\n}\n\n/* To both edges: the track reaches out both ways, and the first slide, and\n each one it stops at, still stands on the box's start. */\n.ds-carousel[data-bleed=\"both\"] > .ds-carousel__track {\n margin-inline: calc(-1 * var(--ds-carousel-before, 0px)) calc(-1 * var(--ds-carousel-after, 0px));\n padding-inline: var(--ds-carousel-before, 0px) var(--ds-carousel-gap, var(--ds-unit));\n scroll-padding-inline-start: var(--ds-carousel-before, 0px);\n}\n\n.ds-carousel__track:focus-visible {\n outline: 2px solid var(--ds-focus-ring);\n outline-offset: 1px;\n}\n\n.ds-carousel__track > * {\n box-sizing: border-box;\n min-width: 0;\n margin: 0;\n scroll-snap-align: start;\n}\n\n/* One slide to a stop; a strip of several in view may pass a few. */\n.ds-carousel:not([data-slides=\"auto\"]) > .ds-carousel__track > * {\n scroll-snap-stop: always;\n}\n\n.ds-carousel__controls {\n display: flex;\n align-items: center;\n height: var(--ds-row-height);\n min-width: 0;\n}\n\n/* The buttons take a one-square cell each, as the dots do. Two classes, so\n the button's own reset of its margin does not undo it. */\n.ds-carousel .ds-carousel__previous,\n.ds-carousel .ds-carousel__next,\n.ds-carousel .ds-carousel__pause {\n flex: none;\n margin: calc((var(--ds-unit) - var(--ds-control-height)) / 2);\n}\n\n/* Previous points back along the reading direction. */\n.ds-carousel:dir(rtl) :is(.ds-carousel__previous, .ds-carousel__next) > * {\n transform: scaleX(-1);\n}\n\n.ds-carousel__status {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n}\n/*\n * A card, as plain classes on native elements.\n *\n * A card holds one thing on a fill of its own: its name in the muted ink on\n * one square, a title in the display size on lines of a square and a half, writing,\n * and a picture of it (`ds-card__media`) a square below, which can run off\n * the card's foot and end edge and be cut there, never framed. Its size is\n * given in whole squares, its padding one square, its corners a card's\n * (`--ds-radius-card`). A card that opens something is one press target, an\n * `<a class=\"ds-card\" href>`: the pointer tints it, keyboard focus rings it\n * 2px inside and its picture rises a little. Ordinary writing still sits\n * straight on the paper; a card is for a thing shown among others, as in a\n * slide. The `ds-card` element draws the same parts.\n *\n * ds-card the card; `data-tone` names a palette colour\n * for its fill (the colour's pastel, dim in dark\n * mode, from palette.css, which one.css imports),\n * `ink` for the ink with the paper's words\n * ds-card--outlined a 1px line and no fill\n * ds-card__label its name, muted, on one square\n * ds-card__title the title, display size, a square and a half a line\n * ds-card__body writing, one square a line\n * ds-card__media the picture, a square below the words\n * ds-card__media--center standing in the card's middle\n * ds-card__media--bleed running on past the padding to the end edge\n *\n * Tokens: `--ds-card-width` and `--ds-card-height` (whole squares; the\n * content's size by default), `--ds-card-padding` (one square), and\n * `--ds-card-fill`, `--ds-card-ink` and `--ds-card-muted`.\n */\n\n.ds-card {\n --ds-card-fill: var(--ds-fill);\n --ds-card-ink: var(--ds-text-color);\n --ds-card-muted: var(--ds-text-color-muted);\n --ds-card-padding: var(--ds-unit);\n position: relative;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n align-items: stretch;\n width: var(--ds-card-width, auto);\n height: var(--ds-card-height, auto);\n min-width: 0;\n margin: 0;\n padding: var(--ds-card-padding) var(--ds-card-padding) 0;\n overflow: hidden;\n border-radius: var(--ds-radius-card);\n background: var(--ds-card-fill);\n color: var(--ds-card-ink);\n font-family: var(--ds-typeface-regular);\n font-size: var(--ds-type-size-default);\n text-decoration: none;\n -webkit-tap-highlight-color: transparent;\n}\n\n.ds-card[hidden] {\n display: none;\n}\n\n.ds-card--outlined {\n --ds-card-fill: transparent;\n box-shadow: inset 0 0 0 1px var(--ds-border-color);\n}\n\n.ds-card[data-tone=\"apple-green\"] { --ds-card-fill: var(--one-apple-green-pastel); }\n.ds-card[data-tone=\"chartreuse\"] { --ds-card-fill: var(--one-chartreuse-pastel); }\n.ds-card[data-tone=\"every-green\"] { --ds-card-fill: var(--one-every-green-pastel); }\n.ds-card[data-tone=\"yellow\"] { --ds-card-fill: var(--one-yellow-pastel); }\n.ds-card[data-tone=\"orange\"] { --ds-card-fill: var(--one-orange-pastel); }\n.ds-card[data-tone=\"tuned-red\"] { --ds-card-fill: var(--one-tuned-red-pastel); }\n.ds-card[data-tone=\"pink\"] { --ds-card-fill: var(--one-pink-pastel); }\n.ds-card[data-tone=\"sharp-blue\"] { --ds-card-fill: var(--one-sharp-blue-pastel); }\n.ds-card[data-tone=\"winning-blue\"] { --ds-card-fill: var(--one-winning-blue-pastel); }\n.ds-card[data-tone=\"hysteric-blue\"] { --ds-card-fill: var(--one-hysteric-blue-pastel); }\n.ds-card[data-tone=\"purple\"] { --ds-card-fill: var(--one-purple-pastel); }\n.ds-card[data-tone=\"metal-green\"] { --ds-card-fill: var(--one-metal-green-pastel); }\n.ds-card[data-tone=\"drip-yellow\"] { --ds-card-fill: var(--one-drip-yellow-pastel); }\n.ds-card[data-tone=\"sand\"] { --ds-card-fill: var(--one-sand-pastel); }\n.ds-card[data-tone=\"old-gold\"] { --ds-card-fill: var(--one-old-gold-pastel); }\n.ds-card[data-tone=\"red-for-blood\"] { --ds-card-fill: var(--one-red-for-blood-pastel); }\n.ds-card[data-tone=\"dark-red-variant\"] { --ds-card-fill: var(--one-dark-red-variant-pastel); }\n.ds-card[data-tone=\"signal-red\"] { --ds-card-fill: var(--one-signal-red-pastel); }\n.ds-card[data-tone=\"ink\"] {\n --ds-card-fill: var(--ds-black);\n --ds-card-ink: var(--ds-white);\n --ds-card-muted: color-mix(in srgb, var(--ds-white) 60%, transparent);\n box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ds-white) 8%, transparent);\n}\n\n/* Words: each line on the lower sixteen of its square. They keep their room\n in a card of a set height; only the picture is cut at the foot. */\n.ds-card__label,\n.ds-card__title,\n.ds-card__body {\n flex: none;\n}\n\n.ds-card__label {\n margin: 0;\n padding-top: calc(4px * var(--ds-sf, 1));\n color: var(--ds-card-muted);\n font: inherit;\n line-height: var(--ds-type-lineheight-default, calc(16px * var(--ds-sf, 1)));\n}\n\n.ds-card__title {\n margin: 0;\n color: var(--ds-card-ink);\n font-family: var(--ds-typeface-medium);\n font-size: var(--ds-type-size-display);\n font-weight: var(--ds-type-weight-bold);\n /* A title that wraps keeps its lines together: a square and a half each. */\n line-height: calc(1.5 * var(--ds-unit));\n letter-spacing: calc(var(--ds-display-text-tracking, -0.4px) * var(--ds-sf, 1));\n text-wrap: balance;\n}\n\n.ds-card__body {\n margin: 0;\n line-height: var(--ds-unit);\n}\n\n.ds-card__media {\n display: flex;\n flex: none;\n align-items: flex-start;\n justify-content: flex-start;\n min-width: 0;\n margin-top: var(--ds-unit);\n}\n\n.ds-card__media--center {\n justify-content: center;\n margin-inline: calc(-1 * var(--ds-card-padding));\n}\n\n.ds-card__media--bleed {\n margin-inline-end: calc(-1 * var(--ds-card-padding));\n}\n\n.ds-card__media > * {\n flex: none;\n}\n\n/* A card that opens something. */\na.ds-card,\n.ds-card[data-interactive] {\n cursor: pointer;\n}\n\na.ds-card::after,\n.ds-card[data-interactive]::after {\n content: \"\";\n position: absolute;\n inset: 0;\n border-radius: inherit;\n background: transparent;\n pointer-events: none;\n}\n\na.ds-card:focus-visible,\n.ds-card[data-interactive]:focus-visible {\n outline: 2px solid var(--ds-focus-ring);\n outline-offset: -2px;\n}\n\n@media (hover: hover) {\n a.ds-card:hover::after,\n .ds-card[data-interactive]:hover::after {\n background: color-mix(in srgb, var(--ds-card-ink) 4%, transparent);\n }\n}\n\na.ds-card:active::after,\n.ds-card[data-interactive]:active::after {\n background: color-mix(in srgb, var(--ds-card-ink) 8%, transparent);\n}\n\n@media (hover: hover) and (prefers-reduced-motion: no-preference) {\n a.ds-card > .ds-card__media,\n .ds-card[data-interactive] > .ds-card__media {\n transition: transform 240ms cubic-bezier(0.23, 1, 0.32, 1);\n }\n\n a.ds-card:hover > .ds-card__media,\n .ds-card[data-interactive]:hover > .ds-card__media {\n transform: translateY(calc(-0.25 * var(--ds-unit)));\n }\n}\n/*\n * Figures, as plain classes on native elements: a stat and a group of stats.\n * The `ds-stat` element draws the same parts; controls.css imports this\n * file. A value against its range, steps and page dots are `progress.css`.\n *\n * Everything sits on the paper with no box, every line is one square and\n * widths come in whole squares. Colour is kept for a state: the danger colour\n * for a figure that is over.\n *\n * A stat: a label, a figure and a line for its change or caption.\n *\n * ds-stat the block, as wide as its column\n * ds-stat__label one line, muted; an icon may lead it on its own square\n * ds-stat__icon the square an icon from the library stands on\n * ds-stat__figure the figure in tabular numerals, 20 on two squares\n * ds-stat__digit one digit in a cell of one width, for a face without tabular numerals\n * ds-stat__caption one line, muted, under the figure\n * ds-stat__change the change at the start of the caption, in the text colour;\n * --danger or --warning where the page knows which way is bad\n * ds-stat--danger, --warning the figure in that colour\n * ds-stats a group of stats on whole squares: columns eight\n * squares wide, one square between them and between rows\n *\n * Tokens: `--ds-stat-size` (the figure's type size before the scale, 20px)\n * and `--ds-stat-lines` (the squares its line takes, 2), `--ds-stat-typeface`\n * (the medium face), `--ds-stat-digit` (a digit cell's width, 1ch),\n * `--ds-stat-change-color`, and `--ds-stats-width` (a column's width in\n * squares, 8).\n *\n */\n\n/* ---- Stat ------------------------------------------------------------ */\n\n.ds-stat {\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n min-width: 0;\n margin: 0;\n color: var(--ds-text-color);\n font-family: var(--ds-typeface-regular);\n font-feature-settings: var(--ds-font-features);\n font-size: var(--ds-type-size-default);\n font-weight: var(--ds-type-weight-default);\n line-height: var(--ds-unit);\n letter-spacing: var(--ds-type-letterspacing-default);\n text-transform: none;\n}\n\n.ds-stat__label,\n.ds-stat__figure,\n.ds-stat__caption {\n box-sizing: border-box;\n max-width: 100%;\n margin: 0;\n font: inherit;\n letter-spacing: inherit;\n}\n\n.ds-stat__label,\n.ds-stat__caption {\n display: flex;\n align-items: flex-start;\n min-height: var(--ds-unit);\n color: var(--ds-text-color-muted);\n overflow-wrap: anywhere;\n}\n\n.ds-stat__icon {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n width: var(--ds-unit);\n height: var(--ds-unit);\n}\n\n.ds-stat__figure {\n display: block;\n overflow: hidden;\n font-family: var(--ds-stat-typeface, var(--ds-typeface-medium));\n font-size: calc(var(--ds-stat-size, var(--ds-display-text-size, 20px)) * var(--ds-sf, 1));\n font-weight: var(--ds-type-weight-bold);\n font-variant-numeric: tabular-nums;\n line-height: calc(var(--ds-stat-lines, 2) * var(--ds-unit));\n min-height: calc(var(--ds-stat-lines, 2) * var(--ds-unit));\n letter-spacing: calc(var(--ds-display-text-tracking, -0.4px) * var(--ds-sf, 1));\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n\n.ds-stat__digit {\n display: inline-block;\n width: var(--ds-stat-digit, 1ch);\n text-align: center;\n}\n\n.ds-stat--danger .ds-stat__figure {\n color: var(--ds-danger-color);\n}\n\n.ds-stat--warning .ds-stat__figure {\n color: var(--ds-warning-color);\n}\n\n.ds-stat__change {\n display: inline-flex;\n flex: none;\n align-items: center;\n height: var(--ds-unit);\n margin-inline-end: calc(4px * var(--ds-sf, 1));\n color: var(--ds-stat-change-color, var(--ds-text-color));\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n}\n\n.ds-stat__change--danger {\n --ds-stat-change-color: var(--ds-danger-color);\n}\n\n.ds-stat__change--warning {\n --ds-stat-change-color: var(--ds-warning-color);\n}\n\n.ds-stats {\n box-sizing: border-box;\n display: grid;\n grid-template-columns: repeat(auto-fill, min(100%, calc(var(--ds-stats-width, 8) * var(--ds-unit))));\n gap: var(--ds-unit);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.ds-stats > li {\n min-width: 0;\n margin: 0;\n}\n/*\n * Progress, as plain classes on native elements: a value against its range,\n * a sequence of steps, or a dot for each page. The `ds-progress` element\n * draws the same parts; controls.css imports this file.\n *\n * Everything sits on the paper with no box, every line is one square and\n * widths come in whole squares. Colour is kept for a state: the danger colour\n * for a value that is over or a step that failed, the ink for the current\n * step and the current dot.\n *\n * A value against its range, one square high (HTML's <progress>):\n *\n * ds-progress the row; role=\"meter\" with aria-valuenow, -min, -max and -valuetext\n * ds-progress__fill the bar, half a square high, drawn to --ds-progress-value (0 to 1)\n * ds-progress--squares the range in squares instead, each one square\n * ds-progress__square one square of the range; --on when it is filled\n * ds-progress--over past the end: filled whole in the danger colour\n *\n * Tokens: `--ds-progress-value`, `--ds-progress-color` (the ink by default)\n * and `--ds-progress-track` (the grey fill).\n *\n * A sequence of steps, one square high, each step on whole squares:\n *\n * ds-progress--steps with ds-progress, on an <ol>; the steps on one row, joined by a line\n * ds-progress--vertical one step to a row, each with room for its body under it\n * ds-progress__step an <li>; aria-current=\"step\" on the current one\n * ds-progress__step--done, --current, --failed states; upcoming without one\n * ds-progress__link a step that goes somewhere: an <a href> around its mark and label\n * ds-progress__mark the number (or the done or failed icon), one control high\n * ds-progress__label the step's name on one line\n * ds-progress__state the state in words for assistive technology, hidden (\"Done\")\n * ds-progress__body what belongs under a step in a vertical sequence\n *\n * A dot for each page, one square each:\n *\n * ds-progress--dots with ds-progress, a group of dots (role=\"group\")\n * ds-progress__dot a button; `data-state` is `past`, `current` (with\n * `aria-current=\"true\"`), `next`, `future`, or `end` for a\n * last dot marked as an end\n *\n * Tokens: the dot colours `--ds-progress-dot-past` and `-future` (the muted\n * text colour by default), `-current`, `-next` and `-end` (the text colour),\n * set on the dots or any element around them. Past, current and next dots are\n * filled; future dots and the end marker are rings.\n */\n\n/* ---- A value against its range: the bar, or the range in squares ---- */\n\n.ds-progress {\n box-sizing: border-box;\n position: relative;\n display: block;\n width: 100%;\n height: var(--ds-unit);\n margin: 0;\n}\n\n/* The range and its bar are a band half a square high across the middle of\n the row, as a chart's ratio is drawn. */\n.ds-progress:not(.ds-progress--squares, .ds-progress--steps, .ds-progress--dots)::before,\n.ds-progress__fill {\n position: absolute;\n inset-inline: 0;\n top: calc(var(--ds-unit) / 4);\n height: calc(var(--ds-unit) / 2);\n}\n\n.ds-progress:not(.ds-progress--squares, .ds-progress--steps, .ds-progress--dots)::before {\n content: \"\";\n background: var(--ds-progress-track, var(--ds-fill));\n}\n\n.ds-progress__fill {\n display: block;\n background: var(--ds-progress-color, var(--ds-text-color));\n transform: scaleX(var(--ds-progress-value, 0));\n transform-origin: left center;\n transition: transform 0.4s cubic-bezier(0.2, 0.8, 0.2, 1);\n}\n\n.ds-progress__fill:dir(rtl) {\n transform-origin: right center;\n}\n\n.ds-progress--over .ds-progress__fill {\n background: var(--ds-danger-color);\n transform: none;\n}\n\n.ds-progress--squares {\n display: flex;\n width: max-content;\n max-width: 100%;\n overflow: hidden;\n}\n\n.ds-progress__square {\n flex: none;\n width: calc(var(--ds-unit) - 1px);\n height: calc(var(--ds-unit) - 1px);\n margin-inline-end: 1px;\n background: var(--ds-progress-track, var(--ds-fill));\n}\n\n.ds-progress__square--on {\n background: var(--ds-progress-color, var(--ds-text-color));\n}\n\n.ds-progress--over .ds-progress__square {\n background: var(--ds-danger-color);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .ds-progress__fill {\n transition: none;\n }\n}\n\n/* ---- A sequence of steps --------------------------------------------- */\n\n.ds-progress--steps {\n box-sizing: border-box;\n position: static;\n display: flex;\n width: auto;\n height: auto;\n flex-wrap: wrap;\n align-items: flex-start;\n min-width: 0;\n margin: 0;\n padding: 0;\n list-style: none;\n color: var(--ds-text-color);\n font-family: var(--ds-typeface-regular);\n font-feature-settings: var(--ds-font-features);\n font-size: var(--ds-type-size-default);\n font-weight: var(--ds-type-weight-default);\n line-height: var(--ds-unit);\n letter-spacing: var(--ds-type-letterspacing-default);\n text-transform: none;\n}\n\n/* Each step rounds its width up to whole squares where the browser can size\n to content in calc; elsewhere it hugs its words. */\n.ds-progress__step {\n --ds-progress-mark: transparent;\n --ds-progress-mark-ink: var(--ds-text-color-muted);\n --ds-progress-mark-line: var(--ds-border-color);\n box-sizing: border-box;\n position: relative;\n display: flex;\n align-items: flex-start;\n min-width: 0;\n max-width: 100%;\n width: max-content;\n width: calc-size(max-content, round(up, size, var(--ds-unit)));\n min-height: var(--ds-unit);\n margin: 0;\n color: var(--ds-text-color-muted);\n}\n\n/* A line joins a step to the next: at least one square, across whatever the\n step's rounding leaves, a quarter of a square short of each end. */\n.ds-progress__step:not(:last-child)::after {\n content: \"\";\n flex: 1 0 var(--ds-unit);\n height: var(--ds-unit);\n background: linear-gradient(var(--ds-border-color), var(--ds-border-color)) center / calc(100% - var(--ds-unit) / 2) 1px no-repeat;\n}\n\n.ds-progress__step--done {\n --ds-progress-mark: var(--ds-fill);\n --ds-progress-mark-ink: var(--ds-text-color);\n --ds-progress-mark-line: transparent;\n color: var(--ds-text-color);\n}\n\n.ds-progress__step--current {\n --ds-progress-mark: var(--ds-text-color);\n --ds-progress-mark-ink: var(--ds-paper);\n --ds-progress-mark-line: transparent;\n color: var(--ds-text-color);\n}\n\n.ds-progress__step--failed {\n --ds-progress-mark: var(--ds-danger-color);\n --ds-progress-mark-ink: light-dark(var(--ds-white), var(--ds-black));\n --ds-progress-mark-line: transparent;\n color: var(--ds-danger-color);\n}\n\n.ds-progress__link {\n display: flex;\n align-items: flex-start;\n min-width: 0;\n color: inherit;\n text-decoration: none;\n border-radius: var(--ds-radius);\n}\n\n.ds-progress__link:hover .ds-progress__label {\n text-decoration: underline;\n text-decoration-thickness: 1px;\n text-underline-offset: calc(3px * var(--ds-sf, 1));\n}\n\n.ds-progress__link:focus-visible {\n outline: 2px solid var(--ds-focus-ring);\n outline-offset: -2px;\n}\n\n.ds-progress__mark {\n box-sizing: border-box;\n display: flex;\n flex: none;\n align-items: center;\n justify-content: center;\n width: var(--ds-control-height);\n height: var(--ds-control-height);\n margin: calc((var(--ds-unit) - var(--ds-control-height)) / 2);\n overflow: hidden;\n background: var(--ds-progress-mark);\n box-shadow: inset 0 0 0 1px var(--ds-progress-mark-line);\n color: var(--ds-progress-mark-ink);\n --ds-icon-color: var(--ds-progress-mark-ink);\n font-family: var(--ds-typeface-medium);\n font-size: var(--ds-type-size-small);\n font-weight: var(--ds-type-weight-bold);\n font-variant-numeric: tabular-nums;\n line-height: var(--ds-control-height);\n letter-spacing: 0;\n}\n\n.ds-progress__label {\n min-width: 0;\n padding-inline-end: calc(4px * var(--ds-sf, 1));\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n\n.ds-progress__step--current .ds-progress__label {\n font-family: var(--ds-typeface-medium);\n font-weight: var(--ds-type-weight-bold);\n}\n\n.ds-progress__state {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n}\n\n.ds-progress--vertical {\n flex-direction: column;\n flex-wrap: nowrap;\n}\n\n.ds-progress--vertical .ds-progress__step {\n flex-wrap: wrap;\n width: 100%;\n}\n\n.ds-progress--vertical .ds-progress__step::after {\n content: none;\n}\n\n/* Its content stands in a column, so an inline element in it (text drawn by\n an element, say) takes its own lines rather than a line box's descent. */\n.ds-progress__body {\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n flex: 1 0 100%;\n min-width: 0;\n padding-inline-start: var(--ds-unit);\n color: var(--ds-text-color);\n}\n\n/* ---- A dot for each page ------------------------------------------- */\n\n.ds-progress--dots {\n position: static;\n display: inline-flex;\n width: auto;\n flex: none;\n align-items: center;\n height: var(--ds-row-height);\n margin: 0;\n padding: 0;\n}\n\n.ds-progress__dot {\n --ds-progress-dot-color: var(--ds-progress-dot-future, var(--ds-text-color-muted));\n appearance: none;\n box-sizing: border-box;\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n width: var(--ds-unit);\n height: var(--ds-unit);\n margin: 0;\n padding: 0;\n border: 0;\n border-radius: var(--ds-radius);\n background: transparent;\n color: inherit;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n}\n\n/* The dot itself: 4px, scaled with the surface, a fifth of the square. */\n.ds-progress__dot::before {\n content: \"\";\n box-sizing: border-box;\n width: calc(4px * var(--ds-sf, 1));\n height: calc(4px * var(--ds-sf, 1));\n border-radius: 50%;\n background: transparent;\n box-shadow: inset 0 0 0 1px var(--ds-progress-dot-color);\n transition: transform 160ms ease-out;\n}\n\n.ds-progress__dot[data-state=\"past\"] { --ds-progress-dot-color: var(--ds-progress-dot-past, var(--ds-text-color-muted)); }\n.ds-progress__dot[data-state=\"next\"] { --ds-progress-dot-color: var(--ds-progress-dot-next, var(--ds-text-color)); }\n.ds-progress__dot[data-state=\"end\"] { --ds-progress-dot-color: var(--ds-progress-dot-end, var(--ds-text-color)); }\n.ds-progress__dot[aria-current=\"true\"] { --ds-progress-dot-color: var(--ds-progress-dot-current, var(--ds-text-color)); }\n\n.ds-progress__dot:is([data-state=\"past\"], [data-state=\"next\"], [aria-current=\"true\"])::before {\n background: var(--ds-progress-dot-color);\n}\n\n/* The current dot is half again as large. */\n.ds-progress__dot[aria-current=\"true\"]::before {\n transform: scale(1.5);\n}\n\n.ds-progress__dot:hover:not([aria-current=\"true\"])::before {\n transform: scale(1.25);\n}\n\n.ds-progress__dot:focus-visible {\n outline: 2px solid var(--ds-focus-ring);\n outline-offset: -2px;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .ds-progress__dot::before {\n transition: none;\n }\n}\n/*\n * Rows, as plain classes on native elements: the `ds-row` and `ds-sidebar`\n * elements' look without the elements.\n *\n * A row is one square high, and what it holds stands on the lower sixteen of\n * the square with the spare four above, as Ezo sets every row: a line of\n * words on a 16px line box, a 16px icon and a 16px control all end on the\n * row's bottom line, so the capitals (7px below the top line, baseline 3px\n * above the next) are centred on an icon beside them. On an `<a href>` or a\n * `<button>` the whole row is one press target, a row, a tile or a card: the pointer over it, the\n * press and the focus ring cover the row, and its content keeps the row's\n * layout. A row that also holds controls of its own (a quick action, a link\n * in another cell) is any element, a `<li>`, a `<div>` or a `<tr>`, with one\n * `ds-row__target` inside: that link or button takes the press anywhere on\n * the row, and every other control inside stays separately pressable.\n *\n * ds-row a row one square high; a press target on <a> or <button>\n * ds-row__target the row's press target, when the row holds other controls\n * ds-row--auto as high as its content, at least one square\n * ds-row--stacked the content stacked, each line one square (a tile, a card)\n * ds-row--nav a navigation row: the sidebar's two-pixel corners\n * aria-current=\"page\" the destination being shown, filled and in the medium weight\n * aria-disabled=\"true\" or `disabled` on a button: dimmed, not pressable\n *\n * A sidebar of nested things (the `ds-sidebar tree` element without Lit) is a\n * `role=\"tree\"` list of `role=\"treeitem\"` rows, flat, each with its\n * `aria-level`, one square further in per level. `0-face/sidebar.ts`\n * (`connectSidebar`) gives it the keyboard of the tree pattern.\n *\n * ds-sidebar the tree (`role=\"tree\"`)\n * ds-sidebar__item a row (`role=\"treeitem\"`, `aria-level`, `aria-expanded` on a parent, `aria-selected`)\n * ds-sidebar__disclosure the arrow on its own square, turned down when the row is open; empty on a leaf\n * ds-sidebar__label the words, cut short with an ellipsis\n * ds-sidebar__meta small muted words at the row's end\n *\n * An icon (`.ds-icon`) in a tree row takes a square of its own. Tokens:\n * `--ds-row-padding` (the words' distance from the row's edges, 4px),\n * `--ds-row-fill-hover`, `--ds-row-fill-press`, `--ds-row-fill-current` and\n * `--ds-row-current-weight` (component-tokens.css, shared with the\n * element), `--ds-sidebar-disclosure` (the arrow's drawing, the library's\n * `right` icon) and `--ds-sidebar-indent` (a row's depth in squares, read\n * from `aria-level` up to twelve levels).\n */\n\n:where(.ds-row):not(tr) {\n box-sizing: border-box;\n display: flex;\n flex-direction: row;\n align-items: flex-end;\n gap: calc(4px * var(--ds-sf));\n min-width: 0;\n height: var(--ds-row-height);\n margin: 0;\n line-height: var(--ds-type-lineheight-default);\n}\n\n:where(.ds-row--auto):not(tr) {\n height: auto;\n min-height: var(--ds-row-height);\n}\n\n:where(.ds-row--stacked):not(tr) {\n flex-direction: column;\n align-items: flex-start;\n justify-content: flex-start;\n gap: 0;\n height: auto;\n}\n\n/* The press target: the row itself, or the one target inside it. */\na.ds-row,\nbutton.ds-row,\n.ds-row__target {\n appearance: none;\n box-sizing: border-box;\n margin: 0;\n border: 0;\n background: none;\n color: inherit;\n font: inherit;\n letter-spacing: inherit;\n text-align: start;\n text-decoration: none;\n text-transform: none;\n cursor: pointer;\n outline: none;\n -webkit-tap-highlight-color: transparent;\n}\n\n/* The font inherited above would bring the page's line into the row: the words go back on the\n sixteen, as every row's (a stacked tile keeps its square lines below). */\na.ds-row,\nbutton.ds-row {\n position: relative;\n width: 100%;\n line-height: var(--ds-type-lineheight-default);\n padding: 0 var(--ds-row-padding, calc(4px * var(--ds-sf)));\n border-radius: var(--ds-row-radius, 0);\n}\n\n.ds-row__target {\n padding: 0;\n}\n\n/* Each line of a tile one square, over the inherited font of a native link or button:\n a tile's lines wrap, so they stay on whole squares rather than the sixteen. */\n.ds-row.ds-row--stacked {\n line-height: var(--ds-row-height);\n}\n\n/* A row with a target inside: the target's layers cover the row. */\n.ds-row:has(.ds-row__target) {\n position: relative;\n isolation: isolate;\n}\n:where(.ds-row:has(.ds-row__target)):not(tr) {\n padding-inline: var(--ds-row-padding, calc(4px * var(--ds-sf)));\n}\n.ds-row__target::before,\n.ds-row__target::after {\n content: \"\";\n position: absolute;\n inset: 0;\n border-radius: var(--ds-row-radius, 0);\n}\n.ds-row__target::before { z-index: 1; }\n.ds-row__target::after { z-index: -1; }\n.ds-row:has(.ds-row__target) :where(a[href], button, input, select, textarea, label, summary, [tabindex]):not(.ds-row__target) {\n position: relative;\n z-index: 2;\n}\n\n/*\n * The pointer, the press, the current destination and the focus ring are one layer under the\n * row's content (Joachim, 19 October 2026). On a one-square row it is a square high and sits two\n * pixels around what the row holds: from two above the lower sixteen to two below the row's foot,\n * and two past the content at each side, its corners the icon tiles' 4px and those two more, so a\n * 16px icon stands in it with two all round. It is drawn over the row, never moving its words or\n * icons off the grid. An open row (`data-open`) wears the same layer. A taller row (a stacked\n * tile, `auto`) is filled whole.\n * Tokens: `--ds-row-fill-radius` (6px), `--ds-row-fill-overhang` (2px).\n */\na.ds-row,\nbutton.ds-row {\n isolation: isolate;\n}\na.ds-row::after,\nbutton.ds-row::after {\n content: \"\";\n position: absolute;\n z-index: -1;\n pointer-events: none;\n}\na.ds-row::after,\nbutton.ds-row::after,\n.ds-row__target::after {\n --ds-row-overhang: var(--ds-row-fill-overhang, calc(2px * var(--ds-sf)));\n inset:\n calc(var(--ds-row-height) - var(--ds-control-height) - var(--ds-row-overhang))\n calc(var(--ds-row-padding, calc(4px * var(--ds-sf))) - var(--ds-row-overhang))\n calc(0px - var(--ds-row-overhang));\n border-radius: var(--ds-row-radius, var(--ds-row-fill-radius, calc(6px * var(--ds-sf))));\n}\n:is(a, button).ds-row:is(.ds-row--stacked, .ds-row--auto)::after,\n:is(.ds-row--stacked, .ds-row--auto) .ds-row__target::after {\n inset: 0;\n}\na.ds-row[href]:hover::after,\nbutton.ds-row:hover:not(:disabled)::after,\n.ds-row__target:is(a[href], button:not(:disabled)):hover::after {\n background: var(--ds-row-fill-hover, var(--ds-fill));\n}\na.ds-row[href]:active::after,\nbutton.ds-row:active:not(:disabled)::after,\n.ds-row__target:is(a[href], button:not(:disabled)):active::after {\n background: var(--ds-row-fill-press, var(--ds-fill-hover));\n}\n.ds-row:is(a, button)[aria-current]:not([aria-current=\"false\"])::after,\n.ds-row__target[aria-current]:not([aria-current=\"false\"])::after {\n background: var(--ds-row-fill-current, var(--ds-fill-hover));\n}\n.ds-row:is(a, button)[aria-current]:not([aria-current=\"false\"]),\n.ds-row:has(.ds-row__target[aria-current]:not([aria-current=\"false\"])) {\n --ds-type-weight-default: var(--ds-row-current-weight, 500);\n font-weight: var(--ds-row-current-weight, 500);\n}\na.ds-row:focus-visible::after,\nbutton.ds-row:focus-visible::after,\n.ds-row__target:focus-visible::after {\n outline: 2px solid var(--ds-focus-ring);\n outline-offset: -2px;\n}\na.ds-row:not([href]),\na.ds-row[aria-disabled=\"true\"],\nbutton.ds-row:disabled,\nbutton.ds-row[aria-disabled=\"true\"],\n.ds-row:has(.ds-row__target:is(:disabled, [aria-disabled=\"true\"], a:not([href]))) {\n cursor: default;\n opacity: 0.5;\n}\n.ds-row__target:is(:disabled, [aria-disabled=\"true\"], a:not([href])) {\n cursor: default;\n}\na.ds-row[aria-disabled=\"true\"]:hover::after,\nbutton.ds-row[aria-disabled=\"true\"]:hover::after {\n background: none;\n}\n\n.ds-row--nav {\n --ds-row-radius: calc(2px * var(--ds-sf));\n}\n\n/* A sidebar of nested things: the tree. */\n\n.ds-sidebar {\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n min-width: 0;\n margin: 0;\n padding: 0;\n list-style: none;\n color: var(--ds-text-color);\n font-family: var(--ds-typeface-regular);\n font-feature-settings: var(--ds-font-features);\n font-size: var(--ds-type-size-default);\n font-weight: var(--ds-type-weight-default);\n line-height: var(--ds-type-lineheight-default);\n letter-spacing: var(--ds-type-letterspacing-default);\n}\n\n.ds-sidebar__item {\n box-sizing: border-box;\n display: flex;\n align-items: flex-end;\n height: var(--ds-row-height);\n min-width: 0;\n margin: 0;\n padding-block: 0;\n padding-inline: calc(var(--ds-sidebar-indent, 0) * var(--ds-unit)) var(--ds-row-padding, calc(4px * var(--ds-sf)));\n white-space: nowrap;\n cursor: pointer;\n user-select: none;\n outline: none;\n -webkit-tap-highlight-color: transparent;\n}\n.ds-sidebar__item[hidden] { display: none; }\n.ds-sidebar__item[aria-level=\"2\"] { --ds-sidebar-indent: 1; }\n.ds-sidebar__item[aria-level=\"3\"] { --ds-sidebar-indent: 2; }\n.ds-sidebar__item[aria-level=\"4\"] { --ds-sidebar-indent: 3; }\n.ds-sidebar__item[aria-level=\"5\"] { --ds-sidebar-indent: 4; }\n.ds-sidebar__item[aria-level=\"6\"] { --ds-sidebar-indent: 5; }\n.ds-sidebar__item[aria-level=\"7\"] { --ds-sidebar-indent: 6; }\n.ds-sidebar__item[aria-level=\"8\"] { --ds-sidebar-indent: 7; }\n.ds-sidebar__item[aria-level=\"9\"] { --ds-sidebar-indent: 8; }\n.ds-sidebar__item[aria-level=\"10\"] { --ds-sidebar-indent: 9; }\n.ds-sidebar__item[aria-level=\"11\"] { --ds-sidebar-indent: 10; }\n.ds-sidebar__item[aria-level=\"12\"] { --ds-sidebar-indent: 11; }\n.ds-sidebar__item:hover:not([aria-disabled=\"true\"]) { background: var(--ds-row-fill-hover, var(--ds-fill)); }\n.ds-sidebar__item:active:not([aria-disabled=\"true\"]) { background: var(--ds-row-fill-press, var(--ds-fill-hover)); }\n.ds-sidebar__item[aria-selected=\"true\"] {\n --ds-type-weight-default: var(--ds-row-current-weight, 500);\n background: var(--ds-row-fill-current, var(--ds-fill-hover));\n font-weight: var(--ds-row-current-weight, 500);\n}\n.ds-sidebar__item:focus-visible {\n outline: 2px solid var(--ds-focus-ring);\n outline-offset: -2px;\n}\n.ds-sidebar__item[aria-disabled=\"true\"] {\n cursor: default;\n opacity: 0.5;\n}\n\n/* The arrow and an icon each stand on a whole square, drawn on the row's sixteen (two\n pixels below the square's middle); a leaf keeps the arrow's square empty. */\n.ds-sidebar__disclosure,\n.ds-sidebar__item > .ds-icon {\n flex: 0 0 var(--ds-unit);\n width: var(--ds-unit);\n height: var(--ds-unit);\n -webkit-mask-size: var(--ds-icon-box, calc(14px * var(--ds-sf)));\n mask-size: var(--ds-icon-box, calc(14px * var(--ds-sf)));\n -webkit-mask-position: center calc(50% + 2px * var(--ds-sf));\n mask-position: center calc(50% + 2px * var(--ds-sf));\n}\n.ds-sidebar__disclosure {\n --ds-icon-box: calc(14px * var(--ds-sf));\n display: inline-block;\n color: var(--ds-text-color-muted);\n background-color: currentColor;\n -webkit-mask: var(--ds-sidebar-disclosure, url(\"data:image/svg+xml,%3Csvg xmlns='http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg' viewBox='0 0 16 16'%3E%3Cpath d='M13.1566 8.60594H0V9.61651L13.1562 9.61791L10.249 12.5251L10.9558 13.219L15.0636 9.11123L10.9524 5L10.245 5.70734L13.1566 8.60594Z'/%3E%3C/svg%3E\")) center calc(50% + 2px * var(--ds-sf)) / var(--ds-icon-box) no-repeat;\n mask: var(--ds-sidebar-disclosure, url(\"data:image/svg+xml,%3Csvg xmlns='http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg' viewBox='0 0 16 16'%3E%3Cpath d='M13.1566 8.60594H0V9.61651L13.1562 9.61791L10.249 12.5251L10.9558 13.219L15.0636 9.11123L10.9524 5L10.245 5.70734L13.1566 8.60594Z'/%3E%3C/svg%3E\")) center calc(50% + 2px * var(--ds-sf)) / var(--ds-icon-box) no-repeat;\n transform: rotate(0deg);\n transition: transform 120ms ease-out;\n}\n.ds-sidebar__item:not([aria-expanded]) > .ds-sidebar__disclosure {\n visibility: hidden;\n}\n.ds-sidebar__item[aria-expanded=\"true\"] > .ds-sidebar__disclosure {\n transform: rotate(90deg);\n}\n.ds-sidebar__label {\n flex: 1 1 auto;\n min-width: 0;\n padding-inline-start: calc(4px * var(--ds-sf));\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ds-sidebar__meta {\n flex: none;\n padding-inline-start: calc(4px * var(--ds-sf));\n color: var(--ds-text-color-muted);\n font-size: var(--ds-type-size-small);\n font-variant-numeric: tabular-nums;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .ds-sidebar__disclosure { transition: none; }\n}\n\n@media (forced-colors: active) {\n .ds-row:is(a, button)[aria-current]:not([aria-current=\"false\"]),\n .ds-row__target[aria-current]:not([aria-current=\"false\"])::after,\n .ds-sidebar__item[aria-selected=\"true\"] {\n outline: 1px solid Highlight;\n outline-offset: -1px;\n }\n .ds-sidebar__disclosure {\n background-color: CanvasText;\n }\n}\n\n/* A controlled detail view: the caller marks its open row, and clears it on close.\n Navigation's current state and the text weight remain independent. */\n.ds-row[data-open]:not([data-open=\"false\"]) {\n position: relative;\n isolation: isolate;\n}\n.ds-row[data-open]:not([data-open=\"false\"])::after {\n content: \"\";\n position: absolute;\n z-index: -1;\n pointer-events: none;\n --ds-row-overhang: var(--ds-row-fill-overhang, calc(2px * var(--ds-sf)));\n inset: calc(var(--ds-row-height) - var(--ds-control-height) - var(--ds-row-overhang))\n calc(var(--ds-row-padding, calc(4px * var(--ds-sf))) - var(--ds-row-overhang))\n calc(0px - var(--ds-row-overhang));\n border-radius: var(--ds-row-radius, var(--ds-row-fill-radius, calc(6px * var(--ds-sf))));\n background: var(--ds-row-fill-current, var(--ds-fill-hover));\n}\n/* A plain row (no press target inside) has no padding of its own: its words and icons stand on\n the row's edges, so the layer reaches two past them, as a flush row's does. */\n.ds-row[data-open]:not([data-open=\"false\"]):not(a, button, .ds-row--stacked, .ds-row--auto, :has(.ds-row__target))::after {\n inset: calc(var(--ds-row-height) - var(--ds-control-height) - var(--ds-row-overhang))\n calc(0px - var(--ds-row-overhang))\n calc(0px - var(--ds-row-overhang));\n}\n.ds-row[data-open]:is(.ds-row--stacked, .ds-row--auto)::after { inset: 0; }\n@media (forced-colors: active) {\n .ds-row[data-open]:not([data-open=\"false\"])::after { outline: 1px solid Highlight; outline-offset: -1px; }\n}\n/*\n * `.ds-context` is the `ds-context` element's look on a native menu, for a page\n * without Lit or a menu a library draws: Ezo's gap face. The rows carry the\n * colour and the menu only cuts the outer silhouette, so the page shows\n * through a pixel between rows and two between groups. The rows are black\n * with white words in both themes, as Ezo's menus always were (the slate in\n * dark, darker than the page). Every row is 19px with the pixel under it, a square's pitch, its words\n * on the sixteen and an icon at its end. A Select's menu is the light face,\n * `.ds-menu`; a Context's is this dark one.\n *\n * ds-context the menu (`role=\"menu\"`); its rows are its buttons,\n * links and menu items, or `ds-context__item`\n * ds-context__item a row; `--danger` (or `data-danger`) in red\n * ds-context__label a heading row, muted\n * ds-context__shortcut a shortcut at the row's end, muted\n * ds-context__separator a group gap (or an `hr`, or `role=\"separator\"`):\n * never a line; first, last or doubled it is quiet\n * [data-ds-refused] a refused change: the row turns red\n *\n * The page places and opens it (a popover at the pointer, say) and owns its\n * keys; the faces of hover, focus, disabled and the wait of a pending menu\n * (`aria-busy=\"true\"`) are here. Tokens as the element's: --ds-context-fill,\n * --ds-context-ink, --ds-context-hover, --ds-context-danger,\n * --ds-context-refused, --ds-context-radius.\n */\n\n.ds-context {\n --ds-context-row: calc(var(--ds-row-height) - 1px);\n /* Black in both themes, as Ezo's menus always were: in dark the slate, darker than the page. */\n --ds-context-fill: light-dark(var(--ds-black, #2a2a2a), var(--ds-slate, #1e1e1e));\n --ds-context-ink: var(--ds-white, #ffffff);\n --ds-context-hover: color-mix(in srgb, var(--ds-context-fill) 78%, var(--ds-context-ink));\n --ds-context-danger: var(--ds-tuned-red);\n --ds-context-refused: var(--ds-tuned-red);\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n gap: 1px;\n width: var(--ds-menu-width, max-content);\n min-width: calc(4 * var(--ds-unit));\n max-height: var(--ds-menu-max-height, calc(16 * var(--ds-unit)));\n overflow-y: auto;\n margin: 0;\n padding: 0;\n border: 0;\n background: transparent;\n box-shadow: none;\n clip-path: inset(0 round var(--ds-context-radius, var(--ds-radius, 4px)));\n color: var(--ds-context-ink);\n font-family: var(--ds-typeface-regular);\n font-feature-settings: var(--ds-font-features);\n font-size: var(--ds-type-size-default);\n font-weight: var(--ds-type-weight-default);\n line-height: var(--ds-type-lineheight-default);\n letter-spacing: var(--ds-type-letterspacing-default);\n list-style: none;\n}\n\n.ds-context > [role=\"group\"] {\n display: contents;\n}\n\n/* A row: whatever the page or library styles its buttons with, one row on the sixteen. */\n.ds-context :is(.ds-context__item, :is(.ds-context, .ds-context > [role=\"group\"]) > :is(button, a, li, [role^=\"menuitem\"]):not(.ds-context__label, .ds-context__separator, [role=\"separator\"])) {\n box-sizing: border-box !important;\n display: flex !important;\n flex: 0 0 auto !important;\n align-items: flex-end !important;\n justify-content: space-between !important;\n gap: calc(4px * var(--ds-sf));\n width: 100%;\n height: var(--ds-context-row) !important;\n min-height: var(--ds-context-row) !important;\n max-height: var(--ds-context-row) !important;\n margin: 0 !important;\n padding: 0 calc(4px * var(--ds-sf)) !important;\n border: 0 !important;\n border-radius: 0 !important;\n background: var(--ds-context-fill) !important;\n color: var(--ds-context-ink) !important;\n font: inherit;\n line-height: var(--ds-type-lineheight-default) !important;\n letter-spacing: inherit;\n text-align: start;\n text-decoration: none;\n white-space: nowrap;\n cursor: pointer;\n outline: none;\n appearance: none;\n -webkit-tap-highlight-color: transparent;\n}\n\n.ds-context :is(.ds-context__item, :is(.ds-context, .ds-context > [role=\"group\"]) > :is(button, a, li, [role^=\"menuitem\"])):is(:hover, :focus-visible, [data-highlighted]):not(:disabled, [aria-disabled=\"true\"], [data-disabled]) {\n background: var(--ds-context-hover) !important;\n}\n\n.ds-context :is(.ds-context__item, :is(.ds-context, .ds-context > [role=\"group\"]) > :is(button, a, li, [role^=\"menuitem\"])):focus-visible {\n box-shadow: inset 0 0 0 2px var(--ds-focus-ring);\n}\n\n.ds-context :is(.ds-context__item, :is(.ds-context, .ds-context > [role=\"group\"]) > :is(button, a, li, [role^=\"menuitem\"])):is(:disabled, [aria-disabled=\"true\"], [data-disabled]) {\n cursor: default;\n opacity: 0.5;\n}\n\n.ds-context[aria-busy=\"true\"] > * {\n cursor: wait !important;\n}\n\n/* Danger is red, on either fill. */\n.ds-context :is(.ds-context__item, :is(.ds-context, .ds-context > [role=\"group\"]) > :is(button, a, li, [role^=\"menuitem\"])):is(.ds-context__item--danger, [data-danger], [data-variant=\"destructive\"]) {\n color: var(--ds-context-danger) !important;\n}\n\n/* A refused change: its row turns red a moment. */\n.ds-context :is(.ds-context__item, :is(.ds-context, .ds-context > [role=\"group\"]) > :is(button, a, li, [role^=\"menuitem\"]))[data-ds-refused] {\n background: var(--ds-context-refused) !important;\n}\n\n/* Groups are a gap, never a line: the separator takes nothing but the gap after it. */\n.ds-context > :is(hr, .ds-context__separator, [role=\"separator\"]) {\n flex: 0 0 0 !important;\n height: 0 !important;\n margin: 0 !important;\n padding: 0 !important;\n border: 0 !important;\n background: none !important;\n}\n\n/* A group left empty leaves no gap. */\n.ds-context > :is(hr, .ds-context__separator, [role=\"separator\"]):is(:first-child, :last-child),\n.ds-context > :is(hr, .ds-context__separator, [role=\"separator\"]) + :is(hr, .ds-context__separator, [role=\"separator\"]) {\n display: none !important;\n}\n\n.ds-context :is(.ds-context__label, [data-ds-menu-label]) {\n box-sizing: border-box;\n height: var(--ds-context-row);\n /* The spare three above the sixteen. */\n padding: calc(var(--ds-context-row) - var(--ds-type-lineheight-default)) calc(4px * var(--ds-sf)) 0;\n background: var(--ds-context-fill);\n color: color-mix(in srgb, var(--ds-context-ink) 60%, var(--ds-context-fill));\n line-height: var(--ds-type-lineheight-default);\n white-space: nowrap;\n}\n\n.ds-context__shortcut {\n margin-left: auto;\n color: color-mix(in srgb, var(--ds-context-ink) 60%, var(--ds-context-fill));\n}\n\n/*\n * Native controls in shared dress.\n *\n * DS one keeps a few components as plain classes on native elements. shared does\n * the same for everything a person types into or picks from: text fields,\n * selects and checkboxes stay `<input>` and `<select>`, so labels, form\n * semantics and React's controlled inputs work as they always have.\n *\n * `.ds-button` is the `ds-button` element's look on a native `<button>`,\n * for a button inside a drag handle. The drag library recognises only native\n * controls: inside a handle it would lift the row on Space where a\n * `ds-button` was meant to be pressed. Both read the same custom properties.\n * On an `<a href>` it is a link drawn as a button (see `a.ds-button` below).\n */\n\n.ds-button {\n appearance: none;\n box-sizing: border-box;\n display: inline-flex;\n /* At the foot of a line, so the 16px face takes the lower sixteen of the square. */\n vertical-align: bottom;\n flex: 0 0 auto;\n width: var(--ds-button-width, max-content);\n align-items: center;\n justify-content: var(--ds-button-align, flex-start);\n gap: var(--ds-button-gap, calc(4px * var(--ds-sf)));\n height: var(--ds-button-height, var(--ds-control-height));\n margin: 0;\n padding: var(--ds-button-padding, 0);\n border: 0;\n border-radius: var(--ds-radius);\n background: var(--ds-button-background, transparent);\n color: var(--ds-button-color, var(--ds-button-text-color));\n font-family: var(--ds-typeface-regular);\n font-feature-settings: var(--ds-font-features);\n font-size: var(--ds-type-size-default);\n line-height: var(--ds-type-lineheight-default);\n letter-spacing: var(--ds-type-letterspacing-default);\n white-space: nowrap;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n}\n\n.ds-button--icon {\n justify-content: center;\n width: var(--ds-button-height, var(--ds-control-height));\n padding: 0;\n color: var(--ds-button-color, var(--ds-text-color-muted));\n}\n\n.ds-button--icon:hover:not(:disabled, [aria-disabled=\"true\"]) {\n --ds-button-color: var(--ds-text-color);\n}\n\n.ds-button:focus-visible {\n outline: 2px solid var(--ds-focus-ring);\n outline-offset: 1px;\n}\n\n.ds-button:disabled,\n.ds-button[aria-disabled=\"true\"] {\n cursor: default;\n opacity: 0.5;\n}\n\n/*\n * one.css carries older button rules that dim a button on hover and nudge it\n * on press. The shared button does neither: these match their specificity, so\n * controls.css, loaded after one.css, wins.\n */\n.ds-button:hover:not(:disabled, [aria-disabled=\"true\"]) {\n filter: none;\n}\n\n.ds-button:active:not(:disabled, [aria-disabled=\"true\"]) {\n transform: none;\n}\n\n/*\n * `a.ds-button` is a link drawn as a button. The browser keeps the link:\n * middle-click, a new tab, Copy link and Enter. It never takes the link's\n * underline or colours, in any state, so every face (primary, secondary,\n * icon, tabs) looks as it does on a `<button>`. A disabled link has no `href`\n * and carries `aria-disabled=\"true\"` and `role=\"link\"`.\n */\na.ds-button,\na.ds-button:is(:hover, :focus, :active) {\n text-decoration: none;\n}\n\na.ds-button:is(:link, :visited) {\n color: var(--ds-button-color, var(--ds-button-text-color));\n}\n\na.ds-button.ds-button--icon:is(:link, :visited) {\n color: var(--ds-button-color, var(--ds-text-color-muted));\n}\n\na.ds-button[aria-disabled=\"true\"] {\n pointer-events: none;\n}\n\n/*\n * `.ds-link` is a link in running text: the text's ink, underlined by a\n * hairline 2px below the letters (as a toast's actions are), which turns to the full ink under\n * the pointer. A visited link is drawn in the muted ink. Words keep their\n * sentence case. Tokens: `--ds-link-color`, `--ds-link-visited-color`,\n * `--ds-link-line-color`, `--ds-link-hover-line-color`.\n */\n.ds-link {\n color: var(--ds-link-color, currentColor);\n text-decoration-line: underline;\n text-decoration-thickness: 1px;\n text-decoration-color: var(--ds-link-line-color, color-mix(in srgb, currentColor 40%, transparent));\n text-underline-offset: calc(2px * var(--ds-sf));\n cursor: pointer;\n}\n\n.ds-link:visited {\n color: var(--ds-link-visited-color, var(--ds-text-color-muted));\n}\n\n.ds-link:hover {\n text-decoration-color: var(--ds-link-hover-line-color, currentColor);\n}\n\n/* Quiet navigation and related-component lists: no underline in any state. */\n.ds-link.ds-link--plain {\n text-decoration: none;\n}\n\n.ds-link:focus-visible {\n outline: 2px solid var(--ds-focus-ring);\n outline-offset: 1px;\n}\n\n.ds-link[aria-disabled=\"true\"] {\n cursor: default;\n opacity: 0.5;\n pointer-events: none;\n}\n\n.ds-input,\n.ds-select {\n box-sizing: border-box;\n min-width: 0;\n height: var(--ds-input-height, var(--ds-control-height));\n margin: 0;\n padding: 0 calc(4px * var(--ds-sf));\n border: 0;\n border-radius: var(--ds-radius);\n background: var(--ds-fill);\n color: var(--ds-text-color);\n font-family: var(--ds-typeface-regular);\n font-feature-settings: var(--ds-font-features);\n font-size: var(--ds-type-size-default);\n line-height: var(--ds-type-lineheight-default);\n letter-spacing: var(--ds-type-letterspacing-default);\n outline: none;\n}\n\n.ds-input::placeholder {\n color: var(--ds-title-color);\n}\n\n.ds-input:focus-visible,\n.ds-select:focus-visible,\n.ds-checkbox:focus-visible,\n.ds-radio:focus-visible,\n.ds-switch:focus-visible {\n outline: 2px solid var(--ds-focus-ring);\n outline-offset: 1px;\n}\n\n.ds-input:disabled,\n.ds-select:disabled,\n.ds-checkbox:disabled,\n.ds-radio:disabled,\n.ds-switch:disabled {\n cursor: default;\n opacity: 0.5;\n}\n\n.ds-select {\n appearance: none;\n padding-right: calc(16px * var(--ds-sf));\n background-image:\n linear-gradient(45deg, transparent 50%, currentColor 50%),\n linear-gradient(135deg, currentColor 50%, transparent 50%);\n background-position:\n calc(100% - 9px) 50%,\n calc(100% - 6px) 50%;\n background-size:\n 3px 3px,\n 3px 3px;\n background-repeat: no-repeat;\n cursor: pointer;\n}\n\n/* A multiline field, `ds-input` on a <textarea>, spans whole rows: --ds-textarea-rows sets how many, three by default. */\ntextarea.ds-input {\n display: block;\n height: var(--ds-textarea-height, calc(var(--ds-textarea-rows, 3) * var(--ds-row-height)));\n min-height: var(--ds-row-height);\n padding: var(--ds-textarea-padding, calc(4px * var(--ds-sf)));\n border-radius: var(--ds-textarea-radius, calc(4px * var(--ds-sf)));\n resize: vertical;\n}\n\n/*\n * `.ds-checkbox` and `.ds-radio` are the `ds-checkbox` and `ds-radio` elements'\n * faces on a native `<input>`: one square on the field's ground with a 1px\n * line. The checkbox has the corners of a control and fills with the accent,\n * the check (`x-icon/check.svg`) in its ink; the radio is a circle with a dot\n * half a square across in the text colour.\n */\n.ds-checkbox,\n.ds-radio {\n appearance: none;\n box-sizing: border-box;\n display: inline-grid;\n flex: 0 0 auto;\n place-items: center;\n vertical-align: middle;\n width: var(--ds-row-height);\n height: var(--ds-row-height);\n margin: 0;\n padding: 0;\n border: 1px solid var(--ds-border-color);\n background: var(--ds-input-background);\n cursor: pointer;\n}\n\n.ds-checkbox {\n --ds-checkbox-mark: url(\"data:image/svg+xml,%3Csvg%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%3Cpath%20d%3D%22M6.68649%2011.884L12.443%206.13601L11.736%205.42715L6.68835%2010.4827L4.26399%208.0501L3.55696%208.74525L6.68649%2011.884Z%22%20fill%3D%22currentColor%22%2F%3E%0A%3C%2Fsvg%3E\");\n border-radius: var(--ds-radius);\n corner-shape: var(--ds-corner-shape, round);\n color: var(--ds-button-text-color-on-accent);\n}\n\n.ds-checkbox:is(:checked, :indeterminate) {\n background: var(--ds-accent-color);\n}\n\n.ds-checkbox::before,\n.ds-radio::before {\n content: \"\";\n opacity: 0;\n background: currentColor;\n}\n\n.ds-checkbox::before {\n width: calc(0.8 * var(--ds-row-height));\n height: calc(0.8 * var(--ds-row-height));\n mask: var(--ds-checkbox-mark) center / contain no-repeat;\n}\n\n.ds-checkbox:indeterminate::before {\n width: calc(var(--ds-row-height) / 2);\n height: calc(2px * var(--ds-sf));\n mask: none;\n}\n\n.ds-radio {\n border-radius: 50%;\n color: var(--ds-text-color);\n}\n\n.ds-radio::before {\n width: calc(var(--ds-row-height) / 2);\n height: calc(var(--ds-row-height) / 2);\n border-radius: 50%;\n}\n\n.ds-checkbox:is(:checked, :indeterminate)::before,\n.ds-radio:checked::before {\n opacity: 1;\n}\n\n/*\n * `.ds-switch` is the `ds-switch` element's look on a native\n * `<button role=\"switch\" aria-checked>`: two control heights wide, its thumb\n * one control high less a 2px inset, travelling one control height.\n * `.ds-switch--rounded` is the round variant with a white thumb.\n */\n\n.ds-switch {\n --ds-choice-unit: var(--ds-control-height);\n appearance: none;\n box-sizing: border-box;\n position: relative;\n display: inline-block;\n flex: 0 0 auto;\n vertical-align: middle;\n width: calc(2 * var(--ds-choice-unit));\n height: var(--ds-choice-unit);\n margin: 0;\n padding: calc(2px * var(--ds-sf));\n border: 0;\n border-radius: var(--ds-radius);\n background: var(--ds-fill);\n color: var(--ds-text-color-muted);\n cursor: pointer;\n touch-action: manipulation;\n}\n\n.ds-switch::after {\n content: \"\";\n display: block;\n width: calc(var(--ds-choice-unit) - 4px * var(--ds-sf));\n height: 100%;\n border-radius: var(--ds-radius);\n background: currentColor;\n transform: translateX(0);\n transition: transform 140ms ease;\n}\n\n.ds-switch[aria-checked=\"true\"] {\n background: var(--ds-accent-color);\n color: var(--ds-button-text-color-on-accent);\n}\n\n.ds-switch[aria-checked=\"true\"]::after {\n transform: translateX(var(--ds-choice-unit));\n}\n\n.ds-switch--rounded {\n border-radius: var(--ds-choice-unit);\n color: var(--ds-white);\n}\n\n.ds-switch--rounded::after {\n border-radius: 50%;\n background: var(--ds-white);\n}\n\n.ds-switch--rounded[aria-checked=\"true\"] {\n background: var(--ds-switch-track-on);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .ds-switch::after {\n transition: none;\n }\n}\n\n/*\n * `.ds-range` draws a native `<input type=\"range\">`: one control high,\n * standing on the lower sixteen of its square (the spare four above it as a\n * margin, so it takes one square alone and in a row), eight squares wide.\n * The track is a 1px line across the middle of the control, the thumb one\n * control high and half a control wide with the shared corners, in the ink.\n * The browser keeps the value, the keys, the form data and the events.\n *\n * Tokens: `--ds-range-width` (eight squares), `--ds-range-track` (the line,\n * the title grey), `--ds-range-thumb` (the ink) and `--ds-range-thumb-width`.\n * WebKit and Blink draw the parts as `::-webkit-slider-*`, Firefox as\n * `::-moz-range-*`; both get the same track and thumb.\n */\n\n.ds-range {\n -webkit-appearance: none;\n appearance: none;\n box-sizing: border-box;\n display: inline-block;\n vertical-align: bottom;\n width: var(--ds-range-width, calc(8 * var(--ds-unit)));\n min-width: 0;\n height: var(--ds-control-height);\n margin: calc(var(--ds-row-height) - var(--ds-control-height)) 0 0;\n padding: 0;\n border: 0;\n border-radius: var(--ds-radius);\n background: transparent;\n color: var(--ds-range-thumb, var(--ds-text-color));\n font: inherit;\n cursor: pointer;\n touch-action: pan-y;\n -webkit-tap-highlight-color: transparent;\n}\n\n.ds-range::-webkit-slider-runnable-track {\n box-sizing: border-box;\n height: var(--ds-control-height);\n border: 0;\n background: linear-gradient(var(--ds-range-track, var(--ds-title-color)) 0 0) 0 calc(var(--ds-control-height) / 2) / 100% 1px no-repeat;\n}\n\n.ds-range::-moz-range-track {\n box-sizing: border-box;\n height: var(--ds-control-height);\n border: 0;\n background: linear-gradient(var(--ds-range-track, var(--ds-title-color)) 0 0) 0 calc(var(--ds-control-height) / 2) / 100% 1px no-repeat;\n}\n\n.ds-range::-moz-range-progress {\n background: transparent;\n}\n\n.ds-range::-webkit-slider-thumb {\n -webkit-appearance: none;\n appearance: none;\n box-sizing: border-box;\n width: var(--ds-range-thumb-width, calc(var(--ds-control-height) / 2));\n height: var(--ds-control-height);\n border: 0;\n border-radius: var(--ds-radius);\n background: currentColor;\n}\n\n.ds-range::-moz-range-thumb {\n box-sizing: border-box;\n width: var(--ds-range-thumb-width, calc(var(--ds-control-height) / 2));\n height: var(--ds-control-height);\n border: 0;\n border-radius: var(--ds-radius);\n background: currentColor;\n}\n\n.ds-range:focus-visible {\n outline: 2px solid var(--ds-focus-ring);\n outline-offset: 1px;\n}\n\n.ds-range:disabled {\n cursor: default;\n opacity: 0.5;\n}\n\n/*\n * `.ds-tabs` is the `ds-tabs` element's row on native buttons. As an option\n * group (role=\"group\", a segmented control) its `.ds-button` choices are\n * grey and the chosen one, aria-pressed, aria-selected or aria-current, is in\n * the accent, as the primary and secondary buttons are.\n */\n\n.ds-tabs {\n box-sizing: border-box;\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: calc(4px * var(--ds-sf));\n min-width: 0;\n height: var(--ds-row-height);\n}\n\n.ds-tabs--fill > * {\n flex: 1 1 0;\n justify-content: center;\n}\n\n.ds-tabs[role=\"group\"] > .ds-button {\n --ds-button-height: var(--ds-row-height);\n --ds-button-padding: 0 calc(4px * var(--ds-sf));\n --ds-button-background: var(--ds-button-background-color-secondary);\n min-width: 0;\n}\n\n.ds-tabs[role=\"group\"] > .ds-button:hover:not(:disabled, [aria-disabled=\"true\"]) {\n --ds-button-background: var(--ds-fill-hover);\n}\n\n.ds-tabs[role=\"group\"] > .ds-button:is([aria-pressed=\"true\"], [aria-selected=\"true\"], [aria-current]:not([aria-current=\"false\"])) {\n --ds-button-background: var(--ds-button-background-color-primary);\n --ds-button-color: var(--ds-button-text-color-on-accent);\n}\n\n/*\n * `.ds-overlay` is the `ds-overlay` element's frame on a native `<dialog>`, a\n * modal by default: twenty squares wide, a square of padding, square corners,\n * a hairline and the paper. `ds-overlay--sheet`, `ds-overlay--drawer` (with\n * `ds-overlay--start` for the start side) and `ds-overlay--screen` are its\n * other types (overlay.css). Its header, title, description, footer and close button are marked\n * with data-ds-overlay, as inside the element.\n */\n\ndialog.ds-overlay {\n box-sizing: border-box;\n position: fixed;\n inset: 0;\n width: var(--ds-overlay-width, calc(20 * var(--ds-unit)));\n max-width: var(--ds-overlay-max-width, calc(100vw - 2 * var(--ds-unit)));\n max-height: var(--ds-overlay-max-height, calc(100dvh - 2 * var(--ds-unit)));\n height: var(--ds-overlay-height, fit-content);\n margin: auto;\n padding: var(--ds-overlay-padding, var(--ds-unit));\n border: var(--ds-overlay-border, 1px solid var(--ds-border-color));\n border-radius: 0;\n background: var(--ds-overlay-paper, var(--ds-paper));\n color: var(--ds-text-color);\n box-shadow: none;\n overflow: auto;\n overscroll-behavior: contain;\n font-family: var(--ds-typeface-regular);\n font-feature-settings: var(--ds-font-features);\n font-size: var(--ds-type-size-default);\n font-weight: var(--ds-type-weight-default);\n line-height: var(--ds-type-lineheight-default);\n letter-spacing: var(--ds-type-letterspacing-default);\n}\n\ndialog.ds-overlay[open] {\n display: flex;\n flex-direction: column;\n gap: var(--ds-unit);\n}\n\ndialog.ds-overlay::backdrop {\n background: color-mix(in srgb, var(--ds-black) 40%, transparent);\n}\n\n.ds-overlay [data-ds-overlay=\"header\"] {\n display: flex;\n flex-direction: column;\n}\n\n/* The header keeps the last square of its row for the close button, only when it has one. */\ndialog.ds-overlay:has(> [data-ds-overlay=\"close\"]) [data-ds-overlay=\"header\"] {\n padding-inline-end: var(--ds-unit);\n}\n\n/* The title on its square's lower sixteen, as every row's words. */\n.ds-overlay [data-ds-overlay=\"title\"] {\n display: flex;\n align-items: flex-end;\n min-height: var(--ds-row-height);\n margin: 0;\n color: var(--ds-text-color);\n font: inherit;\n font-weight: var(--ds-type-weight-bold);\n line-height: var(--ds-type-lineheight-default);\n}\n\n.ds-overlay [data-ds-overlay=\"description\"] {\n margin: 0;\n color: var(--ds-text-color-muted);\n font: inherit;\n}\n\n.ds-overlay [data-ds-overlay=\"footer\"] {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: flex-end;\n gap: calc(var(--ds-unit) / 2);\n min-height: var(--ds-row-height);\n}\n\n.ds-overlay [data-ds-overlay=\"close\"] {\n position: absolute;\n top: var(--ds-unit);\n right: var(--ds-unit);\n}\n\n/* A field in the frame, as a prompt has: its label one row high, the field\n * one row high beneath it and as wide as the frame, two squares in all. */\n.ds-overlay [data-ds-overlay=\"field\"] {\n display: flex;\n flex-direction: column;\n}\n\n.ds-overlay [data-ds-overlay=\"label\"] {\n min-height: var(--ds-row-height);\n line-height: var(--ds-row-height);\n}\n\n.ds-overlay [data-ds-overlay=\"field\"] > .ds-input {\n --ds-input-height: var(--ds-row-height);\n width: 100%;\n}\n\n/*\n * `.ds-button--primary` and `--secondary` are the element's variants on a\n * native button: a form's submit button stays native so Enter in a field\n * submits the form. `--danger` is its `danger` attribute.\n */\n\n.ds-button--primary,\n.ds-button--secondary {\n --ds-button-padding: 0 calc(4px * var(--ds-sf));\n}\n\n.ds-button--primary {\n --ds-button-background: var(--ds-button-background-color-primary);\n --ds-button-color: var(--ds-button-text-color-on-accent);\n}\n\n.ds-button--secondary {\n --ds-button-background: var(--ds-button-background-color-secondary);\n}\n\n.ds-button--secondary:hover:not(:disabled, [aria-disabled=\"true\"]) {\n --ds-button-background: var(--ds-fill-hover);\n}\n\n/* `.ds-button--danger` is the element's `danger`: a destructive action's words\n * in the danger colour, on the secondary face or none. */\n.ds-button--danger {\n --ds-button-color: var(--ds-danger-color, var(--ds-tuned-red));\n}\n\n.ds-button--fixed {\n width: var(--ds-button-width, calc(4 * var(--ds-unit)));\n}\n\n.ds-button--wide {\n justify-content: center;\n width: 100%;\n}\n\n/*\n * `.ds-menu` is the `ds-menu` element's look on a menu a library draws:\n * the context menu's items come from Radix, which owns their roles, focus and\n * keys, so they stay Radix's and take shared's paper, rows and type.\n * On whole squares, as the element: the line and the padding inside it make\n * half a square on each side, entries are one square each with nothing\n * between them, a separator is one square with its line, and there is no\n * shadow. n entries make a menu n + 1 squares high.\n */\n\n.ds-menu {\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n gap: 0;\n min-width: calc(8 * var(--ds-unit));\n max-width: calc(12 * var(--ds-unit));\n max-height: var(--ds-menu-max-height, calc(16 * var(--ds-unit)));\n overflow-y: auto;\n padding: calc(var(--ds-unit) / 2 - 1px);\n border: 1px solid var(--ds-border-color);\n border-radius: var(--ds-radius-card);\n background: var(--ds-paper);\n color: var(--ds-text-color);\n box-shadow: none;\n font-family: var(--ds-typeface-regular);\n font-feature-settings: var(--ds-font-features);\n font-size: var(--ds-type-size-default);\n font-weight: var(--ds-type-weight-default);\n line-height: var(--ds-type-lineheight-default);\n letter-spacing: var(--ds-type-letterspacing-default);\n}\n\n.ds-menu__item {\n position: relative;\n box-sizing: border-box;\n display: flex;\n align-items: center;\n gap: calc(4px * var(--ds-sf));\n width: 100%;\n min-height: var(--ds-row-height);\n padding: calc(2px * var(--ds-sf)) calc(6px * var(--ds-sf));\n border-radius: calc(4px * var(--ds-sf));\n color: var(--ds-text-color);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n cursor: default;\n user-select: none;\n outline: none;\n}\n\n/* An entry a library draws as its own button or link is one square high too,\n * whatever that library's button styles say. */\n.ds-menu > :is(button, a, [role=\"option\"], [role^=\"menuitem\"]),\n.ds-menu > [role=\"group\"] > :is(button, a, [role=\"option\"], [role^=\"menuitem\"]) {\n box-sizing: border-box !important;\n flex: 0 0 auto !important;\n height: var(--ds-row-height) !important;\n min-height: var(--ds-row-height) !important;\n max-height: var(--ds-row-height) !important;\n margin-block: 0 !important;\n padding-block: 0 !important;\n line-height: var(--ds-type-lineheight-default) !important;\n}\n\n/* A menu item may be a native button: it keeps the row's look, not a button's. */\n:where(button).ds-menu__item {\n appearance: none;\n margin: 0;\n border: 0;\n background: transparent;\n font: inherit;\n letter-spacing: inherit;\n text-align: start;\n}\n\n.ds-menu__item--inset {\n padding-left: calc(var(--ds-unit) + calc(4px * var(--ds-sf)));\n}\n\n.ds-menu__item:hover,\n.ds-menu__item:focus-visible,\n.ds-menu__item[data-highlighted] {\n background: var(--ds-fill);\n}\n\n.ds-menu__item[data-variant=\"destructive\"] {\n color: var(--ds-danger-color, var(--ds-tuned-red));\n}\n\n.ds-menu__item[data-disabled],\n.ds-menu__item[aria-disabled=\"true\"] {\n opacity: 0.5;\n pointer-events: none;\n}\n\n.ds-menu__indicator {\n position: absolute;\n left: calc(6px * var(--ds-sf));\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: calc(12px * var(--ds-sf));\n height: calc(12px * var(--ds-sf));\n pointer-events: none;\n}\n\n.ds-menu__indicator svg {\n width: 100%;\n height: 100%;\n}\n\n.ds-menu__chevron {\n width: calc(12px * var(--ds-sf));\n height: calc(12px * var(--ds-sf));\n margin-left: auto;\n color: var(--ds-text-color-muted);\n}\n\n.ds-menu__label {\n box-sizing: border-box;\n min-height: var(--ds-row-height);\n padding: calc(2px * var(--ds-sf)) calc(6px * var(--ds-sf));\n color: var(--ds-title-color);\n font-size: var(--ds-type-size-small);\n}\n\n.ds-menu__separator {\n flex: 0 0 auto;\n height: 1px;\n /* One square with its line. */\n margin: calc(var(--ds-unit) / 2 - 1px) calc(6px * var(--ds-sf)) calc(var(--ds-unit) / 2);\n background: var(--ds-border-color);\n}\n\n.ds-menu__shortcut {\n margin-left: auto;\n padding-left: var(--ds-unit);\n color: var(--ds-title-color);\n font-size: var(--ds-type-size-small);\n}\n\n/*\n * Pieces the components draw, as classes on native elements: a field's label\n * row, an avatar, a badge, a toggle button, an icon and a file drop zone. Each\n * reads the same tokens as its element, so a page without Lit draws the same\n * look.\n */\n\n/*\n * `.ds-field` is the label row `ds-input` draws, around any native control:\n * the label (`.ds-field__label`) on a one-square line above it, then a hint\n * (`.ds-field__hint`) in the muted ink and an error (`.ds-field__error`) in\n * the danger colour, each on one-square lines. Every row is at least one\n * square high and a control lower than that stands centred on it, so the\n * field keeps to whole squares. `.ds-field--beside` sets the label beside the\n * control, in a column `--ds-field-label-width` wide (four squares), with the\n * hint and the error under the control. An empty hint or error takes no room,\n * so the error can stay in the page as a live region.\n */\n\n.ds-field {\n box-sizing: border-box;\n display: grid;\n grid-template-columns: minmax(0, 1fr);\n grid-auto-rows: minmax(var(--ds-row-height), auto);\n align-items: center;\n justify-items: start;\n min-width: 0;\n margin: 0;\n color: var(--ds-text-color);\n font-family: var(--ds-typeface-regular);\n font-feature-settings: var(--ds-font-features);\n font-size: var(--ds-type-size-default);\n font-weight: var(--ds-type-weight-default);\n line-height: var(--ds-unit);\n letter-spacing: var(--ds-type-letterspacing-default);\n}\n\n.ds-field > :is(.ds-input, .ds-select) {\n justify-self: stretch;\n}\n\n.ds-field__label,\n.ds-field__hint,\n.ds-field__error {\n display: block;\n min-width: 0;\n margin: 0;\n font: inherit;\n letter-spacing: inherit;\n}\n\n.ds-field__label {\n color: var(--ds-field-label-color, var(--ds-text-color));\n}\n\n.ds-field__hint {\n color: var(--ds-text-color-muted);\n}\n\n.ds-field__error {\n color: var(--ds-danger-color);\n}\n\n.ds-field__hint:empty,\n.ds-field__error:empty {\n display: none;\n}\n\n.ds-field--beside {\n grid-template-columns: var(--ds-field-label-width, calc(4 * var(--ds-unit))) minmax(0, 1fr);\n}\n\n.ds-field--beside > * {\n grid-column: 2;\n}\n\n.ds-field--beside > .ds-field__label {\n grid-column: 1;\n grid-row: 1;\n align-self: start;\n}\n\n/*\n * `.ds-avatar` is the `ds-avatar` element's mark: round and one control high,\n * an initial in small type on the neutral fill, or an `<img alt=\"\">` after the\n * initial, covering it. While the picture loads the initial shows; remove a\n * picture that fails (`onerror`), as the element does, or the browser draws\n * its broken-image mark. `--large` is two squares with type a square high,\n * `--pair` fits two characters between the hairline edges, `--square` gives it\n * a picture's corners, `--outlined` draws the hairline in its ink and\n * `--pending` greys out someone who has not accepted yet.\n * `--ds-avatar-background` fills it with a colour and `--ds-avatar-color`\n * letters it: on a fill, pick the black or white ink that reads on it\n * (`--ds-black`, `--ds-white`).\n */\n\n.ds-avatar {\n --ds-avatar-size: var(--ds-control-height);\n --ds-avatar-pair-type-size: var(--ds-type-size-small);\n box-sizing: border-box;\n position: relative;\n display: inline-grid;\n place-items: center;\n flex: 0 0 auto;\n vertical-align: middle;\n width: var(--ds-avatar-size);\n height: var(--ds-avatar-size);\n overflow: hidden;\n border-radius: 50%;\n corner-shape: round;\n background: var(--ds-avatar-background, var(--ds-fill));\n color: var(--ds-avatar-color, var(--ds-text-color));\n font: var(--ds-type-weight-default) var(--ds-type-size-small) / 1 var(--ds-typeface-regular);\n letter-spacing: normal;\n text-align: center;\n white-space: nowrap;\n user-select: none;\n -webkit-user-select: none;\n}\n\n.ds-avatar > img {\n position: absolute;\n inset: 0;\n display: block;\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n\n.ds-avatar--large {\n --ds-avatar-size: calc(2 * var(--ds-unit));\n --ds-avatar-pair-type-size: var(--ds-type-size-default);\n font-size: var(--ds-unit);\n}\n\n.ds-avatar--pair {\n font-size: min(var(--ds-avatar-pair-type-size), calc((var(--ds-avatar-size) - 2px) / 2));\n}\n\n.ds-avatar--square {\n border-radius: var(--ds-avatar-radius, var(--ds-radius));\n corner-shape: var(--ds-corner-shape, round);\n}\n\n.ds-avatar--outlined::after {\n content: \"\";\n position: absolute;\n inset: 0;\n border: 1px solid var(--ds-avatar-outline, currentColor);\n border-radius: inherit;\n pointer-events: none;\n}\n\n.ds-avatar--pending {\n background: var(--ds-fill);\n color: var(--ds-text-color-muted);\n}\n\n.ds-avatar--pending > img {\n filter: grayscale(1);\n opacity: 0.5;\n}\n\n/*\n * `.ds-badge` is the `ds-badge` element's status label: one square high on the\n * neutral fill, its words padded 4px and cut short with an ellipsis, never a\n * button. Where the browser can size to content in calc (calc-size), its width\n * rounds up to whole squares as the element's does; elsewhere it hugs the\n * words. An icon inside is a `.ds-icon` before the words. `--icon` shows the\n * icon alone, one square wide, and keeps the words for assistive technology:\n * put them in an element of their own. `--ds-badge-background` and\n * `--ds-badge-color` give a status a meaningful colour.\n */\n\n.ds-badge {\n box-sizing: border-box;\n display: inline-block;\n flex: 0 0 auto;\n vertical-align: middle;\n width: max-content;\n width: calc-size(max-content, round(up, size, var(--ds-unit)));\n min-width: var(--ds-row-height);\n max-width: 100%;\n height: var(--ds-row-height);\n padding: 0 calc(4px * var(--ds-sf));\n overflow: hidden;\n background: var(--ds-badge-background, var(--ds-fill));\n color: var(--ds-badge-color, var(--ds-text-color));\n font-family: var(--ds-typeface-regular);\n font-feature-settings: var(--ds-font-features);\n font-size: var(--ds-type-size-default);\n font-weight: var(--ds-type-weight-default);\n line-height: var(--ds-row-height);\n letter-spacing: var(--ds-type-letterspacing-default);\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.ds-badge > .ds-icon {\n vertical-align: top;\n margin-top: calc((var(--ds-row-height) - var(--ds-icon-box, var(--ds-control-height))) / 2);\n margin-right: calc(4px * var(--ds-sf));\n}\n\n.ds-badge--icon {\n position: relative;\n width: var(--ds-row-height);\n padding: 0;\n text-align: center;\n}\n\n.ds-badge--icon > .ds-icon {\n margin-right: 0;\n}\n\n.ds-badge--icon > :not(.ds-icon) {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n}\n\n/*\n * `.ds-toggle` makes a `.ds-button` a toggle button, `<button aria-pressed>`:\n * the secondary button's grey while it is off, the primary button's accent\n * while it is on, as a chosen segment of `.ds-tabs[role=\"group\"]` is. Its\n * words stay the same in both states; the page toggles `aria-pressed`.\n * A setting that is on or off is a `.ds-switch`; one of several choices is a\n * `.ds-tabs[role=\"group\"]`.\n */\n\n.ds-toggle {\n --ds-button-padding: 0 calc(4px * var(--ds-sf));\n --ds-button-background: var(--ds-button-background-color-secondary);\n}\n\n.ds-toggle:hover:not(:disabled, [aria-disabled=\"true\"]) {\n --ds-button-background: var(--ds-fill-hover);\n}\n\n.ds-toggle[aria-pressed=\"true\"],\n.ds-toggle[aria-pressed=\"true\"]:hover:not(:disabled, [aria-disabled=\"true\"]) {\n --ds-button-background: var(--ds-button-background-color-primary);\n --ds-button-color: var(--ds-button-text-color-on-accent);\n}\n\n/*\n * `.ds-icon` is the `ds-icon` element's drawing without the element: a\n * Design system 1 icon file as a mask, painted in the text colour\n * (`--ds-icon-color`), 14px by default, 12px with `--small` and 16px with\n * `--large`, times the scale. `--ds-icon` names the file, as Sync 1's `icons`\n * export copies it: `style=\"--ds-icon: url(icons/close.svg)\"` (a path from the\n * page) or a rule per icon in the page's stylesheet. The mask takes only the\n * drawing's shape, so an icon with colours of its own is an `<img>`. These\n * rules have no specificity: a page that drew its own `.ds-icon` keeps its\n * rule until it removes it.\n */\n\n:where(.ds-icon) {\n --ds-icon-box: calc(var(--ds-icon-size, 14px) * var(--ds-sf));\n display: inline-block;\n flex: 0 0 auto;\n width: var(--ds-icon-box);\n height: calc(var(--ds-icon-box) / var(--ds-icon-ratio, 1));\n color: var(--ds-icon-color, currentColor);\n background-color: currentColor;\n -webkit-mask: var(--ds-icon) center / contain no-repeat;\n mask: var(--ds-icon) center / contain no-repeat;\n}\n\n:where(.ds-icon--small) {\n --ds-icon-size: 12px;\n}\n\n:where(.ds-icon--large) {\n --ds-icon-size: 16px;\n}\n\n/*\n * `.ds-drop` is `ds-file-picker`'s area on a `<label>` around a native\n * `<input type=\"file\">`: the secondary button's face, as wide as its row and\n * eight squares high (`--compact`: four), its words centred in a square of\n * padding. The input covers the zone unseen, so a click, the keyboard and a\n * dropped file all reach it, and the browser keeps labels, forms, `required`,\n * `multiple` and reset. Keyboard focus draws the focus ring. A page that also\n * handles drops on a larger surface sets `data-dragging` on the zone while\n * files are dragged over it, for the ring and the hover fill. A `.ds-button`\n * on a `<span>` inside draws a button face among the words.\n *\n * The same native input directly inside a `label.ds-button` is the file\n * picker's button: the button's face, one square high.\n */\n\n.ds-drop {\n box-sizing: border-box;\n position: relative;\n display: flex;\n flex-wrap: wrap;\n align-content: center;\n align-items: center;\n justify-content: center;\n gap: calc(var(--ds-unit) / 2);\n width: 100%;\n min-width: 0;\n height: calc(8 * var(--ds-unit));\n margin: 0;\n padding: var(--ds-unit);\n overflow: hidden;\n border-radius: var(--ds-radius);\n corner-shape: var(--ds-corner-shape, round);\n background: var(--ds-button-background-color-secondary);\n color: var(--ds-button-text-color);\n font-family: var(--ds-typeface-regular);\n font-feature-settings: var(--ds-font-features);\n font-size: var(--ds-type-size-default);\n font-weight: var(--ds-type-weight-default);\n line-height: var(--ds-type-lineheight-default);\n letter-spacing: var(--ds-type-letterspacing-default);\n text-align: center;\n cursor: pointer;\n user-select: none;\n -webkit-user-select: none;\n -webkit-tap-highlight-color: transparent;\n}\n\n.ds-drop--compact {\n height: calc(4 * var(--ds-unit));\n}\n\nlabel.ds-button:has(> input[type=\"file\"]) {\n --ds-button-height: var(--ds-row-height);\n position: relative;\n overflow: hidden;\n}\n\n:is(.ds-drop, label.ds-button) > input[type=\"file\"]:not([hidden]) {\n position: absolute;\n inset: 0;\n box-sizing: border-box;\n display: block;\n width: 100%;\n height: 100%;\n min-width: 0;\n margin: 0;\n padding: 0;\n border: 0;\n opacity: 0;\n cursor: pointer;\n}\n\n:is(.ds-drop, label.ds-button) > input[type=\"file\"]::file-selector-button {\n cursor: pointer;\n}\n\n.ds-drop:hover:not(:has(> input:disabled)),\n.ds-drop[data-dragging]:not(:has(> input:disabled)) {\n background: var(--ds-fill-hover);\n}\n\n.ds-drop:has(> input:focus-visible),\n.ds-drop[data-dragging]:not(:has(> input:disabled)),\nlabel.ds-button:has(> input[type=\"file\"]:focus-visible) {\n outline: 2px solid var(--ds-focus-ring);\n outline-offset: 1px;\n}\n\n.ds-drop:has(> input:disabled),\nlabel.ds-button:has(> input[type=\"file\"]:disabled) {\n cursor: default;\n opacity: 0.5;\n}\n\n:is(.ds-drop, label.ds-button) > input[type=\"file\"]:disabled {\n cursor: default;\n}\n";
8831
+ var controls_default = "/* Shape is a surface preference. Keep these base lengths unscaled: each\n * component resolves them with its own --ds-sf in component-metrics.css. */\n[data-ds-shape=\"square\"],\n:host([data-ds-shape=\"square\"]) {\n --ds-corner-size: 0px;\n --ds-corner-shape: round;\n --ds-button-inset: 0.5px;\n}\n\n[data-ds-shape=\"rounded\"],\n:host([data-ds-shape=\"rounded\"]) {\n --ds-corner-size: 4px;\n --ds-corner-shape: round;\n --ds-button-inset: 2.5px;\n}\n\n[data-ds-shape=\"circular\"],\n:host([data-ds-shape=\"circular\"]) {\n --ds-corner-size: 999px;\n --ds-corner-shape: round;\n --ds-button-inset: 5px;\n}\n\n/* A squircle needs twice an arc's radius for the same apparent corner.\n * Circular controls keep true semicircles even in a squircle browser. */\n@supports (corner-shape: squircle) {\n [data-ds-shape=\"rounded\"],\n :host([data-ds-shape=\"rounded\"]) {\n --ds-corner-size: 8px;\n --ds-corner-shape: squircle;\n }\n}\n\n/* Resolve dimensions where a component is drawn. Inherited calc() values have\n * already used their ancestor's scale. Base tokens stay unscaled and inherit;\n * resolved tokens can still be overridden directly on a component.\n * data-ds-surface gives ordinary layout elements the same scaled measures.\n */\n:root,\n:host,\n:where([data-ds-surface]) {\n --ds-unit: calc(var(--ds-1, 20px) * var(--ds-sf, 1));\n --ds-control-height: calc(var(--ds-control-size, var(--ds-08, 16px)) * var(--ds-sf, 1));\n --ds-row-height: calc(var(--ds-row-size, var(--ds-1, 20px)) * var(--ds-sf, 1));\n --ds-type-size-default: calc(var(--ds-text-size, 14px) * var(--ds-sf, 1));\n --ds-type-size-small: calc(var(--ds-small-text-size, 10px) * var(--ds-sf, 1));\n --ds-type-size-heading: calc(var(--ds-heading-text-size, 13px) * var(--ds-sf, 1));\n --ds-type-size-display: calc(var(--ds-display-text-size, 20px) * var(--ds-sf, 1));\n --ds-type-lineheight-default: calc(var(--ds-text-leading, 16px) * var(--ds-sf, 1));\n --ds-type-letterspacing-default: calc(var(--ds-text-tracking, -0.3px) * var(--ds-sf, 1));\n --ds-radius: calc(var(--ds-corner-size, 4px) * var(--ds-sf, 1));\n --ds-radius-card: calc(var(--ds-card-corner-size, 4px) * var(--ds-sf, 1));\n}\n/*\n * A table: words straight on the paper, one square a row. No box and no\n * column lines: each row stands on a hairline at its foot, its words set on\n * that line like a caption: the sixteen of words against the 1px line, three\n * spare above, a 20px pitch, as Ezo sets every row.\n * Column headers keep the text's weight in the title grey; the first column\n * of a body row is a row header in the text colour. Nothing is filled with\n * the accent except a cell someone has selected.\n *\n * `.ds-table` dresses a native <table>; `<ds-table>` draws the same table\n * from data. Put a wide table in `.ds-table-scroll` to scroll it sideways,\n * and give the table `data-sticky` to keep its first column in place.\n *\n * <th data-numeric>, <td data-numeric> numbers, set to the column's end\n * <tfoot> totals, under a line of their own\n * <th colspan=\"2\"> a column group, in a header row\n * <th scope=\"rowgroup\"> a row group, a heading row in grey\n * <td aria-selected=\"true\"> a selected cell\n *\n * `data-sheet` makes it a spreadsheet: every cell four squares by one (80 by\n * 20), its 1px lines at its end and foot so the words have 79 by 19, set 4px\n * in on the sixteen, as an Ezo item's are. Column letters and the row numbers\n * (`.ds-table__letters`, `.ds-table__number`, two squares wide) stay in place\n * while the sheet scrolls both ways inside `.ds-table-scroll`; give the table\n * `--ds-table-columns` (the number of data columns) and the scroll a height\n * with `--ds-table-height`.\n */\n\n.ds-table {\n --ds-table-row: var(--ds-row-height, calc(20px * var(--ds-sf, 1)));\n --ds-table-gap: calc(var(--ds-table-row) / 2);\n --ds-table-rule: var(--ds-border-color, light-dark(#d8d8d8, #3d3d3d));\n --ds-table-paper: var(--ds-background, Canvas);\n width: 100%;\n border-collapse: collapse;\n border-spacing: 0;\n color: var(--ds-text-color, inherit);\n font-family: var(--ds-typeface-regular, inherit);\n font-feature-settings: var(--ds-font-features, normal);\n font-size: var(--ds-type-size-default, 14px);\n letter-spacing: var(--ds-type-letterspacing-default, normal);\n}\n\n.ds-table :is(th, td) {\n box-sizing: border-box;\n height: var(--ds-table-row);\n padding: 0 var(--ds-table-gap) 0 0;\n border: 0;\n border-bottom: 1px solid var(--ds-table-rule);\n background: transparent;\n line-height: var(--ds-type-lineheight-default);\n text-align: start;\n vertical-align: bottom;\n font-weight: inherit;\n white-space: nowrap;\n}\n\n.ds-table :is(th, td):last-child {\n padding-inline-end: 0;\n}\n\n.ds-table thead th,\n.ds-table th[scope=\"rowgroup\"] {\n color: var(--ds-title-color, light-dark(#797979, #9a9a9a));\n}\n\n.ds-table tbody th:not([scope=\"rowgroup\"]) {\n color: inherit;\n}\n\n.ds-table [data-numeric] {\n text-align: end;\n font-variant-numeric: tabular-nums;\n}\n\n/* Totals: the rule above them is the last body row's; theirs closes the table. */\n.ds-table tfoot :is(th, td) {\n color: inherit;\n}\n\n.ds-table [aria-selected=\"true\"] {\n background: var(--ds-accent-color, Highlight);\n color: var(--ds-button-text-color-on-accent, inherit);\n}\n\n.ds-table :is(th, td):has(> :is(a, button):only-child) {\n cursor: pointer;\n}\n\n.ds-table caption {\n box-sizing: border-box;\n height: var(--ds-table-row);\n padding-top: calc(var(--ds-table-row) - var(--ds-type-lineheight-default));\n line-height: var(--ds-type-lineheight-default);\n text-align: start;\n color: var(--ds-title-color, light-dark(#797979, #9a9a9a));\n}\n\n.ds-table-scroll {\n max-width: 100%;\n overflow-x: auto;\n overscroll-behavior-x: contain;\n}\n\n.ds-table-scroll > .ds-table {\n width: max-content;\n min-width: 100%;\n}\n\n.ds-table[data-sticky] :is(th, td):first-child {\n position: sticky;\n inset-inline-start: 0;\n z-index: 1;\n background: var(--ds-table-paper);\n}\n\n.ds-table[data-sticky] [aria-selected=\"true\"]:first-child {\n background: var(--ds-accent-color, Highlight);\n}\n\n/* Keep the row whose details are open visible, including its sticky first cell.\n Cell selection remains independent and keeps its accent. */\n.ds-table tbody > tr[data-open]:not([data-open=\"false\"]) > :is(th, td):not([aria-selected=\"true\"]) {\n background: var(--ds-row-fill-current, var(--ds-fill-hover));\n}\n@media (forced-colors: active) {\n .ds-table tbody > tr[data-open]:not([data-open=\"false\"]) > :is(th, td):not([aria-selected=\"true\"]) {\n background: Highlight;\n color: HighlightText;\n }\n}\n\n/* A spreadsheet: cells four squares by one, on lines, lettered and numbered. */\n.ds-table[data-sheet] {\n --ds-table-cell: calc(4 * var(--ds-table-row));\n --ds-table-index: calc(2 * var(--ds-table-row));\n --ds-table-text: calc(4px * var(--ds-sf, 1));\n table-layout: fixed;\n width: calc(var(--ds-table-index) + var(--ds-table-columns, 8) * var(--ds-table-cell));\n min-width: 0;\n border-collapse: separate;\n border-spacing: 0;\n}\n.ds-table-scroll > .ds-table[data-sheet] {\n width: calc(var(--ds-table-index) + var(--ds-table-columns, 8) * var(--ds-table-cell));\n min-width: 0;\n}\n.ds-table[data-sheet] :is(th, td) {\n width: var(--ds-table-cell);\n max-width: var(--ds-table-cell);\n height: var(--ds-table-row);\n padding: 0 var(--ds-table-text);\n border-bottom: 1px solid var(--ds-table-rule);\n border-inline-end: 1px solid var(--ds-table-rule);\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ds-table[data-sheet] :is(th, td):last-child {\n padding-inline-end: var(--ds-table-text);\n}\n.ds-table[data-sheet] .ds-table__number {\n width: var(--ds-table-index);\n max-width: var(--ds-table-index);\n position: sticky;\n inset-inline-start: 0;\n z-index: 1;\n background: var(--ds-table-paper);\n color: var(--ds-title-color, light-dark(#797979, #9a9a9a));\n text-align: center;\n font-variant-numeric: tabular-nums;\n}\n.ds-table[data-sheet] thead :is(th, td) {\n position: sticky;\n top: 0;\n z-index: 2;\n background: var(--ds-table-paper);\n}\n.ds-table[data-sheet] thead tr + tr :is(th, td) {\n top: var(--ds-table-row);\n}\n.ds-table[data-sheet] thead .ds-table__number {\n z-index: 3;\n}\n.ds-table[data-sheet] .ds-table__letters td {\n color: var(--ds-title-color, light-dark(#797979, #9a9a9a));\n text-align: center;\n}\n.ds-table[data-sheet] tbody td:not([data-numeric]) {\n color: inherit;\n}\n.ds-table[data-sheet] [aria-selected=\"true\"] {\n background: transparent;\n color: inherit;\n box-shadow: inset 0 0 0 2px var(--ds-focus-ring, Highlight);\n}\n.ds-table-scroll:has(> .ds-table[data-sheet]) {\n max-height: var(--ds-table-height, none);\n overflow: auto;\n overscroll-behavior: contain;\n}\n/*\n * Text on the grid, as plain classes on native text.\n *\n * Interface copy on a page without Lit: a label, a message, a title. Each line\n * is one square (`--ds-unit`), so a paragraph of any length stands on whole\n * squares, and a display title takes two. The `ds-text` element draws the same\n * words when they come from a translation key.\n *\n * one.css and controls.css both import this file, so a page that loads either\n * of them, or both, has one definition of these classes.\n *\n * ds-text 14 on a one-square line, the text colour\n * ds-text--small 10\n * ds-text--heading 13, medium\n * ds-text--display 20 on two squares, medium\n * ds-text--strong medium\n * ds-text--muted, --title, --danger tones\n */\n\n.ds-text {\n margin: 0;\n color: var(--ds-text-color);\n font-family: var(--ds-typeface-regular);\n font-feature-settings: var(--ds-font-features);\n font-size: var(--ds-type-size-default);\n font-weight: var(--ds-type-weight-default);\n line-height: var(--ds-unit);\n letter-spacing: var(--ds-type-letterspacing-default);\n}\n\n.ds-text--small {\n font-size: var(--ds-type-size-small);\n}\n\n.ds-text--heading {\n font-family: var(--ds-typeface-medium);\n font-size: var(--ds-type-size-heading);\n font-weight: var(--ds-type-weight-bold);\n}\n\n.ds-text--display {\n font-family: var(--ds-typeface-medium);\n font-size: var(--ds-type-size-display);\n font-weight: var(--ds-type-weight-bold);\n line-height: calc(2 * var(--ds-unit));\n letter-spacing: calc(var(--ds-display-text-tracking, -0.4px) * var(--ds-sf, 1));\n}\n\n.ds-text--strong {\n font-family: var(--ds-typeface-medium);\n font-weight: var(--ds-type-weight-bold);\n}\n\n.ds-text--muted {\n color: var(--ds-text-color-muted);\n}\n\n.ds-text--title {\n color: var(--ds-title-color);\n}\n\n.ds-text--danger {\n color: var(--ds-danger-color);\n}\n\n.ds-text--warning {\n color: var(--ds-warning-color);\n}\n/*\n * An empty state, as plain classes on native elements.\n *\n * It says that a place has nothing in it yet and what to do about it: a title,\n * a line of text and, when there is one, the action that fills it. The words\n * sit on the paper with no box, no illustration and no colour; every line is\n * one square, so the whole stands on whole squares. The `ds-empty` element\n * draws the same parts from a translation key, with the shared words' \"Nothing\n * here yet\" as its title when none is given.\n *\n * ds-empty the block, at most sixteen squares wide\n * ds-empty__icon an icon from the library, on its own square, muted\n * ds-empty__title one line in the medium face\n * ds-empty__text lines of one square, muted\n * ds-empty__actions buttons or links on one row, one square below\n * ds-empty--centered centred in the space it stands in\n * ds-empty--done all done: the check on a mark in the success\n * green, the title in that green\n *\n * Tokens: `--ds-empty-measure` (the widest it grows, sixteen squares) and\n * `--ds-empty-padding` (the room above and below, none by default).\n */\n\n.ds-empty {\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n width: 100%;\n max-width: var(--ds-empty-measure, calc(16 * var(--ds-unit)));\n margin: 0;\n padding-block: var(--ds-empty-padding, 0);\n color: var(--ds-text-color);\n font-family: var(--ds-typeface-regular);\n font-feature-settings: var(--ds-font-features);\n font-size: var(--ds-type-size-default);\n font-weight: var(--ds-type-weight-default);\n line-height: var(--ds-unit);\n letter-spacing: var(--ds-type-letterspacing-default);\n text-transform: none;\n}\n\n.ds-empty__icon {\n display: flex;\n align-items: center;\n height: var(--ds-unit);\n color: var(--ds-text-color-muted);\n}\n\n.ds-empty__title,\n.ds-empty__text {\n margin: 0;\n font: inherit;\n letter-spacing: inherit;\n}\n\n.ds-empty__title {\n font-family: var(--ds-typeface-medium);\n font-weight: var(--ds-type-weight-bold);\n}\n\n.ds-empty__text {\n color: var(--ds-text-color-muted);\n}\n\n.ds-empty__actions {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 0 calc(var(--ds-unit) / 2);\n min-height: var(--ds-row-height);\n margin-top: var(--ds-unit);\n}\n\n.ds-empty--centered {\n align-items: center;\n margin-inline: auto;\n text-align: center;\n}\n\n.ds-empty--centered .ds-empty__actions {\n justify-content: center;\n}\n\n/* Done: a place empty because everything in it is finished (every receipt\n found, nothing left to review). The icon, the check by default, stands on a\n round mark one control high in the success green, on the sixteen, and the\n title takes the same green; the line under it stays muted. */\n.ds-empty--done .ds-empty__icon {\n box-sizing: border-box;\n justify-content: center;\n width: var(--ds-control-height);\n height: var(--ds-control-height);\n margin-top: calc(var(--ds-unit) - var(--ds-control-height));\n border-radius: 50%;\n background: var(--ds-success-color);\n color: light-dark(var(--ds-white), var(--ds-black));\n --ds-icon-color: currentColor;\n}\n\n.ds-empty--done .ds-empty__title {\n color: var(--ds-success-color);\n}\n/*\n * An inline notice, as plain classes on native elements.\n *\n * A notice stands in the flow of the page, where its words apply: a failure\n * beside the form that failed, a warning above the list it is about. It is not\n * positioned and takes no box or shadow. A square mark, one control high and\n * filled in the tone's colour, carries the tone; the words sit on the paper\n * beside it, each line one square, with the actions on the row below and the\n * dismiss button at the end of the first line. Danger turns the words into\n * the danger colour. The `ds-notice` element draws the same parts.\n *\n * ds-notice the block: mark, words, dismiss\n * ds-notice__mark the square mark; holds an icon in its ink\n * ds-notice__title one line in the medium face\n * ds-notice__text lines of one square\n * ds-notice__actions buttons or links on one row\n * ds-notice__dismiss an icon button at the end of the first line\n * ds-notice--success, --warning, --danger tones; neutral without one\n * ds-notice--banner a strip across the top of its positioning\n * context instead: one square high, filled in\n * the tone's colour, everything on one line\n * ds-notice--app a row at the top of the page leading to the\n * site's iPhone app: its picture\n * (ds-notice__picture, two squares), its name\n * and one line about it, Open\n * (a.ds-notice__open) and the close; three\n * squares high with a hairline at its foot.\n * startAppBanners() from 0-face/app-banner.ts\n * shows it only where it belongs\n *\n * Give the block role=\"status\", or role=\"alert\" for danger, so assistive\n * technology reads it when it appears.\n *\n * Tokens: `--ds-notice-mark`, `--ds-notice-mark-ink` and `--ds-notice-ink`,\n * which the tones set.\n */\n\n.ds-notice {\n --ds-notice-mark: var(--ds-fill);\n --ds-notice-mark-ink: var(--ds-text-color);\n --ds-notice-ink: var(--ds-text-color);\n box-sizing: border-box;\n display: grid;\n grid-template-columns: var(--ds-unit) minmax(0, 1fr) auto;\n align-items: start;\n margin: 0;\n color: var(--ds-notice-ink);\n font-family: var(--ds-typeface-regular);\n font-feature-settings: var(--ds-font-features);\n font-size: var(--ds-type-size-default);\n font-weight: var(--ds-type-weight-default);\n line-height: var(--ds-unit);\n letter-spacing: var(--ds-type-letterspacing-default);\n text-transform: none;\n}\n\n.ds-notice--success {\n --ds-notice-mark: var(--ds-apple-green);\n --ds-notice-mark-ink: var(--ds-black);\n}\n\n.ds-notice--warning {\n --ds-notice-mark: var(--ds-orange);\n --ds-notice-mark-ink: var(--ds-black);\n}\n\n.ds-notice--danger {\n --ds-notice-mark: var(--ds-danger-color);\n --ds-notice-mark-ink: light-dark(var(--ds-white), var(--ds-black));\n --ds-notice-ink: var(--ds-danger-color);\n}\n\n/* The mark stands on the first line, one control high in a one-square cell. */\n.ds-notice__mark {\n box-sizing: border-box;\n grid-column: 1;\n grid-row: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n width: var(--ds-control-height);\n height: var(--ds-control-height);\n margin-block: calc((var(--ds-unit) - var(--ds-control-height)) / 2);\n overflow: hidden;\n background: var(--ds-notice-mark);\n color: var(--ds-notice-mark-ink);\n --ds-icon-color: var(--ds-notice-mark-ink);\n}\n\n.ds-notice__title,\n.ds-notice__text,\n.ds-notice__actions {\n grid-column: 2;\n min-width: 0;\n margin: 0;\n}\n\n.ds-notice__title,\n.ds-notice__text {\n font: inherit;\n letter-spacing: inherit;\n overflow-wrap: anywhere;\n}\n\n.ds-notice__title {\n font-family: var(--ds-typeface-medium);\n font-weight: var(--ds-type-weight-bold);\n}\n\n.ds-notice__actions {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 0 calc(var(--ds-unit) / 2);\n min-height: var(--ds-row-height);\n}\n\n/* The dismiss button closes the first line: a one-square cell at the end.\n Two classes, so the button's own reset of its margin does not undo it. */\n.ds-notice .ds-notice__dismiss {\n grid-column: 3;\n grid-row: 1;\n margin-block: calc((var(--ds-unit) - var(--ds-control-height)) / 2);\n margin-inline-start: calc(var(--ds-unit) - var(--ds-control-height));\n}\n\n/* A banner: the same parts on one line, one square high, across the top of\n the nearest positioned ancestor (or the page), on a fill of the tone's\n colour. It takes no room in the flow; leave a square for it where words\n stand under it. The actions stand at the end of the line, half a square\n apart; danger words keep the text colour on the danger fill. */\n.ds-notice--banner {\n position: absolute;\n top: 0;\n inset-inline: 0;\n z-index: 9999;\n display: flex;\n align-items: center;\n gap: calc(var(--ds-unit) / 2);\n height: var(--ds-unit);\n padding-inline: var(--ds-unit);\n background: color-mix(in srgb, var(--ds-notice-mark) 40%, transparent);\n --ds-notice-ink: var(--ds-text-color);\n}\n\n.ds-notice--banner .ds-notice__mark {\n flex: none;\n margin-block: 0;\n}\n\n.ds-notice--banner .ds-notice__title,\n.ds-notice--banner .ds-notice__text {\n min-width: 0;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.ds-notice--banner .ds-notice__text {\n flex: 1;\n}\n\n.ds-notice--banner .ds-notice__actions {\n flex: none;\n min-height: 0;\n}\n\n.ds-notice--banner .ds-notice__dismiss {\n margin-block: 0;\n margin-inline-start: 0;\n}\n\n/* The app banner: a row at the top of the page, in the flow, three squares\n high: half a square above and below two squares of content, a hairline at\n its foot. The app's picture fills two squares with a picture's corners; the\n name and the line about it take a square each, on the sixteen; Open and the\n close stand on the sixteen of the square in the middle of the two. */\n.ds-notice--app {\n grid-template-columns: calc(2 * var(--ds-unit)) minmax(0, 1fr) auto var(--ds-unit);\n grid-template-rows: var(--ds-unit) var(--ds-unit);\n column-gap: calc(var(--ds-unit) / 2);\n align-items: end;\n padding: calc(var(--ds-unit) / 2) var(--ds-unit);\n background: var(--ds-paper);\n box-shadow: inset 0 -1px 0 var(--ds-border-color);\n line-height: var(--ds-type-lineheight-default);\n}\n\n.ds-notice--app[hidden] {\n display: none;\n}\n\n.ds-notice__picture {\n box-sizing: border-box;\n grid-column: 1;\n grid-row: 1 / 3;\n align-self: stretch;\n overflow: hidden;\n border-radius: var(--ds-radius);\n background: var(--ds-fill);\n outline: 1px solid var(--ds-border-color);\n outline-offset: -1px;\n}\n\n.ds-notice__picture img {\n display: block;\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n\n.ds-notice--app .ds-notice__title,\n.ds-notice--app .ds-notice__text {\n grid-column: 2;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.ds-notice--app .ds-notice__title { grid-row: 1; }\n\n.ds-notice--app .ds-notice__text {\n grid-row: 2;\n color: var(--ds-text-color-muted);\n}\n\n/* Open and the close: their square starts half a square into the two, its\n control on the lower sixteen of it. */\n.ds-notice--app .ds-notice__open,\n.ds-notice--app .ds-notice__dismiss {\n grid-row: 1 / 3;\n align-self: start;\n margin: calc(1.5 * var(--ds-unit) - var(--ds-control-height)) 0 0;\n}\n\n.ds-notice--app .ds-notice__open {\n grid-column: 3;\n text-decoration: none;\n}\n\n.ds-notice--app .ds-notice__dismiss {\n grid-column: 4;\n justify-self: end;\n}\n/*\n * Overlay: everything that stands over the page, one component with a type.\n * A `modal` stands in the centre (controls.css and the element's frame); this\n * sheet draws the others, for the `ds-overlay` element (`type=\"sheet\"`,\n * `\"drawer\"` with `side`, `\"screen\"`, which mark its native dialog\n * `data-ds-type` and `data-ds-edge`) and the native frame\n * (`dialog.ds-overlay.ds-overlay--sheet`, `--drawer` with `--start`,\n * `--screen`); controls.css imports it. A popover is placed by the element.\n *\n * - A sheet is as wide as the screen up to 32 squares and centred beyond\n * that, as high as its content up to two squares below the top, with no\n * line at its foot; its one line, at the top, is drawn inside its first\n * square (`--ds-overlay-line`), so it stands on whole squares. A handle two squares wide stands in the middle of its\n * first square and stays there while the content scrolls.\n * - A drawer is the screen's height and 16 squares wide (`--ds-overlay-width`),\n * leaving at least two squares of the page beside it; its one line is on\n * the side that faces the page. `end` is the right in a left-to-right page.\n * - Each keeps the square of padding inside the screen's safe area\n * (`env(safe-area-inset-*)`, with `viewport-fit=cover` on the page).\n * - Each slides in from its edge and back out to it, in transform alone,\n * while the backdrop fades; with reduced motion it fades in place. Dragging\n * it towards its edge (0-face/overlay-swipe.ts) moves it with the finger and\n * fades the backdrop as it goes.\n * - The page behind a modal native sheet or drawer does not scroll.\n */\n\n:is(ds-overlay > dialog[data-ds-edge=\"bottom\"], dialog.ds-overlay.ds-overlay--sheet),\n:is(ds-overlay > dialog[data-ds-edge=\"end\"], dialog.ds-overlay.ds-overlay--drawer:not(.ds-overlay--start)),\n:is(ds-overlay > dialog[data-ds-edge=\"start\"], dialog.ds-overlay.ds-overlay--drawer.ds-overlay--start) {\n --ds-overlay-gap: var(--ds-unit);\n --ds-overlay-inner: var(--ds-overlay-padding, var(--ds-unit));\n /* The line it faces the page with is drawn inside its first square. */\n --ds-overlay-within: max(0px, var(--ds-overlay-inner) - var(--ds-overlay-line, 1px));\n --ds-overlay-motion: var(--ds-overlay-enter, 380ms);\n --ds-overlay-curve: var(--ds-overlay-easing, cubic-bezier(0.32, 0.72, 0, 1));\n margin: 0;\n gap: var(--ds-overlay-gap);\n transform: var(--ds-overlay-held);\n transition:\n transform var(--ds-overlay-motion) var(--ds-overlay-curve),\n opacity var(--ds-overlay-motion) var(--ds-overlay-curve),\n overlay var(--ds-overlay-motion) allow-discrete,\n display var(--ds-overlay-motion) allow-discrete;\n}\n\n/* Content that supplies its own room. */\n:is(ds-overlay > dialog[data-ds-edge], dialog.ds-overlay:is(.ds-overlay--sheet, .ds-overlay--drawer))[data-ds-overlay-flush] {\n --ds-overlay-inner: 0px;\n --ds-overlay-gap: 0px;\n}\n\n:is(ds-overlay > dialog[data-ds-edge=\"bottom\"], dialog.ds-overlay.ds-overlay--sheet) {\n --ds-overlay-held: translateY(var(--ds-overlay-drag, 0px));\n --ds-overlay-away: translateY(100%);\n inset: auto 0 0;\n width: var(--ds-overlay-width, 100%);\n max-width: min(100%, var(--ds-overlay-sheet-width, calc(32 * var(--ds-unit))));\n height: var(--ds-overlay-height, fit-content);\n max-height: calc(100dvh - var(--ds-overlay-sheet-gap, calc(2 * var(--ds-unit))) - env(safe-area-inset-top, 0px));\n margin: 0 auto;\n padding:\n var(--ds-overlay-within)\n calc(var(--ds-overlay-inner) + env(safe-area-inset-right, 0px))\n calc(var(--ds-overlay-inner) + env(safe-area-inset-bottom, 0px))\n calc(var(--ds-overlay-inner) + env(safe-area-inset-left, 0px));\n border-bottom: 0;\n}\n\n/* The handle: two squares wide, in the middle of the first square, above the\n * content as it scrolls. It takes no room: the content starts where it would. */\n:is(ds-overlay > dialog[data-ds-edge=\"bottom\"], dialog.ds-overlay.ds-overlay--sheet)::before {\n --ds-overlay-handle-height: calc(4px * var(--ds-sf, 1));\n content: \"\";\n display: var(--ds-overlay-handle, block);\n position: sticky;\n z-index: 1;\n /* A sticky offset counts from the content edge, under the sheet's top padding: take that\n padding off, so the handle is held where it already stands, in the first square. */\n top: calc((var(--ds-unit) - var(--ds-overlay-handle-height)) / 2 - var(--ds-overlay-line, 1px) - var(--ds-overlay-within));\n flex: 0 0 auto;\n align-self: center;\n width: calc(2 * var(--ds-unit));\n height: var(--ds-overlay-handle-height);\n margin-top: calc(var(--ds-overlay-inner) * -1 + (var(--ds-unit) - var(--ds-overlay-handle-height)) / 2);\n margin-bottom: calc(var(--ds-overlay-inner) - var(--ds-overlay-gap) - var(--ds-overlay-handle-height) - (var(--ds-unit) - var(--ds-overlay-handle-height)) / 2);\n border-radius: calc(var(--ds-overlay-handle-height) / 2);\n background: var(--ds-overlay-handle-color, var(--ds-border-color));\n pointer-events: none;\n}\n\n:is(ds-overlay > dialog[data-ds-edge=\"end\"], dialog.ds-overlay.ds-overlay--drawer:not(.ds-overlay--start)),\n:is(ds-overlay > dialog[data-ds-edge=\"start\"], dialog.ds-overlay.ds-overlay--drawer.ds-overlay--start) {\n --ds-overlay-held: translateX(var(--ds-overlay-drag, 0px));\n width: calc(var(--ds-overlay-width, calc(16 * var(--ds-unit))) + var(--ds-overlay-safe-side, 0px));\n max-width: calc(100vw - 2 * var(--ds-unit));\n height: 100dvh;\n max-height: none;\n border-top: 0;\n border-bottom: 0;\n}\n\n/* At the right: the end of a left-to-right page, the start of a right-to-left one. */\n:is(ds-overlay > dialog[data-ds-edge=\"end\"], dialog.ds-overlay.ds-overlay--drawer:not(.ds-overlay--start)):not(:dir(rtl)),\n:is(ds-overlay > dialog[data-ds-edge=\"start\"], dialog.ds-overlay.ds-overlay--drawer.ds-overlay--start):dir(rtl) {\n --ds-overlay-away: translateX(100%);\n --ds-overlay-safe-side: env(safe-area-inset-right, 0px);\n inset: 0 0 0 auto;\n padding:\n calc(var(--ds-overlay-inner) + env(safe-area-inset-top, 0px))\n calc(var(--ds-overlay-inner) + env(safe-area-inset-right, 0px))\n calc(var(--ds-overlay-inner) + env(safe-area-inset-bottom, 0px))\n var(--ds-overlay-within);\n border-right: 0;\n}\n\n:is(ds-overlay > dialog[data-ds-edge=\"start\"], dialog.ds-overlay.ds-overlay--drawer.ds-overlay--start):not(:dir(rtl)),\n:is(ds-overlay > dialog[data-ds-edge=\"end\"], dialog.ds-overlay.ds-overlay--drawer:not(.ds-overlay--start)):dir(rtl) {\n --ds-overlay-away: translateX(-100%);\n --ds-overlay-safe-side: env(safe-area-inset-left, 0px);\n inset: 0 auto 0 0;\n padding:\n calc(var(--ds-overlay-inner) + env(safe-area-inset-top, 0px))\n var(--ds-overlay-within)\n calc(var(--ds-overlay-inner) + env(safe-area-inset-bottom, 0px))\n calc(var(--ds-overlay-inner) + env(safe-area-inset-left, 0px));\n border-left: 0;\n}\n\n/* The page behind a native sheet or drawer holds still while it is open, as\n * the element holds it (the element saves and restores the scroll itself). */\n:root:has(dialog.ds-overlay:is(.ds-overlay--sheet, .ds-overlay--drawer):modal) {\n overflow: hidden;\n overscroll-behavior: none;\n}\n\n/* Out to its edge, a little quicker than it came; and in from it. */\n:is(ds-overlay > dialog[data-ds-edge], dialog.ds-overlay:is(.ds-overlay--sheet, .ds-overlay--drawer)):not([open]) {\n --ds-overlay-motion: var(--ds-overlay-exit, 260ms);\n transform: var(--ds-overlay-away);\n}\n\n@starting-style {\n :is(ds-overlay > dialog[data-ds-edge], dialog.ds-overlay:is(.ds-overlay--sheet, .ds-overlay--drawer))[open] {\n transform: var(--ds-overlay-away);\n }\n}\n\n:is(ds-overlay > dialog[data-ds-edge], dialog.ds-overlay:is(.ds-overlay--sheet, .ds-overlay--drawer))::backdrop {\n opacity: calc(1 - var(--ds-overlay-progress, 0));\n transition:\n opacity var(--ds-overlay-motion, 380ms) ease,\n overlay var(--ds-overlay-motion, 380ms) allow-discrete,\n display var(--ds-overlay-motion, 380ms) allow-discrete;\n}\n\n:is(ds-overlay > dialog[data-ds-edge], dialog.ds-overlay:is(.ds-overlay--sheet, .ds-overlay--drawer)):not([open])::backdrop {\n opacity: 0;\n}\n\n@starting-style {\n :is(ds-overlay > dialog[data-ds-edge], dialog.ds-overlay:is(.ds-overlay--sheet, .ds-overlay--drawer))[open]::backdrop {\n opacity: 0;\n }\n}\n\n/* While a finger holds it, it follows the finger alone. */\n:is(ds-overlay > dialog[data-ds-edge], dialog.ds-overlay:is(.ds-overlay--sheet, .ds-overlay--drawer))[data-ds-overlay-dragging],\n:is(ds-overlay > dialog[data-ds-edge], dialog.ds-overlay:is(.ds-overlay--sheet, .ds-overlay--drawer))[data-ds-overlay-dragging]::backdrop {\n transition: none;\n}\n\n/* Reduced motion: no slide. It fades where it stands, a dragged one too. */\n@media (prefers-reduced-motion: reduce) {\n :is(ds-overlay > dialog[data-ds-edge], dialog.ds-overlay:is(.ds-overlay--sheet, .ds-overlay--drawer)) {\n --ds-overlay-motion: 150ms;\n transition:\n opacity var(--ds-overlay-motion) ease,\n overlay var(--ds-overlay-motion) allow-discrete,\n display var(--ds-overlay-motion) allow-discrete;\n }\n\n :is(ds-overlay > dialog[data-ds-edge], dialog.ds-overlay:is(.ds-overlay--sheet, .ds-overlay--drawer)):not([open]) {\n --ds-overlay-motion: 150ms;\n transform: var(--ds-overlay-held);\n opacity: 0;\n }\n\n @starting-style {\n :is(ds-overlay > dialog[data-ds-edge], dialog.ds-overlay:is(.ds-overlay--sheet, .ds-overlay--drawer))[open] {\n transform: var(--ds-overlay-held);\n opacity: 0;\n }\n }\n}\n\n/*\n * The screen: an overlay over the whole screen, for navigation folded away on\n * a narrow screen (`ds-header`, `ds-page drawer`, `attachOverlay()`).\n *\n * The panel is the dialog frame (`dialog.ds-overlay`) laid over the whole\n * screen, on the page's own paper, a square of room on every side. Its first\n * row holds the close button where the menu button stood; what the page\n * folded away (its navigation and actions) follows, every link one square\n * high. The page script `0-face/attach-overlay.ts` opens it: it moves the folded parts\n * into the panel and back, keeps focus inside, closes on Escape, on a link and\n * when the screen widens, and holds the page's scroll while it is open.\n * `ds-header` and `ds-page drawer` use the same script and these rules.\n *\n * ds-overlay--screen on a <dialog class=\"ds-overlay\">, the panel\n * ds-overlay__bar the first row: the close button at its end\n * ds-overlay__close the close button, an icon button\n * ds-overlay__toggle the button that opens it: it carries\n * two icons, the first shown while closed and the\n * second (close) while open\n *\n * Tokens: `--ds-overlay-paper` (the page's paper by default).\n */\n\n:is(ds-overlay > dialog[data-ds-type=\"screen\"], dialog.ds-overlay.ds-overlay--screen) {\n --ds-overlay-width: 100vw;\n width: 100vw;\n max-width: none;\n height: 100dvh;\n max-height: none;\n margin: 0;\n padding: var(--ds-unit);\n gap: 0;\n border: 0;\n background: var(--ds-overlay-paper, var(--background, Canvas));\n overflow-y: auto;\n}\n\n:is(ds-overlay > dialog[data-ds-type=\"screen\"], dialog.ds-overlay.ds-overlay--screen)[open] {\n display: flex;\n flex-direction: column;\n align-items: stretch;\n gap: var(--ds-unit);\n}\n\n/* Content that supplies its own room, as the documentation's navigation does. */\n:is(ds-overlay > dialog[data-ds-type=\"screen\"], dialog.ds-overlay.ds-overlay--screen)[data-ds-overlay-flush] {\n padding: 0;\n gap: 0;\n}\n\n:is(ds-overlay > dialog[data-ds-type=\"screen\"], dialog.ds-overlay.ds-overlay--screen)::backdrop {\n background: transparent;\n}\n\n/* It arrives by fading in and settling half a square, and leaves at once. */\n@media (prefers-reduced-motion: no-preference) {\n :is(ds-overlay > dialog[data-ds-type=\"screen\"], dialog.ds-overlay.ds-overlay--screen)[open] {\n transition: opacity 160ms ease-out, transform 160ms ease-out;\n }\n\n @starting-style {\n :is(ds-overlay > dialog[data-ds-type=\"screen\"], dialog.ds-overlay.ds-overlay--screen)[open] {\n opacity: 0;\n transform: translateY(calc(var(--ds-unit) / -2));\n }\n }\n}\n\n.ds-overlay__bar {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n min-height: var(--ds-row-height);\n}\n\n/* Centred in the last square of the row, as the menu button is. Two classes,\n so the button's own margin does not undo it. */\n.ds-overlay__bar > .ds-overlay__close {\n margin-inline-end: calc((var(--ds-unit) - var(--ds-control-height)) / 2);\n}\n\n/* The toggle shows its first icon while closed and its second while open; the\n close button, a copy of it, shows the second. */\n.ds-overlay__toggle:not([aria-expanded=\"true\"]) > :nth-child(2),\n.ds-overlay__toggle[aria-expanded=\"true\"] > :first-child:not(:only-child),\n.ds-overlay__close > :first-child:not(:only-child) {\n display: none;\n}\n/*\n * A site header, as plain classes on native elements.\n *\n * One row of one square, a square of room around it: the brand at the start\n * in twelve squares (as a page's navigation column is, so the links stand\n * over the work), the navigation two squares after it, the actions at the end. Every link is\n * one square high, a square apart, in the text's ink; the page you are on\n * (`aria-current=\"page\"`) stands on a hairline of the full ink, the others\n * show a faint one under the pointer. Below thirty squares of width the\n * navigation and the actions fold into a drawer: the menu button takes their\n * place at the end of the row, and `0-face/header.ts` opens them over the page.\n * The `ds-header` element draws the same parts.\n *\n * ds-header the row, a <header>\n * ds-header__brand the mark or name, a link home; a picture in it is one square high\n * ds-header__nav the links, a <nav>\n * ds-header__actions buttons or links at the end\n * ds-header__toggle the menu button: `.ds-button.ds-button--icon.ds-overlay__toggle`\n * with a menu icon and a close icon, shown only on a narrow screen\n * ds-header--sticky stays at the top of the page as it scrolls, on the paper\n *\n * Tokens: `--ds-header-brand` (the brand's width, at least twelve squares),\n * `--ds-header-paper` (the sticky header's paper) and `--ds-header-line` (the\n * hairline under a sticky header, none by default).\n */\n\n.ds-header {\n box-sizing: border-box;\n container-type: inline-size;\n display: flex;\n align-items: center;\n gap: calc(2 * var(--ds-unit));\n min-width: 0;\n margin: 0;\n padding: var(--ds-unit);\n color: var(--ds-text-color);\n font-family: var(--ds-typeface-regular);\n font-feature-settings: var(--ds-font-features);\n font-size: var(--ds-type-size-default);\n font-weight: var(--ds-type-weight-default);\n line-height: var(--ds-unit);\n letter-spacing: var(--ds-type-letterspacing-default);\n text-transform: none;\n}\n\n.ds-header--sticky {\n position: sticky;\n top: 0;\n z-index: 1;\n background: var(--ds-header-paper, var(--background, Canvas));\n box-shadow: inset 0 -1px var(--ds-header-line, transparent);\n}\n\n.ds-header__brand {\n box-sizing: border-box;\n display: inline-flex;\n flex: 0 0 auto;\n min-width: var(--ds-header-brand, calc(12 * var(--ds-unit)));\n align-items: center;\n gap: calc(var(--ds-unit) / 2);\n height: var(--ds-unit);\n color: inherit;\n font-family: var(--ds-typeface-medium);\n font-weight: var(--ds-type-weight-bold);\n text-decoration: none;\n white-space: nowrap;\n}\n\n.ds-header__brand :is(img, svg) {\n display: block;\n width: auto;\n height: var(--ds-unit);\n}\n\n.ds-header__nav,\n.ds-header__actions {\n display: flex;\n align-items: center;\n gap: var(--ds-unit);\n min-width: 0;\n margin: 0;\n padding: 0;\n}\n\n.ds-header__nav {\n flex: 1 1 auto;\n flex-wrap: wrap;\n row-gap: 0;\n}\n\n.ds-header__actions {\n flex: 0 0 auto;\n justify-content: flex-end;\n gap: calc(var(--ds-unit) / 2);\n margin-inline-start: auto;\n}\n\n/* A link in the navigation: the text's ink on a one-square line. */\n.ds-header__nav a {\n display: block;\n height: var(--ds-unit);\n color: inherit;\n text-decoration-line: underline;\n text-decoration-thickness: 1px;\n text-decoration-color: transparent;\n text-underline-offset: calc(2px * var(--ds-sf));\n white-space: nowrap;\n}\n\n.ds-header__nav a:hover {\n text-decoration-color: color-mix(in srgb, currentColor 40%, transparent);\n}\n\n.ds-header__nav a[aria-current]:not([aria-current=\"false\"]) {\n text-decoration-color: currentColor;\n}\n\n.ds-header__nav a:focus-visible,\n.ds-header__brand:focus-visible {\n outline: 2px solid var(--ds-focus-ring);\n outline-offset: 1px;\n}\n\n/* The menu button stands at the end of the row, and only on a narrow screen.\n Two classes, so the button's own display and margin do not undo it. */\n.ds-header > .ds-header__toggle {\n display: none;\n margin-inline-start: auto;\n /* Centred in the last square of the row, where the close button stands too. */\n margin-inline-end: calc((var(--ds-unit) - var(--ds-control-height)) / 2);\n}\n\n@container (max-width: 599px) {\n .ds-header > :is(.ds-header__nav, .ds-header__actions) {\n display: none;\n }\n\n .ds-header > .ds-header__toggle {\n display: inline-flex;\n }\n\n .ds-header > .ds-header__brand {\n min-width: 0;\n }\n}\n\n/* In the drawer the navigation is a column of one-square links, and the\n actions a row one square below it. */\n.ds-overlay--screen :is(.ds-header__nav, .ds-header__actions) {\n flex: 0 0 auto;\n flex-direction: column;\n align-items: flex-start;\n flex-wrap: nowrap;\n gap: 0;\n margin: 0;\n}\n\n.ds-overlay--screen .ds-header__actions {\n flex-direction: row;\n flex-wrap: wrap;\n gap: calc(var(--ds-unit) / 2);\n}\n/*\n * A site footer, as plain classes on native elements.\n *\n * The brand and its line at the start, twelve squares wide; the columns of\n * links beside it, eight squares each, two squares apart; then, two squares\n * below, the legal line at the start and the preferences (language and theme,\n * as `ds-cycle` or native buttons) at the end. Every line is one square, the\n * column headings in the title grey, the links in the text's ink with a faint\n * hairline under the pointer. A hairline runs along its top. Columns that do\n * not fit beside the brand go below it, and on a narrow screen everything\n * stands in one column. The `ds-footer` element draws the same parts from data.\n *\n * ds-footer the block, a <footer>\n * ds-footer__brand the mark and its line\n * ds-footer__mark the mark or name, a link home; a picture in it is one square high\n * ds-footer__tagline a line about the site, muted\n * ds-footer__columns the columns, a <nav> or <div>\n * ds-footer__column one column: a heading and its links\n * ds-footer__heading the column's heading, one line in the title grey\n * ds-footer__links the links, a <ul> with one link per <li>\n * ds-footer__base the last row, two squares below\n * ds-footer__legal in it, the copyright and legal line, muted\n * ds-footer__preferences in it, the language and theme controls, at its end\n *\n * Tokens: `--ds-footer-line` (the hairline along the top).\n */\n\n.ds-footer {\n box-sizing: border-box;\n container-type: inline-size;\n display: flex;\n flex-wrap: wrap;\n align-items: flex-start;\n gap: calc(2 * var(--ds-unit));\n min-width: 0;\n margin: 0;\n padding: calc(2 * var(--ds-unit)) var(--ds-unit) var(--ds-unit);\n box-shadow: inset 0 1px var(--ds-footer-line, var(--ds-border-color));\n color: var(--ds-text-color);\n font-family: var(--ds-typeface-regular);\n font-feature-settings: var(--ds-font-features);\n font-size: var(--ds-type-size-default);\n font-weight: var(--ds-type-weight-default);\n line-height: var(--ds-unit);\n letter-spacing: var(--ds-type-letterspacing-default);\n text-transform: none;\n}\n\n.ds-footer :is(p, h2, h3, h4, ul) {\n margin: 0;\n font: inherit;\n letter-spacing: inherit;\n}\n\n.ds-footer__brand {\n display: flex;\n flex: 0 0 calc(12 * var(--ds-unit));\n flex-direction: column;\n max-width: 100%;\n min-width: 0;\n}\n\n.ds-footer__mark {\n display: inline-flex;\n align-items: center;\n gap: calc(var(--ds-unit) / 2);\n align-self: flex-start;\n height: var(--ds-unit);\n color: inherit;\n font-family: var(--ds-typeface-medium);\n font-weight: var(--ds-type-weight-bold);\n text-decoration: none;\n}\n\n.ds-footer__mark :is(img, svg) {\n display: block;\n width: auto;\n height: var(--ds-unit);\n}\n\n.ds-footer__tagline,\n.ds-footer__legal {\n color: var(--ds-text-color-muted);\n}\n\n.ds-footer__columns {\n display: flex;\n /* Never narrower than one column: below that the columns go under the brand. */\n flex: 1 1 calc(8 * var(--ds-unit));\n flex-wrap: wrap;\n gap: var(--ds-unit) calc(2 * var(--ds-unit));\n min-width: min(100%, calc(8 * var(--ds-unit)));\n}\n\n.ds-footer__column {\n display: flex;\n flex: 0 0 calc(8 * var(--ds-unit));\n flex-direction: column;\n max-width: 100%;\n min-width: 0;\n}\n\n.ds-footer .ds-footer__heading {\n color: var(--ds-title-color);\n}\n\n.ds-footer .ds-footer__links {\n padding: 0;\n list-style: none;\n}\n\n.ds-footer__links li {\n margin: 0;\n padding: 0;\n}\n\n.ds-footer__links a {\n display: inline-block;\n max-width: 100%;\n overflow: hidden;\n color: inherit;\n text-decoration-line: underline;\n text-decoration-thickness: 1px;\n text-decoration-color: transparent;\n text-overflow: ellipsis;\n text-underline-offset: calc(2px * var(--ds-sf));\n vertical-align: top;\n white-space: nowrap;\n}\n\n.ds-footer__links a:hover {\n text-decoration-color: color-mix(in srgb, currentColor 40%, transparent);\n}\n\n.ds-footer__links a[aria-current]:not([aria-current=\"false\"]) {\n text-decoration-color: currentColor;\n}\n\n:is(.ds-footer__links a, .ds-footer__mark):focus-visible {\n outline: 2px solid var(--ds-focus-ring);\n outline-offset: 1px;\n}\n\n/* The last row, two squares below: the legal line at the start, the\n preferences at the end; on a narrow screen the preferences go first. */\n.ds-footer__base {\n display: flex;\n flex: 0 0 100%;\n flex-wrap: wrap;\n align-items: flex-start;\n gap: var(--ds-unit) calc(2 * var(--ds-unit));\n min-width: 0;\n}\n\n.ds-footer__legal {\n flex: 1 1 auto;\n min-width: 0;\n}\n\n.ds-footer__preferences {\n display: flex;\n flex: 0 0 auto;\n flex-wrap: wrap;\n align-items: center;\n gap: 0 calc(var(--ds-unit) / 2);\n min-height: var(--ds-unit);\n margin-inline-start: auto;\n}\n\n@container (max-width: 599px) {\n .ds-footer__brand,\n .ds-footer__columns {\n flex-basis: 100%;\n }\n\n .ds-footer__base {\n flex-direction: column;\n }\n\n .ds-footer__preferences {\n order: -1;\n margin-inline-start: 0;\n }\n}\n/*\n * A document page: terms, a privacy policy, a legal notice, a help article.\n *\n * The contents stand at the start, twelve squares wide, and stay in view as\n * the writing scrolls past; the writing, two squares after them, is a reading\n * column of at most thirty-two squares, native headings and paragraphs marked\n * `data-ds-prose` (prose.css draws them). Where the two do not fit side by\n * side the contents stand above the writing. Every line is one square; the\n * contents' links are in the text's ink, the section in view (`aria-current`)\n * on a hairline of the full ink, a third level half a square in. The\n * `ds-document` element builds the contents from the headings and marks the\n * one in view.\n *\n * ds-document the page's two columns\n * ds-document__toc the contents, a <nav>, as tall as the writing\n * ds-document__contents inside it, what stays in view: the title and the list\n * ds-document__toc-title \"Contents\", one line in the title grey\n * ds-document__toc-list the links, an <ol> with one link per <li>\n * ds-document__toc-sub an <li> for a third-level heading\n * ds-document__body the writing, marked data-ds-prose\n *\n * A definition table (`ds-definitions` on a <dl>) sets terms beside what they\n * mean, as a legal notice such as 特定商取引法に基づく表記 does: the term eight\n * squares wide in the muted ink, its definition beside it, each pair on a\n * hairline at its foot with half a square above and below, as a prose table's\n * rows are. Pairs may stand in a <div> each. Below twenty-four squares the\n * definition goes under its term. It works inside or outside prose.\n *\n * Tokens: `--ds-document-top` (where the contents stop below the top of the\n * view, one square; add a sticky header's height), `--ds-definitions-term`\n * (the term column, eight squares).\n */\n\n.ds-document {\n box-sizing: border-box;\n display: flex;\n flex-wrap: wrap;\n align-items: stretch;\n gap: var(--ds-unit) calc(2 * var(--ds-unit));\n min-width: 0;\n color: var(--ds-text-color);\n font-family: var(--ds-typeface-regular);\n font-feature-settings: var(--ds-font-features);\n font-size: var(--ds-type-size-default);\n font-weight: var(--ds-type-weight-default);\n line-height: var(--ds-unit);\n letter-spacing: var(--ds-type-letterspacing-default);\n}\n\n/* The contents column stretches to the writing's height when the two stand\n side by side, so what is inside it can stay in view; standing above the\n writing it is only as tall as itself, and nothing sticks. */\n.ds-document__toc {\n flex: 0 0 calc(12 * var(--ds-unit));\n max-width: 100%;\n min-width: 0;\n}\n\n.ds-document__contents {\n position: sticky;\n top: var(--ds-document-top, var(--ds-unit));\n}\n\n.ds-document__toc-title {\n margin: 0;\n color: var(--ds-title-color);\n font: inherit;\n letter-spacing: inherit;\n}\n\n.ds-document__toc-list,\n.ds-document__toc-list ol {\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.ds-document__toc-list li {\n margin: 0;\n padding: 0;\n}\n\n.ds-document__toc-list .ds-document__toc-sub {\n padding-inline-start: calc(var(--ds-unit) / 2);\n}\n\n.ds-document__toc-list a {\n display: block;\n overflow: hidden;\n color: inherit;\n text-decoration-line: underline;\n text-decoration-thickness: 1px;\n text-decoration-color: transparent;\n text-overflow: ellipsis;\n text-underline-offset: calc(2px * var(--ds-sf));\n white-space: nowrap;\n}\n\n.ds-document__toc-list a:hover {\n text-decoration-color: color-mix(in srgb, currentColor 40%, transparent);\n}\n\n.ds-document__toc-list a[aria-current]:not([aria-current=\"false\"]) {\n text-decoration-color: currentColor;\n}\n\n.ds-document__toc-list a:focus-visible {\n outline: 2px solid var(--ds-focus-ring);\n outline-offset: -1px;\n}\n\n/* The writing: at least twenty squares beside the contents, else below them. */\n.ds-document__body {\n flex: 1 1 0;\n min-width: min(100%, calc(20 * var(--ds-unit)));\n max-width: calc(32 * var(--ds-unit));\n}\n\n/* A heading the contents jump to stops where the contents do. */\n.ds-document__body :is(h1, h2, h3, h4, h5, h6) {\n scroll-margin-top: var(--ds-document-top, var(--ds-unit));\n}\n\n/* A definition table. */\n\n.ds-definitions {\n box-sizing: border-box;\n container-type: inline-size;\n display: grid;\n grid-template-columns: minmax(0, var(--ds-definitions-term, calc(8 * var(--ds-unit)))) minmax(0, 1fr);\n margin: 0;\n padding: 0;\n color: var(--ds-text-color);\n font-family: var(--ds-typeface-regular);\n font-size: var(--ds-type-size-default);\n line-height: var(--ds-unit);\n letter-spacing: var(--ds-type-letterspacing-default);\n}\n\n.ds-definitions > div {\n display: contents;\n}\n\n.ds-definitions :is(dt, dd) {\n box-sizing: border-box;\n min-width: 0;\n margin: 0;\n padding-block: calc(var(--ds-unit) / 2);\n box-shadow: inset 0 -1px var(--ds-border-color);\n font: inherit;\n letter-spacing: inherit;\n line-height: var(--ds-unit);\n overflow-wrap: anywhere;\n}\n\n.ds-definitions dt {\n grid-column: 1;\n padding-inline-end: calc(var(--ds-unit) / 2);\n color: var(--ds-text-color-muted);\n}\n\n.ds-definitions dd {\n grid-column: 2;\n}\n\n@container (max-width: 479px) {\n .ds-definitions dt,\n .ds-definitions dd {\n grid-column: 1 / -1;\n }\n\n .ds-definitions dt {\n padding-block-end: 0;\n padding-inline-end: 0;\n box-shadow: none;\n }\n\n .ds-definitions dd {\n padding-block-start: 0;\n }\n}\n/*\n * A bento, as plain classes on native elements: tiles of different sizes\n * standing half a square (10px) apart, each a card (`.ds-card`, or any box)\n * that holds one thing. The `ds-bento` element draws the same.\n *\n * Flow (the default): columns that share the width (`--ds-bento-columns`,\n * three), a tile one column wide or more (`data-span`, or\n * `--ds-bento-span`) and one row high or more (`data-rows`, or\n * `--ds-bento-rows`); a row is as high as its tallest tile, at least\n * `--ds-bento-row` (ten squares by default). Below thirty squares of width\n * the bento is one column.\n *\n * Squares (`data-squares`): the bento is exactly `--ds-bento-columns` squares wide\n * and every tile is placed on whole squares (`data-place=\"x y w h\"`,\n * counted from 0). Tiles are placed a square apart, and a tile's surface\n * reaches a quarter square towards each tile beside it, so they stand half a\n * square apart while what they hold stays on the squares, and the bento's\n * own edges stay on the page's. `placeBento()` (0-face/bento.ts) reads the\n * places and sets the reach; the element does it itself.\n *\n * data-span=\"2\" a tile two columns wide (to 6)\n * data-rows=\"2\" a tile two rows high (to 6)\n * data-place=\"0 0 15 7\" a tile's square, width and height in squares\n *\n * Tokens: `--ds-bento-gap` (half a square), `--ds-bento-columns`,\n * `--ds-bento-row`, and on a tile `--ds-bento-span`, `--ds-bento-rows`.\n */\n\n.ds-bento {\n --ds-bento-gap: calc(var(--ds-unit) / 2);\n box-sizing: border-box;\n display: grid;\n grid-template-columns: repeat(var(--ds-bento-columns, 3), minmax(0, 1fr));\n grid-auto-rows: minmax(var(--ds-bento-row, calc(10 * var(--ds-unit))), auto);\n grid-auto-flow: row dense;\n gap: var(--ds-bento-gap);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n.ds-bento[hidden] { display: none; }\n\n.ds-bento > * {\n --ds-card-width: auto;\n --ds-card-height: auto;\n box-sizing: border-box;\n min-width: 0;\n min-height: 0;\n grid-column: span var(--ds-bento-span, 1);\n grid-row: span var(--ds-bento-rows, 1);\n}\n.ds-bento > [data-span=\"2\"] { --ds-bento-span: 2; }\n.ds-bento > [data-span=\"3\"] { --ds-bento-span: 3; }\n.ds-bento > [data-span=\"4\"] { --ds-bento-span: 4; }\n.ds-bento > [data-span=\"5\"] { --ds-bento-span: 5; }\n.ds-bento > [data-span=\"6\"] { --ds-bento-span: 6; }\n.ds-bento > [data-rows=\"2\"] { --ds-bento-rows: 2; }\n.ds-bento > [data-rows=\"3\"] { --ds-bento-rows: 3; }\n.ds-bento > [data-rows=\"4\"] { --ds-bento-rows: 4; }\n.ds-bento > [data-rows=\"5\"] { --ds-bento-rows: 5; }\n.ds-bento > [data-rows=\"6\"] { --ds-bento-rows: 6; }\n\n@media (max-width: 600px) {\n .ds-bento:not([data-squares]) { grid-template-columns: minmax(0, 1fr); }\n .ds-bento:not([data-squares]) > * { grid-column: auto; }\n}\n\n/* Squares: every tile on whole squares, a square apart, its surface a quarter square nearer each neighbour. */\n.ds-bento[data-squares] {\n --ds-bento-reach: calc(var(--ds-unit) / 4);\n /* Exactly its squares wide: each column one square. */\n width: calc(var(--ds-bento-columns, 48) * var(--ds-unit));\n max-width: none;\n grid-template-columns: repeat(var(--ds-bento-columns, 48), var(--ds-unit));\n grid-auto-rows: var(--ds-unit);\n grid-auto-flow: row;\n gap: 0;\n}\n.ds-bento[data-squares] > * {\n --ds-bento-inset-top: calc(var(--ds-bento-reach) * var(--ds-bento-top, 0));\n --ds-bento-inset-end: calc(var(--ds-bento-reach) * var(--ds-bento-end, 0));\n --ds-bento-inset-bottom: calc(var(--ds-bento-reach) * var(--ds-bento-bottom, 0));\n --ds-bento-inset-start: calc(var(--ds-bento-reach) * var(--ds-bento-start, 0));\n grid-column: calc(var(--ds-bento-x, 0) + 1) / span var(--ds-bento-w, 4);\n grid-row: calc(var(--ds-bento-y, 0) + 1) / span var(--ds-bento-h, 4);\n margin: calc(-1 * var(--ds-bento-inset-top)) calc(-1 * var(--ds-bento-inset-end)) calc(-1 * var(--ds-bento-inset-bottom)) calc(-1 * var(--ds-bento-inset-start));\n}\n/* A card grows its own padding by the reach (card.css); any other tile is padded here, a square inside. */\n.ds-bento[data-squares] > :not(.ds-card, ds-card) {\n padding:\n calc(var(--ds-unit) + var(--ds-bento-inset-top))\n calc(var(--ds-unit) + var(--ds-bento-inset-end))\n calc(var(--ds-unit) + var(--ds-bento-inset-bottom))\n calc(var(--ds-unit) + var(--ds-bento-inset-start));\n}\n/*\n * A carousel, as plain classes on native elements.\n *\n * A carousel is a row of slides that snaps sideways, one slide to a stop,\n * with a row of controls one square high under it: optional previous and\n * next buttons and a dot for each slide. Slides are the track's width, or\n * whole squares wide, a whole number of squares apart (one by default). The\n * controls stand one square below the track, on the grid from its start.\n * The `ds-carousel` element draws the same parts, and\n * `0-face/carousel.ts` gives the native markup its behaviour.\n *\n * ds-carousel the region: track, controls, status;\n * `data-slides=\"auto\"` for slides of their\n * own width, `data-bleed` `end` or `both`,\n * `data-snap=\"none\"` to slide freely\n * ds-carousel__track the scroller; every child is a slide\n * ds-carousel__controls the row of buttons and dots\n * ds-carousel__previous, __next, icon buttons, on `.ds-button.ds-button--icon`\n * __pause\n * ds-carousel__status the polite announcement, kept out of sight\n *\n * The dots are `ds-progress--dots` from progress.css.\n *\n * A strip of slides of their own width can run on past its box to the\n * window's end edge, or to both edges, while its first slide keeps to the\n * box's start, so it lines up with the page's column; `--ds-carousel-before`\n * and `--ds-carousel-after` are the room to the edges, which the script\n * measures.\n *\n * Tokens: `--ds-carousel-slide` (a slide's width, the track's by default)\n * and `--ds-carousel-gap` (between slides, one square by default).\n */\n\n.ds-carousel {\n box-sizing: border-box;\n display: grid;\n grid-template-columns: minmax(0, 1fr);\n row-gap: var(--ds-unit);\n position: relative;\n min-width: 0;\n margin: 0;\n}\n\n.ds-carousel__track {\n box-sizing: border-box;\n display: grid;\n grid-auto-flow: column;\n grid-auto-columns: var(--ds-carousel-slide, 100%);\n column-gap: var(--ds-carousel-gap, var(--ds-unit));\n min-width: 0;\n margin: 0;\n padding: 0;\n list-style: none;\n overflow-x: auto;\n overflow-y: hidden;\n overscroll-behavior-x: contain;\n scroll-snap-type: x mandatory;\n scrollbar-width: none;\n}\n\n.ds-carousel__track::-webkit-scrollbar {\n display: none;\n}\n\n/* Slides of their own width, several in view. */\n.ds-carousel[data-slides=\"auto\"] > .ds-carousel__track {\n grid-auto-columns: max-content;\n}\n\n.ds-carousel[data-snap=\"none\"] > .ds-carousel__track {\n scroll-snap-type: none;\n}\n\n/* To the window's end edge: the track reaches out over the room after the\n box and keeps a gap at its end, so the last slide stops short of the edge. */\n.ds-carousel[data-bleed=\"end\"] > .ds-carousel__track {\n margin-inline-end: calc(-1 * var(--ds-carousel-after, 0px));\n padding-inline-end: var(--ds-carousel-gap, var(--ds-unit));\n}\n\n/* To both edges: the track reaches out both ways, and the first slide, and\n each one it stops at, still stands on the box's start. */\n.ds-carousel[data-bleed=\"both\"] > .ds-carousel__track {\n margin-inline: calc(-1 * var(--ds-carousel-before, 0px)) calc(-1 * var(--ds-carousel-after, 0px));\n padding-inline: var(--ds-carousel-before, 0px) var(--ds-carousel-gap, var(--ds-unit));\n scroll-padding-inline-start: var(--ds-carousel-before, 0px);\n}\n\n.ds-carousel__track:focus-visible {\n outline: 2px solid var(--ds-focus-ring);\n outline-offset: 1px;\n}\n\n.ds-carousel__track > * {\n box-sizing: border-box;\n min-width: 0;\n margin: 0;\n scroll-snap-align: start;\n}\n\n/* One slide to a stop; a strip of several in view may pass a few. */\n.ds-carousel:not([data-slides=\"auto\"]) > .ds-carousel__track > * {\n scroll-snap-stop: always;\n}\n\n.ds-carousel__controls {\n display: flex;\n align-items: center;\n height: var(--ds-row-height);\n min-width: 0;\n}\n\n/* The buttons take a one-square cell each, as the dots do. Two classes, so\n the button's own reset of its margin does not undo it. */\n.ds-carousel .ds-carousel__previous,\n.ds-carousel .ds-carousel__next,\n.ds-carousel .ds-carousel__pause {\n flex: none;\n margin: calc((var(--ds-unit) - var(--ds-control-height)) / 2);\n}\n\n/* Previous points back along the reading direction. */\n.ds-carousel:dir(rtl) :is(.ds-carousel__previous, .ds-carousel__next) > * {\n transform: scaleX(-1);\n}\n\n.ds-carousel__status {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n}\n/*\n * A card, as plain classes on native elements.\n *\n * A card holds one thing on a fill of its own: its name in the muted ink on\n * one square, a title in the display size on lines of a square and a half, writing,\n * and a picture of it (`ds-card__media`) a square below, which can run off\n * the card's foot and end edge and be cut there, never framed. Its size is\n * given in whole squares, its padding one square, its corners a card's\n * (`--ds-radius-card`). A card that opens something is one press target, an\n * `<a class=\"ds-card\" href>`: the pointer tints it, keyboard focus rings it\n * 2px inside and its picture rises a little. Ordinary writing still sits\n * straight on the paper; a card is for a thing shown among others, as in a\n * slide. The `ds-card` element draws the same parts.\n *\n * ds-card the card; `data-tone` names a palette colour\n * for its fill (the colour's pastel, dim in dark\n * mode, from palette.css, which one.css imports),\n * `ink` for the ink with the paper's words\n * ds-card--outlined a 1px line and no fill\n * ds-card__label its name, muted, on one square\n * ds-card__title the title, display size, a square and a half a line\n * ds-card__body writing, one square a line\n * ds-card__media the picture, a square below the words\n * ds-card__media--center standing in the card's middle\n * ds-card__media--bleed running on past the padding to the end edge\n *\n * Tokens: `--ds-card-width` and `--ds-card-height` (whole squares; the\n * content's size by default), `--ds-card-padding` (one square), and\n * `--ds-card-fill`, `--ds-card-ink` and `--ds-card-muted`.\n */\n\n.ds-card {\n --ds-card-fill: var(--ds-fill);\n --ds-card-ink: var(--ds-text-color);\n --ds-card-muted: var(--ds-text-color-muted);\n --ds-card-padding: var(--ds-unit);\n position: relative;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n align-items: stretch;\n width: var(--ds-card-width, auto);\n height: var(--ds-card-height, auto);\n min-width: 0;\n margin: 0;\n /* In a squares bento the surface reaches a quarter square towards its neighbours (`--ds-bento-inset-*`, 0 elsewhere), and the padding grows by as much so what it holds stays on the squares. */\n padding:\n calc(var(--ds-card-padding) + var(--ds-bento-inset-top, 0px))\n calc(var(--ds-card-padding) + var(--ds-bento-inset-end, 0px))\n var(--ds-bento-inset-bottom, 0px)\n calc(var(--ds-card-padding) + var(--ds-bento-inset-start, 0px));\n overflow: hidden;\n border-radius: var(--ds-radius-card);\n background: var(--ds-card-fill);\n color: var(--ds-card-ink);\n font-family: var(--ds-typeface-regular);\n font-size: var(--ds-type-size-default);\n text-decoration: none;\n -webkit-tap-highlight-color: transparent;\n}\n\n.ds-card[hidden] {\n display: none;\n}\n\n.ds-card--outlined {\n --ds-card-fill: transparent;\n box-shadow: inset 0 0 0 1px var(--ds-border-color);\n}\n\n.ds-card[data-tone=\"apple-green\"] { --ds-card-fill: var(--one-apple-green-pastel); }\n.ds-card[data-tone=\"chartreuse\"] { --ds-card-fill: var(--one-chartreuse-pastel); }\n.ds-card[data-tone=\"every-green\"] { --ds-card-fill: var(--one-every-green-pastel); }\n.ds-card[data-tone=\"yellow\"] { --ds-card-fill: var(--one-yellow-pastel); }\n.ds-card[data-tone=\"orange\"] { --ds-card-fill: var(--one-orange-pastel); }\n.ds-card[data-tone=\"tuned-red\"] { --ds-card-fill: var(--one-tuned-red-pastel); }\n.ds-card[data-tone=\"pink\"] { --ds-card-fill: var(--one-pink-pastel); }\n.ds-card[data-tone=\"sharp-blue\"] { --ds-card-fill: var(--one-sharp-blue-pastel); }\n.ds-card[data-tone=\"winning-blue\"] { --ds-card-fill: var(--one-winning-blue-pastel); }\n.ds-card[data-tone=\"hysteric-blue\"] { --ds-card-fill: var(--one-hysteric-blue-pastel); }\n.ds-card[data-tone=\"purple\"] { --ds-card-fill: var(--one-purple-pastel); }\n.ds-card[data-tone=\"metal-green\"] { --ds-card-fill: var(--one-metal-green-pastel); }\n.ds-card[data-tone=\"drip-yellow\"] { --ds-card-fill: var(--one-drip-yellow-pastel); }\n.ds-card[data-tone=\"sand\"] { --ds-card-fill: var(--one-sand-pastel); }\n.ds-card[data-tone=\"old-gold\"] { --ds-card-fill: var(--one-old-gold-pastel); }\n.ds-card[data-tone=\"red-for-blood\"] { --ds-card-fill: var(--one-red-for-blood-pastel); }\n.ds-card[data-tone=\"dark-red-variant\"] { --ds-card-fill: var(--one-dark-red-variant-pastel); }\n.ds-card[data-tone=\"signal-red\"] { --ds-card-fill: var(--one-signal-red-pastel); }\n.ds-card[data-tone=\"ink\"] {\n --ds-card-fill: var(--ds-black);\n --ds-card-ink: var(--ds-white);\n --ds-card-muted: color-mix(in srgb, var(--ds-white) 60%, transparent);\n box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ds-white) 8%, transparent);\n}\n\n/* Words: each line on the lower sixteen of its square. They keep their room\n in a card of a set height; only the picture is cut at the foot. */\n.ds-card__label,\n.ds-card__title,\n.ds-card__body {\n flex: none;\n}\n\n.ds-card__label {\n margin: 0;\n padding-top: calc(4px * var(--ds-sf, 1));\n color: var(--ds-card-muted);\n font: inherit;\n line-height: var(--ds-type-lineheight-default, calc(16px * var(--ds-sf, 1)));\n}\n\n.ds-card__title {\n margin: 0;\n color: var(--ds-card-ink);\n font-family: var(--ds-typeface-medium);\n font-size: var(--ds-type-size-display);\n font-weight: var(--ds-type-weight-bold);\n /* A title that wraps keeps its lines together: a square and a half each. */\n line-height: calc(1.5 * var(--ds-unit));\n letter-spacing: calc(var(--ds-display-text-tracking, -0.4px) * var(--ds-sf, 1));\n text-wrap: balance;\n}\n\n.ds-card__body {\n margin: 0;\n line-height: var(--ds-unit);\n}\n\n.ds-card__media {\n display: flex;\n flex: none;\n align-items: flex-start;\n justify-content: flex-start;\n min-width: 0;\n margin-top: var(--ds-unit);\n}\n\n.ds-card__media--center {\n justify-content: center;\n margin-inline: calc(-1 * var(--ds-card-padding));\n}\n\n.ds-card__media--bleed {\n margin-inline-end: calc(-1 * var(--ds-card-padding));\n}\n\n.ds-card__media > * {\n flex: none;\n}\n\n/* A card that opens something. */\na.ds-card,\n.ds-card[data-interactive] {\n cursor: pointer;\n}\n\na.ds-card::after,\n.ds-card[data-interactive]::after {\n content: \"\";\n position: absolute;\n inset: 0;\n border-radius: inherit;\n background: transparent;\n pointer-events: none;\n}\n\na.ds-card:focus-visible,\n.ds-card[data-interactive]:focus-visible {\n outline: 2px solid var(--ds-focus-ring);\n outline-offset: -2px;\n}\n\n@media (hover: hover) {\n a.ds-card:hover::after,\n .ds-card[data-interactive]:hover::after {\n background: color-mix(in srgb, var(--ds-card-ink) 4%, transparent);\n }\n}\n\na.ds-card:active::after,\n.ds-card[data-interactive]:active::after {\n background: color-mix(in srgb, var(--ds-card-ink) 8%, transparent);\n}\n\n@media (hover: hover) and (prefers-reduced-motion: no-preference) {\n a.ds-card > .ds-card__media,\n .ds-card[data-interactive] > .ds-card__media {\n transition: transform 240ms cubic-bezier(0.23, 1, 0.32, 1);\n }\n\n a.ds-card:hover > .ds-card__media,\n .ds-card[data-interactive]:hover > .ds-card__media {\n transform: translateY(calc(-0.25 * var(--ds-unit)));\n }\n}\n/*\n * Figures, as plain classes on native elements: a stat and a group of stats.\n * The `ds-stat` element draws the same parts; controls.css imports this\n * file. A value against its range, steps and page dots are `progress.css`.\n *\n * Everything sits on the paper with no box, every line is one square and\n * widths come in whole squares. Colour is kept for a state: the danger colour\n * for a figure that is over.\n *\n * A stat: a label, a figure and a line for its change or caption.\n *\n * ds-stat the block, as wide as its column\n * ds-stat__label one line, muted; an icon may lead it on its own square\n * ds-stat__icon the square an icon from the library stands on\n * ds-stat__figure the figure in tabular numerals, 20 on two squares\n * ds-stat__digit one digit in a cell of one width, for a face without tabular numerals\n * ds-stat__caption one line, muted, under the figure\n * ds-stat__change the change at the start of the caption, in the text colour;\n * --danger or --warning where the page knows which way is bad\n * ds-stat--danger, --warning the figure in that colour\n * ds-stats a group of stats on whole squares: columns eight\n * squares wide, one square between them and between rows\n *\n * Tokens: `--ds-stat-size` (the figure's type size before the scale, 20px)\n * and `--ds-stat-lines` (the squares its line takes, 2), `--ds-stat-typeface`\n * (the medium face), `--ds-stat-digit` (a digit cell's width, 1ch),\n * `--ds-stat-change-color`, and `--ds-stats-width` (a column's width in\n * squares, 8).\n *\n */\n\n/* ---- Stat ------------------------------------------------------------ */\n\n.ds-stat {\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n min-width: 0;\n margin: 0;\n color: var(--ds-text-color);\n font-family: var(--ds-typeface-regular);\n font-feature-settings: var(--ds-font-features);\n font-size: var(--ds-type-size-default);\n font-weight: var(--ds-type-weight-default);\n line-height: var(--ds-unit);\n letter-spacing: var(--ds-type-letterspacing-default);\n text-transform: none;\n}\n\n.ds-stat__label,\n.ds-stat__figure,\n.ds-stat__caption {\n box-sizing: border-box;\n max-width: 100%;\n margin: 0;\n font: inherit;\n letter-spacing: inherit;\n}\n\n.ds-stat__label,\n.ds-stat__caption {\n display: flex;\n align-items: flex-start;\n min-height: var(--ds-unit);\n color: var(--ds-text-color-muted);\n overflow-wrap: anywhere;\n}\n\n.ds-stat__icon {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n width: var(--ds-unit);\n height: var(--ds-unit);\n}\n\n.ds-stat__figure {\n display: block;\n overflow: hidden;\n font-family: var(--ds-stat-typeface, var(--ds-typeface-medium));\n font-size: calc(var(--ds-stat-size, var(--ds-display-text-size, 20px)) * var(--ds-sf, 1));\n font-weight: var(--ds-type-weight-bold);\n font-variant-numeric: tabular-nums;\n line-height: calc(var(--ds-stat-lines, 2) * var(--ds-unit));\n min-height: calc(var(--ds-stat-lines, 2) * var(--ds-unit));\n letter-spacing: calc(var(--ds-display-text-tracking, -0.4px) * var(--ds-sf, 1));\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n\n.ds-stat__digit {\n display: inline-block;\n width: var(--ds-stat-digit, 1ch);\n text-align: center;\n}\n\n.ds-stat--danger .ds-stat__figure {\n color: var(--ds-danger-color);\n}\n\n.ds-stat--warning .ds-stat__figure {\n color: var(--ds-warning-color);\n}\n\n.ds-stat__change {\n display: inline-flex;\n flex: none;\n align-items: center;\n height: var(--ds-unit);\n margin-inline-end: calc(4px * var(--ds-sf, 1));\n color: var(--ds-stat-change-color, var(--ds-text-color));\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n}\n\n.ds-stat__change--danger {\n --ds-stat-change-color: var(--ds-danger-color);\n}\n\n.ds-stat__change--warning {\n --ds-stat-change-color: var(--ds-warning-color);\n}\n\n.ds-stats {\n box-sizing: border-box;\n display: grid;\n grid-template-columns: repeat(auto-fill, min(100%, calc(var(--ds-stats-width, 8) * var(--ds-unit))));\n gap: var(--ds-unit);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.ds-stats > li {\n min-width: 0;\n margin: 0;\n}\n/*\n * Progress, as plain classes on native elements: a value against its range,\n * a sequence of steps, or a dot for each page. The `ds-progress` element\n * draws the same parts; controls.css imports this file.\n *\n * Everything sits on the paper with no box, every line is one square and\n * widths come in whole squares. Colour is kept for a state: the danger colour\n * for a value that is over or a step that failed, the ink for the current\n * step and the current dot.\n *\n * A value against its range, one square high (HTML's <progress>):\n *\n * ds-progress the row; role=\"meter\" with aria-valuenow, -min, -max and -valuetext\n * ds-progress__fill the bar, half a square high, drawn to --ds-progress-value (0 to 1)\n * ds-progress--squares the range in squares instead, each one square\n * ds-progress__square one square of the range; --on when it is filled\n * ds-progress--over past the end: filled whole in the danger colour\n *\n * Tokens: `--ds-progress-value`, `--ds-progress-color` (the ink by default)\n * and `--ds-progress-track` (the grey fill).\n *\n * A sequence of steps, one square high, each step on whole squares:\n *\n * ds-progress--steps with ds-progress, on an <ol>; the steps on one row, joined by a line\n * ds-progress--vertical one step to a row, each with room for its body under it\n * ds-progress__step an <li>; aria-current=\"step\" on the current one\n * ds-progress__step--done, --current, --failed states; upcoming without one\n * ds-progress__link a step that goes somewhere: an <a href> around its mark and label\n * ds-progress__mark the number (or the done or failed icon), one control high\n * ds-progress__label the step's name on one line\n * ds-progress__state the state in words for assistive technology, hidden (\"Done\")\n * ds-progress__body what belongs under a step in a vertical sequence\n *\n * A dot for each page, one square each:\n *\n * ds-progress--dots with ds-progress, a group of dots (role=\"group\")\n * ds-progress__dot a button; `data-state` is `past`, `current` (with\n * `aria-current=\"true\"`), `next`, `future`, or `end` for a\n * last dot marked as an end\n *\n * Tokens: the dot colours `--ds-progress-dot-past` and `-future` (the muted\n * text colour by default), `-current`, `-next` and `-end` (the text colour),\n * set on the dots or any element around them. Past, current and next dots are\n * filled; future dots and the end marker are rings.\n */\n\n/* ---- A value against its range: the bar, or the range in squares ---- */\n\n.ds-progress {\n box-sizing: border-box;\n position: relative;\n display: block;\n width: 100%;\n height: var(--ds-unit);\n margin: 0;\n}\n\n/* The range and its bar are a band half a square high across the middle of\n the row, as a chart's ratio is drawn. */\n.ds-progress:not(.ds-progress--squares, .ds-progress--steps, .ds-progress--dots)::before,\n.ds-progress__fill {\n position: absolute;\n inset-inline: 0;\n top: calc(var(--ds-unit) / 4);\n height: calc(var(--ds-unit) / 2);\n}\n\n.ds-progress:not(.ds-progress--squares, .ds-progress--steps, .ds-progress--dots)::before {\n content: \"\";\n background: var(--ds-progress-track, var(--ds-fill));\n}\n\n.ds-progress__fill {\n display: block;\n background: var(--ds-progress-color, var(--ds-text-color));\n transform: scaleX(var(--ds-progress-value, 0));\n transform-origin: left center;\n transition: transform 0.4s cubic-bezier(0.2, 0.8, 0.2, 1);\n}\n\n.ds-progress__fill:dir(rtl) {\n transform-origin: right center;\n}\n\n.ds-progress--over .ds-progress__fill {\n background: var(--ds-danger-color);\n transform: none;\n}\n\n.ds-progress--squares {\n display: flex;\n width: max-content;\n max-width: 100%;\n overflow: hidden;\n}\n\n.ds-progress__square {\n flex: none;\n width: calc(var(--ds-unit) - 1px);\n height: calc(var(--ds-unit) - 1px);\n margin-inline-end: 1px;\n background: var(--ds-progress-track, var(--ds-fill));\n}\n\n.ds-progress__square--on {\n background: var(--ds-progress-color, var(--ds-text-color));\n}\n\n.ds-progress--over .ds-progress__square {\n background: var(--ds-danger-color);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .ds-progress__fill {\n transition: none;\n }\n}\n\n/* ---- A sequence of steps --------------------------------------------- */\n\n.ds-progress--steps {\n box-sizing: border-box;\n position: static;\n display: flex;\n width: auto;\n height: auto;\n flex-wrap: wrap;\n align-items: flex-start;\n min-width: 0;\n margin: 0;\n padding: 0;\n list-style: none;\n color: var(--ds-text-color);\n font-family: var(--ds-typeface-regular);\n font-feature-settings: var(--ds-font-features);\n font-size: var(--ds-type-size-default);\n font-weight: var(--ds-type-weight-default);\n line-height: var(--ds-unit);\n letter-spacing: var(--ds-type-letterspacing-default);\n text-transform: none;\n}\n\n/* Each step rounds its width up to whole squares where the browser can size\n to content in calc; elsewhere it hugs its words. */\n.ds-progress__step {\n --ds-progress-mark: transparent;\n --ds-progress-mark-ink: var(--ds-text-color-muted);\n --ds-progress-mark-line: var(--ds-border-color);\n box-sizing: border-box;\n position: relative;\n display: flex;\n align-items: flex-start;\n min-width: 0;\n max-width: 100%;\n width: max-content;\n width: calc-size(max-content, round(up, size, var(--ds-unit)));\n min-height: var(--ds-unit);\n margin: 0;\n color: var(--ds-text-color-muted);\n}\n\n/* A line joins a step to the next: at least one square, across whatever the\n step's rounding leaves, a quarter of a square short of each end. */\n.ds-progress__step:not(:last-child)::after {\n content: \"\";\n flex: 1 0 var(--ds-unit);\n height: var(--ds-unit);\n background: linear-gradient(var(--ds-border-color), var(--ds-border-color)) center / calc(100% - var(--ds-unit) / 2) 1px no-repeat;\n}\n\n.ds-progress__step--done {\n --ds-progress-mark: var(--ds-fill);\n --ds-progress-mark-ink: var(--ds-text-color);\n --ds-progress-mark-line: transparent;\n color: var(--ds-text-color);\n}\n\n.ds-progress__step--current {\n --ds-progress-mark: var(--ds-text-color);\n --ds-progress-mark-ink: var(--ds-paper);\n --ds-progress-mark-line: transparent;\n color: var(--ds-text-color);\n}\n\n.ds-progress__step--failed {\n --ds-progress-mark: var(--ds-danger-color);\n --ds-progress-mark-ink: light-dark(var(--ds-white), var(--ds-black));\n --ds-progress-mark-line: transparent;\n color: var(--ds-danger-color);\n}\n\n.ds-progress__link {\n display: flex;\n align-items: flex-start;\n min-width: 0;\n color: inherit;\n text-decoration: none;\n border-radius: var(--ds-radius);\n}\n\n.ds-progress__link:hover .ds-progress__label {\n text-decoration: underline;\n text-decoration-thickness: 1px;\n text-underline-offset: calc(3px * var(--ds-sf, 1));\n}\n\n.ds-progress__link:focus-visible {\n outline: 2px solid var(--ds-focus-ring);\n outline-offset: -2px;\n}\n\n.ds-progress__mark {\n box-sizing: border-box;\n display: flex;\n flex: none;\n align-items: center;\n justify-content: center;\n width: var(--ds-control-height);\n height: var(--ds-control-height);\n margin: calc((var(--ds-unit) - var(--ds-control-height)) / 2);\n overflow: hidden;\n background: var(--ds-progress-mark);\n box-shadow: inset 0 0 0 1px var(--ds-progress-mark-line);\n color: var(--ds-progress-mark-ink);\n --ds-icon-color: var(--ds-progress-mark-ink);\n font-family: var(--ds-typeface-medium);\n font-size: var(--ds-type-size-small);\n font-weight: var(--ds-type-weight-bold);\n font-variant-numeric: tabular-nums;\n line-height: var(--ds-control-height);\n letter-spacing: 0;\n}\n\n.ds-progress__label {\n min-width: 0;\n padding-inline-end: calc(4px * var(--ds-sf, 1));\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n\n.ds-progress__step--current .ds-progress__label {\n font-family: var(--ds-typeface-medium);\n font-weight: var(--ds-type-weight-bold);\n}\n\n.ds-progress__state {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n}\n\n.ds-progress--vertical {\n flex-direction: column;\n flex-wrap: nowrap;\n}\n\n.ds-progress--vertical .ds-progress__step {\n flex-wrap: wrap;\n width: 100%;\n}\n\n.ds-progress--vertical .ds-progress__step::after {\n content: none;\n}\n\n/* Its content stands in a column, so an inline element in it (text drawn by\n an element, say) takes its own lines rather than a line box's descent. */\n.ds-progress__body {\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n flex: 1 0 100%;\n min-width: 0;\n padding-inline-start: var(--ds-unit);\n color: var(--ds-text-color);\n}\n\n/* ---- A dot for each page ------------------------------------------- */\n\n.ds-progress--dots {\n position: static;\n display: inline-flex;\n width: auto;\n flex: none;\n align-items: center;\n height: var(--ds-row-height);\n margin: 0;\n padding: 0;\n}\n\n.ds-progress__dot {\n --ds-progress-dot-color: var(--ds-progress-dot-future, var(--ds-text-color-muted));\n appearance: none;\n box-sizing: border-box;\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n width: var(--ds-unit);\n height: var(--ds-unit);\n margin: 0;\n padding: 0;\n border: 0;\n border-radius: var(--ds-radius);\n background: transparent;\n color: inherit;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n}\n\n/* The dot itself: 4px, scaled with the surface, a fifth of the square. */\n.ds-progress__dot::before {\n content: \"\";\n box-sizing: border-box;\n width: calc(4px * var(--ds-sf, 1));\n height: calc(4px * var(--ds-sf, 1));\n border-radius: 50%;\n background: transparent;\n box-shadow: inset 0 0 0 1px var(--ds-progress-dot-color);\n transition: transform 160ms ease-out;\n}\n\n.ds-progress__dot[data-state=\"past\"] { --ds-progress-dot-color: var(--ds-progress-dot-past, var(--ds-text-color-muted)); }\n.ds-progress__dot[data-state=\"next\"] { --ds-progress-dot-color: var(--ds-progress-dot-next, var(--ds-text-color)); }\n.ds-progress__dot[data-state=\"end\"] { --ds-progress-dot-color: var(--ds-progress-dot-end, var(--ds-text-color)); }\n.ds-progress__dot[aria-current=\"true\"] { --ds-progress-dot-color: var(--ds-progress-dot-current, var(--ds-text-color)); }\n\n.ds-progress__dot:is([data-state=\"past\"], [data-state=\"next\"], [aria-current=\"true\"])::before {\n background: var(--ds-progress-dot-color);\n}\n\n/* The current dot is half again as large. */\n.ds-progress__dot[aria-current=\"true\"]::before {\n transform: scale(1.5);\n}\n\n.ds-progress__dot:hover:not([aria-current=\"true\"])::before {\n transform: scale(1.25);\n}\n\n.ds-progress__dot:focus-visible {\n outline: 2px solid var(--ds-focus-ring);\n outline-offset: -2px;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .ds-progress__dot::before {\n transition: none;\n }\n}\n/*\n * Rows, as plain classes on native elements: the `ds-row` and `ds-sidebar`\n * elements' look without the elements.\n *\n * A row is one square high, and what it holds stands on the lower sixteen of\n * the square with the spare four above, as Ezo sets every row: a line of\n * words on a 16px line box, a 16px icon and a 16px control all end on the\n * row's bottom line, so the capitals (7px below the top line, baseline 3px\n * above the next) are centred on an icon beside them. On an `<a href>` or a\n * `<button>` the whole row is one press target, a row, a tile or a card: the pointer over it, the\n * press and the focus ring cover the row, and its content keeps the row's\n * layout. A row that also holds controls of its own (a quick action, a link\n * in another cell) is any element, a `<li>`, a `<div>` or a `<tr>`, with one\n * `ds-row__target` inside: that link or button takes the press anywhere on\n * the row, and every other control inside stays separately pressable.\n *\n * ds-row a row one square high; a press target on <a> or <button>\n * ds-row__target the row's press target, when the row holds other controls\n * ds-row--auto as high as its content, at least one square\n * ds-row--stacked the content stacked, each line one square (a tile, a card)\n * ds-row--nav a navigation row: the sidebar's two-pixel corners\n * aria-current=\"page\" the destination being shown, filled and in the medium weight\n * aria-disabled=\"true\" or `disabled` on a button: dimmed, not pressable\n *\n * A sidebar of nested things (the `ds-sidebar tree` element without Lit) is a\n * `role=\"tree\"` list of `role=\"treeitem\"` rows, flat, each with its\n * `aria-level`, one square further in per level. `0-face/sidebar.ts`\n * (`connectSidebar`) gives it the keyboard of the tree pattern.\n *\n * ds-sidebar the tree (`role=\"tree\"`)\n * ds-sidebar__item a row (`role=\"treeitem\"`, `aria-level`, `aria-expanded` on a parent, `aria-selected`)\n * ds-sidebar__disclosure the arrow on its own square, turned down when the row is open; empty on a leaf\n * ds-sidebar__label the words, cut short with an ellipsis\n * ds-sidebar__meta small muted words at the row's end\n *\n * An icon (`.ds-icon`) in a tree row takes a square of its own. Tokens:\n * `--ds-row-padding` (the words' distance from the row's edges, 4px),\n * `--ds-row-fill-hover`, `--ds-row-fill-press`, `--ds-row-fill-current` and\n * `--ds-row-current-weight` (component-tokens.css, shared with the\n * element), `--ds-sidebar-disclosure` (the arrow's drawing, the library's\n * `right` icon) and `--ds-sidebar-indent` (a row's depth in squares, read\n * from `aria-level` up to twelve levels).\n */\n\n:where(.ds-row):not(tr) {\n box-sizing: border-box;\n display: flex;\n flex-direction: row;\n align-items: flex-end;\n gap: calc(4px * var(--ds-sf));\n min-width: 0;\n height: var(--ds-row-height);\n margin: 0;\n line-height: var(--ds-type-lineheight-default);\n}\n\n:where(.ds-row--auto):not(tr) {\n height: auto;\n min-height: var(--ds-row-height);\n}\n\n:where(.ds-row--stacked):not(tr) {\n flex-direction: column;\n align-items: flex-start;\n justify-content: flex-start;\n gap: 0;\n height: auto;\n}\n\n/* The press target: the row itself, or the one target inside it. */\na.ds-row,\nbutton.ds-row,\n.ds-row__target {\n appearance: none;\n box-sizing: border-box;\n margin: 0;\n border: 0;\n background: none;\n color: inherit;\n font: inherit;\n letter-spacing: inherit;\n text-align: start;\n text-decoration: none;\n text-transform: none;\n cursor: pointer;\n outline: none;\n -webkit-tap-highlight-color: transparent;\n}\n\n/* The font inherited above would bring the page's line into the row: the words go back on the\n sixteen, as every row's (a stacked tile keeps its square lines below). */\na.ds-row,\nbutton.ds-row {\n position: relative;\n width: 100%;\n line-height: var(--ds-type-lineheight-default);\n padding: 0 var(--ds-row-padding, calc(4px * var(--ds-sf)));\n border-radius: var(--ds-row-radius, 0);\n}\n\n.ds-row__target {\n padding: 0;\n}\n\n/* Each line of a tile one square, over the inherited font of a native link or button:\n a tile's lines wrap, so they stay on whole squares rather than the sixteen. */\n.ds-row.ds-row--stacked {\n line-height: var(--ds-row-height);\n}\n\n/* A row with a target inside: the target's layers cover the row. */\n.ds-row:has(.ds-row__target) {\n position: relative;\n isolation: isolate;\n}\n:where(.ds-row:has(.ds-row__target)):not(tr) {\n padding-inline: var(--ds-row-padding, calc(4px * var(--ds-sf)));\n}\n.ds-row__target::before,\n.ds-row__target::after {\n content: \"\";\n position: absolute;\n inset: 0;\n border-radius: var(--ds-row-radius, 0);\n}\n.ds-row__target::before { z-index: 1; }\n.ds-row__target::after { z-index: -1; }\n.ds-row:has(.ds-row__target) :where(a[href], button, input, select, textarea, label, summary, [tabindex]):not(.ds-row__target) {\n position: relative;\n z-index: 2;\n}\n\n/*\n * The pointer, the press, the current destination and the focus ring are one layer under the\n * row's content (Joachim, 19 October 2026). On a one-square row it is a square high and sits two\n * pixels around what the row holds: from two above the lower sixteen to two below the row's foot,\n * and two past the content at each side, its corners the icon tiles' 4px and those two more, so a\n * 16px icon stands in it with two all round. It is drawn over the row, never moving its words or\n * icons off the grid. An open row (`data-open`) wears the same layer. A taller row (a stacked\n * tile, `auto`) is filled whole.\n * Tokens: `--ds-row-fill-radius` (6px), `--ds-row-fill-overhang` (2px).\n */\na.ds-row,\nbutton.ds-row {\n isolation: isolate;\n}\na.ds-row::after,\nbutton.ds-row::after {\n content: \"\";\n position: absolute;\n z-index: -1;\n pointer-events: none;\n}\na.ds-row::after,\nbutton.ds-row::after,\n.ds-row__target::after {\n --ds-row-overhang: var(--ds-row-fill-overhang, calc(2px * var(--ds-sf)));\n inset:\n calc(var(--ds-row-height) - var(--ds-control-height) - var(--ds-row-overhang))\n calc(var(--ds-row-padding, calc(4px * var(--ds-sf))) - var(--ds-row-overhang))\n calc(0px - var(--ds-row-overhang));\n border-radius: var(--ds-row-radius, var(--ds-row-fill-radius, calc(6px * var(--ds-sf))));\n}\n:is(a, button).ds-row:is(.ds-row--stacked, .ds-row--auto)::after,\n:is(.ds-row--stacked, .ds-row--auto) .ds-row__target::after {\n inset: 0;\n}\na.ds-row[href]:hover::after,\nbutton.ds-row:hover:not(:disabled)::after,\n.ds-row__target:is(a[href], button:not(:disabled)):hover::after {\n background: var(--ds-row-fill-hover, var(--ds-fill));\n}\na.ds-row[href]:active::after,\nbutton.ds-row:active:not(:disabled)::after,\n.ds-row__target:is(a[href], button:not(:disabled)):active::after {\n background: var(--ds-row-fill-press, var(--ds-fill-hover));\n}\n.ds-row:is(a, button)[aria-current]:not([aria-current=\"false\"])::after,\n.ds-row__target[aria-current]:not([aria-current=\"false\"])::after {\n background: var(--ds-row-fill-current, var(--ds-fill-hover));\n}\n.ds-row:is(a, button)[aria-current]:not([aria-current=\"false\"]),\n.ds-row:has(.ds-row__target[aria-current]:not([aria-current=\"false\"])) {\n --ds-type-weight-default: var(--ds-row-current-weight, 500);\n font-weight: var(--ds-row-current-weight, 500);\n}\na.ds-row:focus-visible::after,\nbutton.ds-row:focus-visible::after,\n.ds-row__target:focus-visible::after {\n outline: 2px solid var(--ds-focus-ring);\n outline-offset: -2px;\n}\na.ds-row:not([href]),\na.ds-row[aria-disabled=\"true\"],\nbutton.ds-row:disabled,\nbutton.ds-row[aria-disabled=\"true\"],\n.ds-row:has(.ds-row__target:is(:disabled, [aria-disabled=\"true\"], a:not([href]))) {\n cursor: default;\n opacity: 0.5;\n}\n.ds-row__target:is(:disabled, [aria-disabled=\"true\"], a:not([href])) {\n cursor: default;\n}\na.ds-row[aria-disabled=\"true\"]:hover::after,\nbutton.ds-row[aria-disabled=\"true\"]:hover::after {\n background: none;\n}\n\n.ds-row--nav {\n --ds-row-radius: calc(2px * var(--ds-sf));\n}\n\n/* A sidebar of nested things: the tree. */\n\n.ds-sidebar {\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n min-width: 0;\n margin: 0;\n padding: 0;\n list-style: none;\n color: var(--ds-text-color);\n font-family: var(--ds-typeface-regular);\n font-feature-settings: var(--ds-font-features);\n font-size: var(--ds-type-size-default);\n font-weight: var(--ds-type-weight-default);\n line-height: var(--ds-type-lineheight-default);\n letter-spacing: var(--ds-type-letterspacing-default);\n}\n\n.ds-sidebar__item {\n box-sizing: border-box;\n display: flex;\n align-items: flex-end;\n height: var(--ds-row-height);\n min-width: 0;\n margin: 0;\n padding-block: 0;\n padding-inline: calc(var(--ds-sidebar-indent, 0) * var(--ds-unit)) var(--ds-row-padding, calc(4px * var(--ds-sf)));\n white-space: nowrap;\n cursor: pointer;\n user-select: none;\n outline: none;\n -webkit-tap-highlight-color: transparent;\n}\n.ds-sidebar__item[hidden] { display: none; }\n.ds-sidebar__item[aria-level=\"2\"] { --ds-sidebar-indent: 1; }\n.ds-sidebar__item[aria-level=\"3\"] { --ds-sidebar-indent: 2; }\n.ds-sidebar__item[aria-level=\"4\"] { --ds-sidebar-indent: 3; }\n.ds-sidebar__item[aria-level=\"5\"] { --ds-sidebar-indent: 4; }\n.ds-sidebar__item[aria-level=\"6\"] { --ds-sidebar-indent: 5; }\n.ds-sidebar__item[aria-level=\"7\"] { --ds-sidebar-indent: 6; }\n.ds-sidebar__item[aria-level=\"8\"] { --ds-sidebar-indent: 7; }\n.ds-sidebar__item[aria-level=\"9\"] { --ds-sidebar-indent: 8; }\n.ds-sidebar__item[aria-level=\"10\"] { --ds-sidebar-indent: 9; }\n.ds-sidebar__item[aria-level=\"11\"] { --ds-sidebar-indent: 10; }\n.ds-sidebar__item[aria-level=\"12\"] { --ds-sidebar-indent: 11; }\n.ds-sidebar__item:hover:not([aria-disabled=\"true\"]) { background: var(--ds-row-fill-hover, var(--ds-fill)); }\n.ds-sidebar__item:active:not([aria-disabled=\"true\"]) { background: var(--ds-row-fill-press, var(--ds-fill-hover)); }\n.ds-sidebar__item[aria-selected=\"true\"] {\n --ds-type-weight-default: var(--ds-row-current-weight, 500);\n background: var(--ds-row-fill-current, var(--ds-fill-hover));\n font-weight: var(--ds-row-current-weight, 500);\n}\n.ds-sidebar__item:focus-visible {\n outline: 2px solid var(--ds-focus-ring);\n outline-offset: -2px;\n}\n.ds-sidebar__item[aria-disabled=\"true\"] {\n cursor: default;\n opacity: 0.5;\n}\n\n/* The arrow and an icon each stand on a whole square, drawn on the row's sixteen (two\n pixels below the square's middle); a leaf keeps the arrow's square empty. */\n.ds-sidebar__disclosure,\n.ds-sidebar__item > .ds-icon {\n flex: 0 0 var(--ds-unit);\n width: var(--ds-unit);\n height: var(--ds-unit);\n -webkit-mask-size: var(--ds-icon-box, calc(14px * var(--ds-sf)));\n mask-size: var(--ds-icon-box, calc(14px * var(--ds-sf)));\n -webkit-mask-position: center calc(50% + 2px * var(--ds-sf));\n mask-position: center calc(50% + 2px * var(--ds-sf));\n}\n.ds-sidebar__disclosure {\n --ds-icon-box: calc(14px * var(--ds-sf));\n display: inline-block;\n color: var(--ds-text-color-muted);\n background-color: currentColor;\n -webkit-mask: var(--ds-sidebar-disclosure, url(\"data:image/svg+xml,%3Csvg xmlns='http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg' viewBox='0 0 16 16'%3E%3Cpath d='M13.1566 8.60594H0V9.61651L13.1562 9.61791L10.249 12.5251L10.9558 13.219L15.0636 9.11123L10.9524 5L10.245 5.70734L13.1566 8.60594Z'/%3E%3C/svg%3E\")) center calc(50% + 2px * var(--ds-sf)) / var(--ds-icon-box) no-repeat;\n mask: var(--ds-sidebar-disclosure, url(\"data:image/svg+xml,%3Csvg xmlns='http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg' viewBox='0 0 16 16'%3E%3Cpath d='M13.1566 8.60594H0V9.61651L13.1562 9.61791L10.249 12.5251L10.9558 13.219L15.0636 9.11123L10.9524 5L10.245 5.70734L13.1566 8.60594Z'/%3E%3C/svg%3E\")) center calc(50% + 2px * var(--ds-sf)) / var(--ds-icon-box) no-repeat;\n transform: rotate(0deg);\n transition: transform 120ms ease-out;\n}\n.ds-sidebar__item:not([aria-expanded]) > .ds-sidebar__disclosure {\n visibility: hidden;\n}\n.ds-sidebar__item[aria-expanded=\"true\"] > .ds-sidebar__disclosure {\n transform: rotate(90deg);\n}\n.ds-sidebar__label {\n flex: 1 1 auto;\n min-width: 0;\n padding-inline-start: calc(4px * var(--ds-sf));\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ds-sidebar__meta {\n flex: none;\n padding-inline-start: calc(4px * var(--ds-sf));\n color: var(--ds-text-color-muted);\n font-size: var(--ds-type-size-small);\n font-variant-numeric: tabular-nums;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .ds-sidebar__disclosure { transition: none; }\n}\n\n@media (forced-colors: active) {\n .ds-row:is(a, button)[aria-current]:not([aria-current=\"false\"]),\n .ds-row__target[aria-current]:not([aria-current=\"false\"])::after,\n .ds-sidebar__item[aria-selected=\"true\"] {\n outline: 1px solid Highlight;\n outline-offset: -1px;\n }\n .ds-sidebar__disclosure {\n background-color: CanvasText;\n }\n}\n\n/* A controlled detail view: the caller marks its open row, and clears it on close.\n Navigation's current state and the text weight remain independent. */\n.ds-row[data-open]:not([data-open=\"false\"]) {\n position: relative;\n isolation: isolate;\n}\n.ds-row[data-open]:not([data-open=\"false\"])::after {\n content: \"\";\n position: absolute;\n z-index: -1;\n pointer-events: none;\n --ds-row-overhang: var(--ds-row-fill-overhang, calc(2px * var(--ds-sf)));\n inset: calc(var(--ds-row-height) - var(--ds-control-height) - var(--ds-row-overhang))\n calc(var(--ds-row-padding, calc(4px * var(--ds-sf))) - var(--ds-row-overhang))\n calc(0px - var(--ds-row-overhang));\n border-radius: var(--ds-row-radius, var(--ds-row-fill-radius, calc(6px * var(--ds-sf))));\n background: var(--ds-row-fill-current, var(--ds-fill-hover));\n}\n/* A plain row (no press target inside) has no padding of its own: its words and icons stand on\n the row's edges, so the layer reaches two past them, as a flush row's does. */\n.ds-row[data-open]:not([data-open=\"false\"]):not(a, button, .ds-row--stacked, .ds-row--auto, :has(.ds-row__target))::after {\n inset: calc(var(--ds-row-height) - var(--ds-control-height) - var(--ds-row-overhang))\n calc(0px - var(--ds-row-overhang))\n calc(0px - var(--ds-row-overhang));\n}\n.ds-row[data-open]:is(.ds-row--stacked, .ds-row--auto)::after { inset: 0; }\n@media (forced-colors: active) {\n .ds-row[data-open]:not([data-open=\"false\"])::after { outline: 1px solid Highlight; outline-offset: -1px; }\n}\n/*\n * `.ds-context` is the `ds-context` element's look on a native menu, for a page\n * without Lit or a menu a library draws: Ezo's gap face. The rows carry the\n * colour and the menu only cuts the outer silhouette, so the page shows\n * through a pixel between rows and two between groups. The rows are black\n * with white words in both themes, as Ezo's menus always were (the slate in\n * dark, darker than the page). Every row is 19px with the pixel under it, a square's pitch, its words\n * on the sixteen and an icon at its end. A Select's menu is the light face,\n * `.ds-menu`; a Context's is this dark one.\n *\n * ds-context the menu (`role=\"menu\"`); its rows are its buttons,\n * links and menu items, or `ds-context__item`\n * ds-context__item a row; `--danger` (or `data-danger`) in red\n * ds-context__label a heading row, muted\n * ds-context__shortcut a shortcut at the row's end, muted\n * ds-context__separator a group gap (or an `hr`, or `role=\"separator\"`):\n * never a line; first, last or doubled it is quiet\n * [data-ds-refused] a refused change: the row turns red\n *\n * The page places and opens it (a popover at the pointer, say) and owns its\n * keys; the faces of hover, focus, disabled and the wait of a pending menu\n * (`aria-busy=\"true\"`) are here. Tokens as the element's: --ds-context-fill,\n * --ds-context-ink, --ds-context-hover, --ds-context-danger,\n * --ds-context-refused, --ds-context-radius.\n */\n\n.ds-context {\n --ds-context-row: calc(var(--ds-row-height) - 1px);\n /* Black in both themes, as Ezo's menus always were: in dark the slate, darker than the page. */\n --ds-context-fill: light-dark(var(--ds-black, #2a2a2a), var(--ds-slate, #1e1e1e));\n --ds-context-ink: var(--ds-white, #ffffff);\n --ds-context-hover: color-mix(in srgb, var(--ds-context-fill) 78%, var(--ds-context-ink));\n --ds-context-danger: var(--ds-tuned-red);\n --ds-context-refused: var(--ds-tuned-red);\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n gap: 1px;\n width: var(--ds-menu-width, max-content);\n min-width: calc(4 * var(--ds-unit));\n max-height: var(--ds-menu-max-height, calc(16 * var(--ds-unit)));\n overflow-y: auto;\n margin: 0;\n padding: 0;\n border: 0;\n background: transparent;\n box-shadow: none;\n clip-path: inset(0 round var(--ds-context-radius, var(--ds-radius, 4px)));\n color: var(--ds-context-ink);\n font-family: var(--ds-typeface-regular);\n font-feature-settings: var(--ds-font-features);\n font-size: var(--ds-type-size-default);\n font-weight: var(--ds-type-weight-default);\n line-height: var(--ds-type-lineheight-default);\n letter-spacing: var(--ds-type-letterspacing-default);\n list-style: none;\n}\n\n.ds-context > [role=\"group\"] {\n display: contents;\n}\n\n/* A row: whatever the page or library styles its buttons with, one row on the sixteen. */\n.ds-context :is(.ds-context__item, :is(.ds-context, .ds-context > [role=\"group\"]) > :is(button, a, li, [role^=\"menuitem\"]):not(.ds-context__label, .ds-context__separator, [role=\"separator\"])) {\n box-sizing: border-box !important;\n display: flex !important;\n flex: 0 0 auto !important;\n align-items: flex-end !important;\n justify-content: space-between !important;\n gap: calc(4px * var(--ds-sf));\n width: 100%;\n height: var(--ds-context-row) !important;\n min-height: var(--ds-context-row) !important;\n max-height: var(--ds-context-row) !important;\n margin: 0 !important;\n padding: 0 calc(4px * var(--ds-sf)) !important;\n border: 0 !important;\n border-radius: 0 !important;\n background: var(--ds-context-fill) !important;\n color: var(--ds-context-ink) !important;\n font: inherit;\n line-height: var(--ds-type-lineheight-default) !important;\n letter-spacing: inherit;\n text-align: start;\n text-decoration: none;\n white-space: nowrap;\n cursor: pointer;\n outline: none;\n appearance: none;\n -webkit-tap-highlight-color: transparent;\n}\n\n.ds-context :is(.ds-context__item, :is(.ds-context, .ds-context > [role=\"group\"]) > :is(button, a, li, [role^=\"menuitem\"])):is(:hover, :focus-visible, [data-highlighted]):not(:disabled, [aria-disabled=\"true\"], [data-disabled]) {\n background: var(--ds-context-hover) !important;\n}\n\n.ds-context :is(.ds-context__item, :is(.ds-context, .ds-context > [role=\"group\"]) > :is(button, a, li, [role^=\"menuitem\"])):focus-visible {\n box-shadow: inset 0 0 0 2px var(--ds-focus-ring);\n}\n\n.ds-context :is(.ds-context__item, :is(.ds-context, .ds-context > [role=\"group\"]) > :is(button, a, li, [role^=\"menuitem\"])):is(:disabled, [aria-disabled=\"true\"], [data-disabled]) {\n cursor: default;\n opacity: 0.5;\n}\n\n.ds-context[aria-busy=\"true\"] > * {\n cursor: wait !important;\n}\n\n/* Danger is red, on either fill. */\n.ds-context :is(.ds-context__item, :is(.ds-context, .ds-context > [role=\"group\"]) > :is(button, a, li, [role^=\"menuitem\"])):is(.ds-context__item--danger, [data-danger], [data-variant=\"destructive\"]) {\n color: var(--ds-context-danger) !important;\n}\n\n/* A refused change: its row turns red a moment. */\n.ds-context :is(.ds-context__item, :is(.ds-context, .ds-context > [role=\"group\"]) > :is(button, a, li, [role^=\"menuitem\"]))[data-ds-refused] {\n background: var(--ds-context-refused) !important;\n}\n\n/* Groups are a gap, never a line: the separator takes nothing but the gap after it. */\n.ds-context > :is(hr, .ds-context__separator, [role=\"separator\"]) {\n flex: 0 0 0 !important;\n height: 0 !important;\n margin: 0 !important;\n padding: 0 !important;\n border: 0 !important;\n background: none !important;\n}\n\n/* A group left empty leaves no gap. */\n.ds-context > :is(hr, .ds-context__separator, [role=\"separator\"]):is(:first-child, :last-child),\n.ds-context > :is(hr, .ds-context__separator, [role=\"separator\"]) + :is(hr, .ds-context__separator, [role=\"separator\"]) {\n display: none !important;\n}\n\n.ds-context :is(.ds-context__label, [data-ds-menu-label]) {\n box-sizing: border-box;\n height: var(--ds-context-row);\n /* The spare three above the sixteen. */\n padding: calc(var(--ds-context-row) - var(--ds-type-lineheight-default)) calc(4px * var(--ds-sf)) 0;\n background: var(--ds-context-fill);\n color: color-mix(in srgb, var(--ds-context-ink) 60%, var(--ds-context-fill));\n line-height: var(--ds-type-lineheight-default);\n white-space: nowrap;\n}\n\n.ds-context__shortcut {\n margin-left: auto;\n color: color-mix(in srgb, var(--ds-context-ink) 60%, var(--ds-context-fill));\n}\n\n/*\n * Native controls in shared dress.\n *\n * DS one keeps a few components as plain classes on native elements. shared does\n * the same for everything a person types into or picks from: text fields,\n * selects and checkboxes stay `<input>` and `<select>`, so labels, form\n * semantics and React's controlled inputs work as they always have.\n *\n * `.ds-button` is the `ds-button` element's look on a native `<button>`,\n * for a button inside a drag handle. The drag library recognises only native\n * controls: inside a handle it would lift the row on Space where a\n * `ds-button` was meant to be pressed. Both read the same custom properties.\n * On an `<a href>` it is a link drawn as a button (see `a.ds-button` below).\n */\n\n.ds-button {\n appearance: none;\n box-sizing: border-box;\n display: inline-flex;\n /* At the foot of a line, so the 16px face takes the lower sixteen of the square. */\n vertical-align: bottom;\n flex: 0 0 auto;\n width: var(--ds-button-width, max-content);\n align-items: center;\n justify-content: var(--ds-button-align, flex-start);\n gap: var(--ds-button-gap, calc(4px * var(--ds-sf)));\n height: var(--ds-button-height, var(--ds-control-height));\n margin: 0;\n padding: var(--ds-button-padding, 0);\n border: 0;\n border-radius: var(--ds-radius);\n background: var(--ds-button-background, transparent);\n color: var(--ds-button-color, var(--ds-button-text-color));\n font-family: var(--ds-typeface-regular);\n font-feature-settings: var(--ds-font-features);\n font-size: var(--ds-type-size-default);\n line-height: var(--ds-type-lineheight-default);\n letter-spacing: var(--ds-type-letterspacing-default);\n white-space: nowrap;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n}\n\n.ds-button--icon {\n justify-content: center;\n width: var(--ds-button-height, var(--ds-control-height));\n padding: 0;\n color: var(--ds-button-color, var(--ds-text-color-muted));\n}\n\n.ds-button--icon:hover:not(:disabled, [aria-disabled=\"true\"]) {\n --ds-button-color: var(--ds-text-color);\n}\n\n.ds-button:focus-visible {\n outline: 2px solid var(--ds-focus-ring);\n outline-offset: 1px;\n}\n\n.ds-button:disabled,\n.ds-button[aria-disabled=\"true\"] {\n cursor: default;\n opacity: 0.5;\n}\n\n/*\n * one.css carries older button rules that dim a button on hover and nudge it\n * on press. The shared button does neither: these match their specificity, so\n * controls.css, loaded after one.css, wins.\n */\n.ds-button:hover:not(:disabled, [aria-disabled=\"true\"]) {\n filter: none;\n}\n\n.ds-button:active:not(:disabled, [aria-disabled=\"true\"]) {\n transform: none;\n}\n\n/*\n * `a.ds-button` is a link drawn as a button. The browser keeps the link:\n * middle-click, a new tab, Copy link and Enter. It never takes the link's\n * underline or colours, in any state, so every face (primary, secondary,\n * icon, tabs) looks as it does on a `<button>`. A disabled link has no `href`\n * and carries `aria-disabled=\"true\"` and `role=\"link\"`.\n */\na.ds-button,\na.ds-button:is(:hover, :focus, :active) {\n text-decoration: none;\n}\n\na.ds-button:is(:link, :visited) {\n color: var(--ds-button-color, var(--ds-button-text-color));\n}\n\na.ds-button.ds-button--icon:is(:link, :visited) {\n color: var(--ds-button-color, var(--ds-text-color-muted));\n}\n\na.ds-button[aria-disabled=\"true\"] {\n pointer-events: none;\n}\n\n/*\n * `.ds-link` is a link in running text: the text's ink, underlined by a\n * hairline 2px below the letters (as a toast's actions are), which turns to the full ink under\n * the pointer. A visited link is drawn in the muted ink. Words keep their\n * sentence case. Tokens: `--ds-link-color`, `--ds-link-visited-color`,\n * `--ds-link-line-color`, `--ds-link-hover-line-color`.\n */\n.ds-link {\n color: var(--ds-link-color, currentColor);\n text-decoration-line: underline;\n text-decoration-thickness: 1px;\n text-decoration-color: var(--ds-link-line-color, color-mix(in srgb, currentColor 40%, transparent));\n text-underline-offset: calc(2px * var(--ds-sf));\n cursor: pointer;\n}\n\n.ds-link:visited {\n color: var(--ds-link-visited-color, var(--ds-text-color-muted));\n}\n\n.ds-link:hover {\n text-decoration-color: var(--ds-link-hover-line-color, currentColor);\n}\n\n/* Quiet navigation and related-component lists: no underline in any state. */\n.ds-link.ds-link--plain {\n text-decoration: none;\n}\n\n.ds-link:focus-visible {\n outline: 2px solid var(--ds-focus-ring);\n outline-offset: 1px;\n}\n\n.ds-link[aria-disabled=\"true\"] {\n cursor: default;\n opacity: 0.5;\n pointer-events: none;\n}\n\n.ds-input,\n.ds-select {\n box-sizing: border-box;\n min-width: 0;\n height: var(--ds-input-height, var(--ds-control-height));\n margin: 0;\n padding: 0 calc(4px * var(--ds-sf));\n border: 0;\n border-radius: var(--ds-radius);\n background: var(--ds-fill);\n color: var(--ds-text-color);\n font-family: var(--ds-typeface-regular);\n font-feature-settings: var(--ds-font-features);\n font-size: var(--ds-type-size-default);\n line-height: var(--ds-type-lineheight-default);\n letter-spacing: var(--ds-type-letterspacing-default);\n outline: none;\n}\n\n.ds-input::placeholder {\n color: var(--ds-title-color);\n}\n\n.ds-input:focus-visible,\n.ds-select:focus-visible,\n.ds-checkbox:focus-visible,\n.ds-radio:focus-visible,\n.ds-switch:focus-visible {\n outline: 2px solid var(--ds-focus-ring);\n outline-offset: 1px;\n}\n\n.ds-input:disabled,\n.ds-select:disabled,\n.ds-checkbox:disabled,\n.ds-radio:disabled,\n.ds-switch:disabled {\n cursor: default;\n opacity: 0.5;\n}\n\n.ds-select {\n appearance: none;\n padding-right: calc(16px * var(--ds-sf));\n background-image:\n linear-gradient(45deg, transparent 50%, currentColor 50%),\n linear-gradient(135deg, currentColor 50%, transparent 50%);\n background-position:\n calc(100% - 9px) 50%,\n calc(100% - 6px) 50%;\n background-size:\n 3px 3px,\n 3px 3px;\n background-repeat: no-repeat;\n cursor: pointer;\n}\n\n/* A multiline field, `ds-input` on a <textarea>, spans whole rows: --ds-textarea-rows sets how many, three by default. */\ntextarea.ds-input {\n display: block;\n height: var(--ds-textarea-height, calc(var(--ds-textarea-rows, 3) * var(--ds-row-height)));\n min-height: var(--ds-row-height);\n padding: var(--ds-textarea-padding, calc(4px * var(--ds-sf)));\n border-radius: var(--ds-textarea-radius, calc(4px * var(--ds-sf)));\n resize: vertical;\n}\n\n/*\n * `.ds-checkbox` and `.ds-radio` are the `ds-checkbox` and `ds-radio` elements'\n * faces on a native `<input>`: one square on the field's ground with a 1px\n * line. The checkbox has the corners of a control and fills with the accent,\n * the check (`x-icon/check.svg`) in its ink; the radio is a circle with a dot\n * half a square across in the text colour.\n */\n.ds-checkbox,\n.ds-radio {\n appearance: none;\n box-sizing: border-box;\n display: inline-grid;\n flex: 0 0 auto;\n place-items: center;\n vertical-align: middle;\n width: var(--ds-row-height);\n height: var(--ds-row-height);\n margin: 0;\n padding: 0;\n border: 1px solid var(--ds-border-color);\n background: var(--ds-input-background);\n cursor: pointer;\n}\n\n.ds-checkbox {\n --ds-checkbox-mark: url(\"data:image/svg+xml,%3Csvg%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%3Cpath%20d%3D%22M6.68649%2011.884L12.443%206.13601L11.736%205.42715L6.68835%2010.4827L4.26399%208.0501L3.55696%208.74525L6.68649%2011.884Z%22%20fill%3D%22currentColor%22%2F%3E%0A%3C%2Fsvg%3E\");\n border-radius: var(--ds-radius);\n corner-shape: var(--ds-corner-shape, round);\n color: var(--ds-button-text-color-on-accent);\n}\n\n.ds-checkbox:is(:checked, :indeterminate) {\n background: var(--ds-accent-color);\n}\n\n.ds-checkbox::before,\n.ds-radio::before {\n content: \"\";\n opacity: 0;\n background: currentColor;\n}\n\n.ds-checkbox::before {\n width: calc(0.8 * var(--ds-row-height));\n height: calc(0.8 * var(--ds-row-height));\n mask: var(--ds-checkbox-mark) center / contain no-repeat;\n}\n\n.ds-checkbox:indeterminate::before {\n width: calc(var(--ds-row-height) / 2);\n height: calc(2px * var(--ds-sf));\n mask: none;\n}\n\n.ds-radio {\n border-radius: 50%;\n color: var(--ds-text-color);\n}\n\n.ds-radio::before {\n width: calc(var(--ds-row-height) / 2);\n height: calc(var(--ds-row-height) / 2);\n border-radius: 50%;\n}\n\n.ds-checkbox:is(:checked, :indeterminate)::before,\n.ds-radio:checked::before {\n opacity: 1;\n}\n\n/*\n * `.ds-switch` is the `ds-switch` element's look on a native\n * `<button role=\"switch\" aria-checked>`: two control heights wide, its thumb\n * one control high less a 2px inset, travelling one control height.\n * `.ds-switch--rounded` is the round variant with a white thumb.\n */\n\n.ds-switch {\n --ds-choice-unit: var(--ds-control-height);\n appearance: none;\n box-sizing: border-box;\n position: relative;\n display: inline-block;\n flex: 0 0 auto;\n vertical-align: middle;\n width: calc(2 * var(--ds-choice-unit));\n height: var(--ds-choice-unit);\n margin: 0;\n padding: calc(2px * var(--ds-sf));\n border: 0;\n border-radius: var(--ds-radius);\n background: var(--ds-fill);\n color: var(--ds-text-color-muted);\n cursor: pointer;\n touch-action: manipulation;\n}\n\n.ds-switch::after {\n content: \"\";\n display: block;\n width: calc(var(--ds-choice-unit) - 4px * var(--ds-sf));\n height: 100%;\n border-radius: var(--ds-radius);\n background: currentColor;\n transform: translateX(0);\n transition: transform 140ms ease;\n}\n\n.ds-switch[aria-checked=\"true\"] {\n background: var(--ds-accent-color);\n color: var(--ds-button-text-color-on-accent);\n}\n\n.ds-switch[aria-checked=\"true\"]::after {\n transform: translateX(var(--ds-choice-unit));\n}\n\n.ds-switch--rounded {\n border-radius: var(--ds-choice-unit);\n color: var(--ds-white);\n}\n\n.ds-switch--rounded::after {\n border-radius: 50%;\n background: var(--ds-white);\n}\n\n.ds-switch--rounded[aria-checked=\"true\"] {\n background: var(--ds-switch-track-on);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .ds-switch::after {\n transition: none;\n }\n}\n\n/*\n * `.ds-range` draws a native `<input type=\"range\">`: one control high,\n * standing on the lower sixteen of its square (the spare four above it as a\n * margin, so it takes one square alone and in a row), eight squares wide.\n * The track is a 1px line across the middle of the control, the thumb one\n * control high and half a control wide with the shared corners, in the ink.\n * The browser keeps the value, the keys, the form data and the events.\n *\n * Tokens: `--ds-range-width` (eight squares), `--ds-range-track` (the line,\n * the title grey), `--ds-range-thumb` (the ink) and `--ds-range-thumb-width`.\n * WebKit and Blink draw the parts as `::-webkit-slider-*`, Firefox as\n * `::-moz-range-*`; both get the same track and thumb.\n */\n\n.ds-range {\n -webkit-appearance: none;\n appearance: none;\n box-sizing: border-box;\n display: inline-block;\n vertical-align: bottom;\n width: var(--ds-range-width, calc(8 * var(--ds-unit)));\n min-width: 0;\n height: var(--ds-control-height);\n margin: calc(var(--ds-row-height) - var(--ds-control-height)) 0 0;\n padding: 0;\n border: 0;\n border-radius: var(--ds-radius);\n background: transparent;\n color: var(--ds-range-thumb, var(--ds-text-color));\n font: inherit;\n cursor: pointer;\n touch-action: pan-y;\n -webkit-tap-highlight-color: transparent;\n}\n\n.ds-range::-webkit-slider-runnable-track {\n box-sizing: border-box;\n height: var(--ds-control-height);\n border: 0;\n background: linear-gradient(var(--ds-range-track, var(--ds-title-color)) 0 0) 0 calc(var(--ds-control-height) / 2) / 100% 1px no-repeat;\n}\n\n.ds-range::-moz-range-track {\n box-sizing: border-box;\n height: var(--ds-control-height);\n border: 0;\n background: linear-gradient(var(--ds-range-track, var(--ds-title-color)) 0 0) 0 calc(var(--ds-control-height) / 2) / 100% 1px no-repeat;\n}\n\n.ds-range::-moz-range-progress {\n background: transparent;\n}\n\n.ds-range::-webkit-slider-thumb {\n -webkit-appearance: none;\n appearance: none;\n box-sizing: border-box;\n width: var(--ds-range-thumb-width, calc(var(--ds-control-height) / 2));\n height: var(--ds-control-height);\n border: 0;\n border-radius: var(--ds-radius);\n background: currentColor;\n}\n\n.ds-range::-moz-range-thumb {\n box-sizing: border-box;\n width: var(--ds-range-thumb-width, calc(var(--ds-control-height) / 2));\n height: var(--ds-control-height);\n border: 0;\n border-radius: var(--ds-radius);\n background: currentColor;\n}\n\n.ds-range:focus-visible {\n outline: 2px solid var(--ds-focus-ring);\n outline-offset: 1px;\n}\n\n.ds-range:disabled {\n cursor: default;\n opacity: 0.5;\n}\n\n/*\n * `.ds-tabs` is the `ds-tabs` element's row on native buttons. As an option\n * group (role=\"group\", a segmented control) its `.ds-button` choices are\n * grey and the chosen one, aria-pressed, aria-selected or aria-current, is in\n * the accent, as the primary and secondary buttons are.\n */\n\n.ds-tabs {\n box-sizing: border-box;\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: calc(4px * var(--ds-sf));\n min-width: 0;\n height: var(--ds-row-height);\n}\n\n.ds-tabs--fill > * {\n flex: 1 1 0;\n justify-content: center;\n}\n\n.ds-tabs[role=\"group\"] > .ds-button {\n --ds-button-height: var(--ds-row-height);\n --ds-button-padding: 0 calc(4px * var(--ds-sf));\n --ds-button-background: var(--ds-button-background-color-secondary);\n min-width: 0;\n}\n\n.ds-tabs[role=\"group\"] > .ds-button:hover:not(:disabled, [aria-disabled=\"true\"]) {\n --ds-button-background: var(--ds-fill-hover);\n}\n\n.ds-tabs[role=\"group\"] > .ds-button:is([aria-pressed=\"true\"], [aria-selected=\"true\"], [aria-current]:not([aria-current=\"false\"])) {\n --ds-button-background: var(--ds-button-background-color-primary);\n --ds-button-color: var(--ds-button-text-color-on-accent);\n}\n\n/*\n * `.ds-overlay` is the `ds-overlay` element's frame on a native `<dialog>`, a\n * modal by default: twenty squares wide, a square of padding, square corners,\n * a hairline and the paper. `ds-overlay--sheet`, `ds-overlay--drawer` (with\n * `ds-overlay--start` for the start side) and `ds-overlay--screen` are its\n * other types (overlay.css). Its header, title, description, footer and close button are marked\n * with data-ds-overlay, as inside the element.\n */\n\ndialog.ds-overlay {\n box-sizing: border-box;\n position: fixed;\n inset: 0;\n width: var(--ds-overlay-width, calc(20 * var(--ds-unit)));\n max-width: var(--ds-overlay-max-width, calc(100vw - 2 * var(--ds-unit)));\n max-height: var(--ds-overlay-max-height, calc(100dvh - 2 * var(--ds-unit)));\n height: var(--ds-overlay-height, fit-content);\n margin: auto;\n padding: var(--ds-overlay-padding, var(--ds-unit));\n border: var(--ds-overlay-border, 1px solid var(--ds-border-color));\n border-radius: 0;\n background: var(--ds-overlay-paper, var(--ds-paper));\n color: var(--ds-text-color);\n box-shadow: none;\n overflow: auto;\n overscroll-behavior: contain;\n font-family: var(--ds-typeface-regular);\n font-feature-settings: var(--ds-font-features);\n font-size: var(--ds-type-size-default);\n font-weight: var(--ds-type-weight-default);\n line-height: var(--ds-type-lineheight-default);\n letter-spacing: var(--ds-type-letterspacing-default);\n}\n\ndialog.ds-overlay[open] {\n display: flex;\n flex-direction: column;\n gap: var(--ds-unit);\n}\n\ndialog.ds-overlay::backdrop {\n background: color-mix(in srgb, var(--ds-black) 40%, transparent);\n}\n\n.ds-overlay [data-ds-overlay=\"header\"] {\n display: flex;\n flex-direction: column;\n}\n\n/* The header keeps the last square of its row for the close button, only when it has one. */\ndialog.ds-overlay:has(> [data-ds-overlay=\"close\"]) [data-ds-overlay=\"header\"] {\n padding-inline-end: var(--ds-unit);\n}\n\n/* The title on its square's lower sixteen, as every row's words. */\n.ds-overlay [data-ds-overlay=\"title\"] {\n display: flex;\n align-items: flex-end;\n min-height: var(--ds-row-height);\n margin: 0;\n color: var(--ds-text-color);\n font: inherit;\n font-weight: var(--ds-type-weight-bold);\n line-height: var(--ds-type-lineheight-default);\n}\n\n.ds-overlay [data-ds-overlay=\"description\"] {\n margin: 0;\n color: var(--ds-text-color-muted);\n font: inherit;\n}\n\n.ds-overlay [data-ds-overlay=\"footer\"] {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: flex-end;\n gap: calc(var(--ds-unit) / 2);\n min-height: var(--ds-row-height);\n}\n\n.ds-overlay [data-ds-overlay=\"close\"] {\n position: absolute;\n top: var(--ds-unit);\n right: var(--ds-unit);\n}\n\n/* A field in the frame, as a prompt has: its label one row high, the field\n * one row high beneath it and as wide as the frame, two squares in all. */\n.ds-overlay [data-ds-overlay=\"field\"] {\n display: flex;\n flex-direction: column;\n}\n\n.ds-overlay [data-ds-overlay=\"label\"] {\n min-height: var(--ds-row-height);\n line-height: var(--ds-row-height);\n}\n\n.ds-overlay [data-ds-overlay=\"field\"] > .ds-input {\n --ds-input-height: var(--ds-row-height);\n width: 100%;\n}\n\n/*\n * `.ds-button--primary` and `--secondary` are the element's variants on a\n * native button: a form's submit button stays native so Enter in a field\n * submits the form. `--danger` is its `danger` attribute.\n */\n\n.ds-button--primary,\n.ds-button--secondary {\n --ds-button-padding: 0 calc(4px * var(--ds-sf));\n}\n\n.ds-button--primary {\n --ds-button-background: var(--ds-button-background-color-primary);\n --ds-button-color: var(--ds-button-text-color-on-accent);\n}\n\n.ds-button--secondary {\n --ds-button-background: var(--ds-button-background-color-secondary);\n}\n\n.ds-button--secondary:hover:not(:disabled, [aria-disabled=\"true\"]) {\n --ds-button-background: var(--ds-fill-hover);\n}\n\n/* `.ds-button--danger` is the element's `danger`: a destructive action's words\n * in the danger colour, on the secondary face or none. */\n.ds-button--danger {\n --ds-button-color: var(--ds-danger-color, var(--ds-tuned-red));\n}\n\n.ds-button--fixed {\n width: var(--ds-button-width, calc(4 * var(--ds-unit)));\n}\n\n.ds-button--wide {\n justify-content: center;\n width: 100%;\n}\n\n/*\n * `.ds-menu` is the `ds-menu` element's look on a menu a library draws:\n * the context menu's items come from Radix, which owns their roles, focus and\n * keys, so they stay Radix's and take shared's paper, rows and type.\n * On whole squares, as the element: the line and the padding inside it make\n * half a square on each side, entries are one square each with nothing\n * between them, a separator is one square with its line, and there is no\n * shadow. n entries make a menu n + 1 squares high.\n */\n\n.ds-menu {\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n gap: 0;\n min-width: calc(8 * var(--ds-unit));\n max-width: calc(12 * var(--ds-unit));\n max-height: var(--ds-menu-max-height, calc(16 * var(--ds-unit)));\n overflow-y: auto;\n padding: calc(var(--ds-unit) / 2 - 1px);\n border: 1px solid var(--ds-border-color);\n border-radius: var(--ds-radius-card);\n background: var(--ds-paper);\n color: var(--ds-text-color);\n box-shadow: none;\n font-family: var(--ds-typeface-regular);\n font-feature-settings: var(--ds-font-features);\n font-size: var(--ds-type-size-default);\n font-weight: var(--ds-type-weight-default);\n line-height: var(--ds-type-lineheight-default);\n letter-spacing: var(--ds-type-letterspacing-default);\n}\n\n.ds-menu__item {\n position: relative;\n box-sizing: border-box;\n display: flex;\n align-items: center;\n gap: calc(4px * var(--ds-sf));\n width: 100%;\n min-height: var(--ds-row-height);\n padding: calc(2px * var(--ds-sf)) calc(6px * var(--ds-sf));\n border-radius: calc(4px * var(--ds-sf));\n color: var(--ds-text-color);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n cursor: default;\n user-select: none;\n outline: none;\n}\n\n/* An entry a library draws as its own button or link is one square high too,\n * whatever that library's button styles say. */\n.ds-menu > :is(button, a, [role=\"option\"], [role^=\"menuitem\"]),\n.ds-menu > [role=\"group\"] > :is(button, a, [role=\"option\"], [role^=\"menuitem\"]) {\n box-sizing: border-box !important;\n flex: 0 0 auto !important;\n height: var(--ds-row-height) !important;\n min-height: var(--ds-row-height) !important;\n max-height: var(--ds-row-height) !important;\n margin-block: 0 !important;\n padding-block: 0 !important;\n line-height: var(--ds-type-lineheight-default) !important;\n}\n\n/* A menu item may be a native button: it keeps the row's look, not a button's. */\n:where(button).ds-menu__item {\n appearance: none;\n margin: 0;\n border: 0;\n background: transparent;\n font: inherit;\n letter-spacing: inherit;\n text-align: start;\n}\n\n.ds-menu__item--inset {\n padding-left: calc(var(--ds-unit) + calc(4px * var(--ds-sf)));\n}\n\n.ds-menu__item:hover,\n.ds-menu__item:focus-visible,\n.ds-menu__item[data-highlighted] {\n background: var(--ds-fill);\n}\n\n.ds-menu__item[data-variant=\"destructive\"] {\n color: var(--ds-danger-color, var(--ds-tuned-red));\n}\n\n.ds-menu__item[data-disabled],\n.ds-menu__item[aria-disabled=\"true\"] {\n opacity: 0.5;\n pointer-events: none;\n}\n\n.ds-menu__indicator {\n position: absolute;\n left: calc(6px * var(--ds-sf));\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: calc(12px * var(--ds-sf));\n height: calc(12px * var(--ds-sf));\n pointer-events: none;\n}\n\n.ds-menu__indicator svg {\n width: 100%;\n height: 100%;\n}\n\n.ds-menu__chevron {\n width: calc(12px * var(--ds-sf));\n height: calc(12px * var(--ds-sf));\n margin-left: auto;\n color: var(--ds-text-color-muted);\n}\n\n.ds-menu__label {\n box-sizing: border-box;\n min-height: var(--ds-row-height);\n padding: calc(2px * var(--ds-sf)) calc(6px * var(--ds-sf));\n color: var(--ds-title-color);\n font-size: var(--ds-type-size-small);\n}\n\n.ds-menu__separator {\n flex: 0 0 auto;\n height: 1px;\n /* One square with its line. */\n margin: calc(var(--ds-unit) / 2 - 1px) calc(6px * var(--ds-sf)) calc(var(--ds-unit) / 2);\n background: var(--ds-border-color);\n}\n\n.ds-menu__shortcut {\n margin-left: auto;\n padding-left: var(--ds-unit);\n color: var(--ds-title-color);\n font-size: var(--ds-type-size-small);\n}\n\n/*\n * Pieces the components draw, as classes on native elements: a field's label\n * row, an avatar, a badge, a toggle button, an icon and a file drop zone. Each\n * reads the same tokens as its element, so a page without Lit draws the same\n * look.\n */\n\n/*\n * `.ds-field` is the label row `ds-input` draws, around any native control:\n * the label (`.ds-field__label`) on a one-square line above it, then a hint\n * (`.ds-field__hint`) in the muted ink and an error (`.ds-field__error`) in\n * the danger colour, each on one-square lines. Every row is at least one\n * square high and a control lower than that stands centred on it, so the\n * field keeps to whole squares. `.ds-field--beside` sets the label beside the\n * control, in a column `--ds-field-label-width` wide (four squares), with the\n * hint and the error under the control. An empty hint or error takes no room,\n * so the error can stay in the page as a live region.\n */\n\n.ds-field {\n box-sizing: border-box;\n display: grid;\n grid-template-columns: minmax(0, 1fr);\n grid-auto-rows: minmax(var(--ds-row-height), auto);\n align-items: center;\n justify-items: start;\n min-width: 0;\n margin: 0;\n color: var(--ds-text-color);\n font-family: var(--ds-typeface-regular);\n font-feature-settings: var(--ds-font-features);\n font-size: var(--ds-type-size-default);\n font-weight: var(--ds-type-weight-default);\n line-height: var(--ds-unit);\n letter-spacing: var(--ds-type-letterspacing-default);\n}\n\n.ds-field > :is(.ds-input, .ds-select) {\n justify-self: stretch;\n}\n\n.ds-field__label,\n.ds-field__hint,\n.ds-field__error {\n display: block;\n min-width: 0;\n margin: 0;\n font: inherit;\n letter-spacing: inherit;\n}\n\n.ds-field__label {\n color: var(--ds-field-label-color, var(--ds-text-color));\n}\n\n.ds-field__hint {\n color: var(--ds-text-color-muted);\n}\n\n.ds-field__error {\n color: var(--ds-danger-color);\n}\n\n.ds-field__hint:empty,\n.ds-field__error:empty {\n display: none;\n}\n\n.ds-field--beside {\n grid-template-columns: var(--ds-field-label-width, calc(4 * var(--ds-unit))) minmax(0, 1fr);\n}\n\n.ds-field--beside > * {\n grid-column: 2;\n}\n\n.ds-field--beside > .ds-field__label {\n grid-column: 1;\n grid-row: 1;\n align-self: start;\n}\n\n/*\n * `.ds-avatar` is the `ds-avatar` element's mark: round and one control high,\n * an initial in small type on the neutral fill, or an `<img alt=\"\">` after the\n * initial, covering it. While the picture loads the initial shows; remove a\n * picture that fails (`onerror`), as the element does, or the browser draws\n * its broken-image mark. `--large` is two squares with type a square high,\n * `--pair` fits two characters between the hairline edges, `--square` gives it\n * a picture's corners, `--outlined` draws the hairline in its ink and\n * `--pending` greys out someone who has not accepted yet.\n * `--ds-avatar-background` fills it with a colour and `--ds-avatar-color`\n * letters it: on a fill, pick the black or white ink that reads on it\n * (`--ds-black`, `--ds-white`).\n */\n\n.ds-avatar {\n --ds-avatar-size: var(--ds-control-height);\n --ds-avatar-pair-type-size: var(--ds-type-size-small);\n box-sizing: border-box;\n position: relative;\n display: inline-grid;\n place-items: center;\n flex: 0 0 auto;\n vertical-align: middle;\n width: var(--ds-avatar-size);\n height: var(--ds-avatar-size);\n overflow: hidden;\n border-radius: 50%;\n corner-shape: round;\n background: var(--ds-avatar-background, var(--ds-fill));\n color: var(--ds-avatar-color, var(--ds-text-color));\n font: var(--ds-type-weight-default) var(--ds-type-size-small) / 1 var(--ds-typeface-regular);\n letter-spacing: normal;\n text-align: center;\n white-space: nowrap;\n user-select: none;\n -webkit-user-select: none;\n}\n\n.ds-avatar > img {\n position: absolute;\n inset: 0;\n display: block;\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n\n.ds-avatar--large {\n --ds-avatar-size: calc(2 * var(--ds-unit));\n --ds-avatar-pair-type-size: var(--ds-type-size-default);\n font-size: var(--ds-unit);\n}\n\n.ds-avatar--pair {\n font-size: min(var(--ds-avatar-pair-type-size), calc((var(--ds-avatar-size) - 2px) / 2));\n}\n\n.ds-avatar--square {\n border-radius: var(--ds-avatar-radius, var(--ds-radius));\n corner-shape: var(--ds-corner-shape, round);\n}\n\n.ds-avatar--outlined::after {\n content: \"\";\n position: absolute;\n inset: 0;\n border: 1px solid var(--ds-avatar-outline, currentColor);\n border-radius: inherit;\n pointer-events: none;\n}\n\n.ds-avatar--pending {\n background: var(--ds-fill);\n color: var(--ds-text-color-muted);\n}\n\n.ds-avatar--pending > img {\n filter: grayscale(1);\n opacity: 0.5;\n}\n\n/*\n * `.ds-badge` is the `ds-badge` element's status label: one square high on the\n * neutral fill, its words padded 4px and cut short with an ellipsis, never a\n * button. Where the browser can size to content in calc (calc-size), its width\n * rounds up to whole squares as the element's does; elsewhere it hugs the\n * words. An icon inside is a `.ds-icon` before the words. `--icon` shows the\n * icon alone, one square wide, and keeps the words for assistive technology:\n * put them in an element of their own. `--ds-badge-background` and\n * `--ds-badge-color` give a status a meaningful colour.\n */\n\n.ds-badge {\n box-sizing: border-box;\n display: inline-block;\n flex: 0 0 auto;\n vertical-align: middle;\n width: max-content;\n width: calc-size(max-content, round(up, size, var(--ds-unit)));\n min-width: var(--ds-row-height);\n max-width: 100%;\n height: var(--ds-row-height);\n padding: 0 calc(4px * var(--ds-sf));\n overflow: hidden;\n background: var(--ds-badge-background, var(--ds-fill));\n color: var(--ds-badge-color, var(--ds-text-color));\n font-family: var(--ds-typeface-regular);\n font-feature-settings: var(--ds-font-features);\n font-size: var(--ds-type-size-default);\n font-weight: var(--ds-type-weight-default);\n line-height: var(--ds-row-height);\n letter-spacing: var(--ds-type-letterspacing-default);\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.ds-badge > .ds-icon {\n vertical-align: top;\n margin-top: calc((var(--ds-row-height) - var(--ds-icon-box, var(--ds-control-height))) / 2);\n margin-right: calc(4px * var(--ds-sf));\n}\n\n.ds-badge--icon {\n position: relative;\n width: var(--ds-row-height);\n padding: 0;\n text-align: center;\n}\n\n.ds-badge--icon > .ds-icon {\n margin-right: 0;\n}\n\n.ds-badge--icon > :not(.ds-icon) {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n}\n\n/*\n * `.ds-toggle` makes a `.ds-button` a toggle button, `<button aria-pressed>`:\n * the secondary button's grey while it is off, the primary button's accent\n * while it is on, as a chosen segment of `.ds-tabs[role=\"group\"]` is. Its\n * words stay the same in both states; the page toggles `aria-pressed`.\n * A setting that is on or off is a `.ds-switch`; one of several choices is a\n * `.ds-tabs[role=\"group\"]`.\n */\n\n.ds-toggle {\n --ds-button-padding: 0 calc(4px * var(--ds-sf));\n --ds-button-background: var(--ds-button-background-color-secondary);\n}\n\n.ds-toggle:hover:not(:disabled, [aria-disabled=\"true\"]) {\n --ds-button-background: var(--ds-fill-hover);\n}\n\n.ds-toggle[aria-pressed=\"true\"],\n.ds-toggle[aria-pressed=\"true\"]:hover:not(:disabled, [aria-disabled=\"true\"]) {\n --ds-button-background: var(--ds-button-background-color-primary);\n --ds-button-color: var(--ds-button-text-color-on-accent);\n}\n\n/*\n * `.ds-icon` is the `ds-icon` element's drawing without the element: a\n * Design system 1 icon file as a mask, painted in the text colour\n * (`--ds-icon-color`), 14px by default, 12px with `--small` and 16px with\n * `--large`, times the scale. `--ds-icon` names the file, as Sync 1's `icons`\n * export copies it: `style=\"--ds-icon: url(icons/close.svg)\"` (a path from the\n * page) or a rule per icon in the page's stylesheet. The mask takes only the\n * drawing's shape, so an icon with colours of its own is an `<img>`. These\n * rules have no specificity: a page that drew its own `.ds-icon` keeps its\n * rule until it removes it.\n */\n\n:where(.ds-icon) {\n --ds-icon-box: calc(var(--ds-icon-size, 14px) * var(--ds-sf));\n display: inline-block;\n flex: 0 0 auto;\n width: var(--ds-icon-box);\n height: calc(var(--ds-icon-box) / var(--ds-icon-ratio, 1));\n color: var(--ds-icon-color, currentColor);\n background-color: currentColor;\n -webkit-mask: var(--ds-icon) center / contain no-repeat;\n mask: var(--ds-icon) center / contain no-repeat;\n}\n\n:where(.ds-icon--small) {\n --ds-icon-size: 12px;\n}\n\n:where(.ds-icon--large) {\n --ds-icon-size: 16px;\n}\n\n/*\n * `.ds-drop` is `ds-file-picker`'s area on a `<label>` around a native\n * `<input type=\"file\">`: the secondary button's face, as wide as its row and\n * eight squares high (`--compact`: four), its words centred in a square of\n * padding. The input covers the zone unseen, so a click, the keyboard and a\n * dropped file all reach it, and the browser keeps labels, forms, `required`,\n * `multiple` and reset. Keyboard focus draws the focus ring. A page that also\n * handles drops on a larger surface sets `data-dragging` on the zone while\n * files are dragged over it, for the ring and the hover fill. A `.ds-button`\n * on a `<span>` inside draws a button face among the words.\n *\n * The same native input directly inside a `label.ds-button` is the file\n * picker's button: the button's face, one square high.\n */\n\n.ds-drop {\n box-sizing: border-box;\n position: relative;\n display: flex;\n flex-wrap: wrap;\n align-content: center;\n align-items: center;\n justify-content: center;\n gap: calc(var(--ds-unit) / 2);\n width: 100%;\n min-width: 0;\n height: calc(8 * var(--ds-unit));\n margin: 0;\n padding: var(--ds-unit);\n overflow: hidden;\n border-radius: var(--ds-radius);\n corner-shape: var(--ds-corner-shape, round);\n background: var(--ds-button-background-color-secondary);\n color: var(--ds-button-text-color);\n font-family: var(--ds-typeface-regular);\n font-feature-settings: var(--ds-font-features);\n font-size: var(--ds-type-size-default);\n font-weight: var(--ds-type-weight-default);\n line-height: var(--ds-type-lineheight-default);\n letter-spacing: var(--ds-type-letterspacing-default);\n text-align: center;\n cursor: pointer;\n user-select: none;\n -webkit-user-select: none;\n -webkit-tap-highlight-color: transparent;\n}\n\n.ds-drop--compact {\n height: calc(4 * var(--ds-unit));\n}\n\nlabel.ds-button:has(> input[type=\"file\"]) {\n --ds-button-height: var(--ds-row-height);\n position: relative;\n overflow: hidden;\n}\n\n:is(.ds-drop, label.ds-button) > input[type=\"file\"]:not([hidden]) {\n position: absolute;\n inset: 0;\n box-sizing: border-box;\n display: block;\n width: 100%;\n height: 100%;\n min-width: 0;\n margin: 0;\n padding: 0;\n border: 0;\n opacity: 0;\n cursor: pointer;\n}\n\n:is(.ds-drop, label.ds-button) > input[type=\"file\"]::file-selector-button {\n cursor: pointer;\n}\n\n.ds-drop:hover:not(:has(> input:disabled)),\n.ds-drop[data-dragging]:not(:has(> input:disabled)) {\n background: var(--ds-fill-hover);\n}\n\n.ds-drop:has(> input:focus-visible),\n.ds-drop[data-dragging]:not(:has(> input:disabled)),\nlabel.ds-button:has(> input[type=\"file\"]:focus-visible) {\n outline: 2px solid var(--ds-focus-ring);\n outline-offset: 1px;\n}\n\n.ds-drop:has(> input:disabled),\nlabel.ds-button:has(> input[type=\"file\"]:disabled) {\n cursor: default;\n opacity: 0.5;\n}\n\n:is(.ds-drop, label.ds-button) > input[type=\"file\"]:disabled {\n cursor: default;\n}\n";
8832
8832
  //#endregion
8833
8833
  //#region DS1/2-core/styles/ds-notice.css?inline
8834
8834
  var ds_notice_default = "/* ds-notice.css */\n/* The element only places the block; 1-root/notice.css draws it and\n controls.css its dismiss button. */\n\n:host {\n display: block;\n min-width: 0;\n}\n\n:host([hidden]),\n[hidden] {\n display: none !important;\n}\n\n/* The banner is placed by the host; its block then fills the host. */\n:host([banner]),\n:host([form=\"banner\"]) {\n position: absolute;\n top: 0;\n inset-inline: 0;\n z-index: 9999;\n}\n:host([banner]) .ds-notice--banner,\n:host([form=\"banner\"]) .ds-notice--banner {\n position: static;\n}\n\n/* The app banner stands in the flow, at the top of the page. */\n:host([form=\"app\"]) {\n display: block;\n}\n\n/* Slotted words take the notice's lines rather than their own margins. */\n::slotted([slot=\"heading\"]),\n::slotted(:not([slot])) {\n margin: 0;\n}\n\n/* The app's own picture in the icon slot fills its two squares. */\n::slotted(img[slot=\"icon\"]) {\n display: block;\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n";
@@ -10741,7 +10741,7 @@ var CalendarBase = class extends i$2 {
10741
10741
  this.relabel = () => this.requestUpdate();
10742
10742
  this.value = "";
10743
10743
  this.selectable = false;
10744
- this.range = false;
10744
+ this.endPending = false;
10745
10745
  this.literal = false;
10746
10746
  this.events = [];
10747
10747
  }
@@ -10758,6 +10758,7 @@ var CalendarBase = class extends i$2 {
10758
10758
  willUpdate(changed) {
10759
10759
  super.willUpdate(changed);
10760
10760
  if (changed.has("value") && this.value && !readRange(this.value).start) this.value = "";
10761
+ if (changed.has("value") && this.value !== this.pressedValue) this.endPending = false;
10761
10762
  }
10762
10763
  /** Shared words, filled in: `words("Week {week}", { week: 42 })`. */
10763
10764
  words(key, params) {
@@ -10788,21 +10789,18 @@ var CalendarBase = class extends i$2 {
10788
10789
  /** The chosen days: the first and the last of a range, or the one day twice. */
10789
10790
  get chosen() {
10790
10791
  const { start, end } = readRange(this.value);
10791
- return this.range ? {
10792
- start,
10793
- end
10794
- } : {
10792
+ return {
10795
10793
  start,
10796
- end: start
10794
+ end: end || start
10797
10795
  };
10798
10796
  }
10799
10797
  /** How a day stands to the selection: an end of it, inside it, or neither; with the end still to come, the pointer previews it. */
10800
10798
  selection(day) {
10801
10799
  let { start, end } = this.chosen;
10802
- if (this.range && start && !end && this.hoverDay) end = this.hoverDay;
10800
+ if (this.endPending && start && this.hoverDay) end = this.hoverDay;
10803
10801
  if (end && end < start) [start, end] = [end, start];
10804
10802
  const selected = day === start || day === end;
10805
- const spans = Boolean(this.range && start && end && start !== end);
10803
+ const spans = Boolean(start && end && start !== end);
10806
10804
  return {
10807
10805
  selected,
10808
10806
  inside: spans && day > start && day < end,
@@ -10834,7 +10832,10 @@ var CalendarBase = class extends i$2 {
10834
10832
  }
10835
10833
  /**
10836
10834
  * A person pressed a day: `day-press` fires, and unless it is cancelled a
10837
- * selectable calendar takes the day and fires `change`.
10835
+ * selectable calendar takes the day and fires `change`. There is one way to
10836
+ * pick: the first press chooses a day, which is the value; the next ends a
10837
+ * range from it, before or after (the same day again keeps the one day);
10838
+ * the press after that starts again.
10838
10839
  */
10839
10840
  press(day) {
10840
10841
  const pressed = new CustomEvent("day-press", {
@@ -10848,15 +10849,15 @@ var CalendarBase = class extends i$2 {
10848
10849
  });
10849
10850
  if (!this.dispatchEvent(pressed) || !this.selectable || !this.allowed(day)) return;
10850
10851
  const previous = this.value || "";
10851
- let value = day;
10852
- if (this.range) {
10853
- const { start, end } = readRange(previous);
10854
- if (start && !end) value = day < start ? `${day}/${start}` : `${start}/${day}`;
10855
- }
10852
+ const { start: first } = readRange(previous);
10853
+ const ending = this.endPending && Boolean(first);
10854
+ const value = !ending || day === first ? day : day < first ? `${day}/${first}` : `${first}/${day}`;
10856
10855
  this.focusDay = day;
10857
- if (value === previous) return;
10858
- this.value = value;
10856
+ this.endPending = !ending;
10859
10857
  this.hoverDay = void 0;
10858
+ this.pressedValue = value;
10859
+ if (value === previous && !ending) return;
10860
+ this.value = value;
10860
10861
  const { start, end } = readRange(value);
10861
10862
  this.dispatchEvent(new CustomEvent("change", {
10862
10863
  bubbles: true,
@@ -10865,7 +10866,7 @@ var CalendarBase = class extends i$2 {
10865
10866
  value,
10866
10867
  previous,
10867
10868
  start,
10868
- end: this.range ? end : start
10869
+ end: ending ? end || start : ""
10869
10870
  }
10870
10871
  }));
10871
10872
  }
@@ -10915,10 +10916,6 @@ _CalendarBase.properties = {
10915
10916
  type: Boolean,
10916
10917
  reflect: true
10917
10918
  },
10918
- range: {
10919
- type: Boolean,
10920
- reflect: true
10921
- },
10922
10919
  label: { type: String },
10923
10920
  literal: {
10924
10921
  type: Boolean,
@@ -10926,21 +10923,64 @@ _CalendarBase.properties = {
10926
10923
  },
10927
10924
  events: { attribute: false },
10928
10925
  focusDay: { state: true },
10929
- hoverDay: { state: true }
10926
+ hoverDay: { state: true },
10927
+ endPending: { state: true }
10930
10928
  };
10931
10929
  //#endregion
10932
10930
  //#region DS1/3-unit/styles/ds-calendar.css?inline
10933
- var ds_calendar_default = "/* ds-calendar.css\n * A month on whole squares. A day is --ds-calendar-day-squares wide (four,\n * one when compact) and, beside its date's square, holds --ds-calendar-lines\n * one-square lines of events and one square to spare. Every line of words\n * stands on the lower sixteen of its square. The boxes are hairlines centred\n * on the grid lines; today is a red disc nine tenths of a square across. */\n\n:host {\n --ds-calendar-day: calc(var(--ds-unit) * var(--ds-calendar-day-squares, 4));\n --ds-calendar-day-height: calc(var(--ds-unit) * (var(--ds-calendar-lines, 2) + 2));\n --ds-calendar-line: 1px solid var(--ds-border-color);\n --ds-calendar-today: var(--ds-red-for-blood);\n --ds-calendar-today-ink: var(--ds-white, #fff);\n --ds-calendar-selected: var(--ds-sharp-blue);\n --ds-calendar-selected-ink: var(--ds-white, #fff);\n --ds-calendar-band: color-mix(in srgb, var(--ds-calendar-selected) 16%, var(--ds-paper));\n --ds-calendar-mark: calc(var(--ds-unit) * 0.9);\n --ds-calendar-pad: calc(4px * var(--ds-sf, 1));\n box-sizing: border-box;\n display: inline-block;\n vertical-align: top;\n color: var(--ds-text-color);\n font-family: var(--ds-typeface-regular);\n font-feature-settings: var(--ds-font-features);\n font-size: var(--ds-type-size-default);\n font-weight: var(--ds-type-weight-default);\n line-height: var(--ds-type-lineheight-default);\n letter-spacing: var(--ds-type-letterspacing-default);\n}\n:host([hidden]) { display: none !important; }\n/* A compact day is square: one square, or two where the month has room. Its marks grow with it. */\n:host([compact]) {\n --ds-calendar-day-height: var(--ds-calendar-day);\n --ds-calendar-mark: calc(var(--ds-unit) * (var(--ds-calendar-day-squares, 1) * 0.75 + 0.15));\n}\n\n.calendar { width: calc(7 * var(--ds-calendar-day)); }\n\n/* The title and the arrows: one square, one square above the days (none when compact). */\n.header {\n box-sizing: border-box;\n display: flex;\n align-items: flex-end;\n gap: var(--ds-calendar-pad);\n height: var(--ds-unit);\n margin-block-end: var(--ds-unit);\n}\n:host([compact]) .header { margin-block-end: 0; }\n.title {\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n font-family: var(--ds-typeface-medium, var(--ds-typeface-regular));\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.controls {\n display: flex;\n flex: 0 0 auto;\n align-items: flex-end;\n gap: 0;\n}\n/* Each arrow is a chevron on its own square, one control high at the square's foot,\n * in the title grey; the pointer fills it with the system's corners and inks it. */\n.nav {\n flex: 0 0 auto;\n width: var(--ds-unit);\n height: var(--ds-control-height);\n border-radius: var(--ds-radius);\n color: var(--ds-title-color);\n}\n.nav:hover:not(:disabled) { background: var(--ds-fill); color: var(--ds-text-color); }\n:host(:dir(rtl)) .nav > ds-icon { transform: scaleX(-1); }\n\n.grid { outline: none; }\n.weekdays,\n.week {\n display: grid;\n grid-template-columns: repeat(7, var(--ds-calendar-day));\n}\n\n/* The weekday names: a block line in the title grey, at the start of a wide day, centred on a compact one. */\n.weekday {\n box-sizing: border-box;\n height: var(--ds-unit);\n padding: calc(4px * var(--ds-sf, 1)) var(--ds-calendar-pad) 0;\n overflow: hidden;\n color: var(--ds-title-color);\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n:host([compact]) .weekday { padding-inline: 0; text-align: center; }\n\n/* A day's box: hairlines on the grid lines, top and start; the last column\n * and the last week close it at the end and the foot. */\n.day {\n position: relative;\n box-sizing: border-box;\n min-width: 0;\n height: var(--ds-calendar-day-height);\n outline: none;\n}\n.day::after {\n position: absolute;\n inset: -0.5px;\n border-block-start: var(--ds-calendar-line);\n border-inline-start: var(--ds-calendar-line);\n content: \"\";\n pointer-events: none;\n}\n.day:nth-child(7)::after { border-inline-end: var(--ds-calendar-line); }\n.week:last-child .day::after { border-block-end: var(--ds-calendar-line); }\n:host([compact]) .day::after { content: none; }\n\n.day[data-day] { cursor: default; }\n:host([selectable]) .day[data-day]:not([aria-disabled=\"true\"]) { cursor: pointer; }\n:host([selectable]:not([compact])) .day[data-day]:not([aria-disabled=\"true\"]):hover { background: var(--ds-row-fill-hover, var(--ds-fill)); }\n.day:focus-visible { outline: 2px solid var(--ds-focus-ring); outline-offset: -2px; }\n:host([compact]) .day:focus-visible { outline: none; }\n:host([compact]) .day:focus-visible .date { outline: 2px solid var(--ds-focus-ring); outline-offset: -1px; }\n\n/* The date: on the first square, at the end of a wide day, in the title grey. */\n.date-line {\n box-sizing: border-box;\n display: flex;\n align-items: flex-end;\n justify-content: flex-end;\n gap: var(--ds-calendar-pad);\n height: var(--ds-unit);\n padding-inline: var(--ds-calendar-pad);\n}\n.date {\n color: var(--ds-title-color);\n font-variant-numeric: tabular-nums;\n}\n.week-number {\n margin-inline-end: auto;\n color: var(--ds-title-color);\n font-family: var(--ds-typeface-compressed);\n}\n\n/* A compact day: the date centred in its square, in the ink; with events,\n * the empty days fall back to the title grey and the busy ones keep the ink. */\n:host([compact]) .date-line { justify-content: center; padding: 0; }\n:host([compact]) .date { color: var(--ds-text-color); }\n:host([compact][data-events]) .day:not([data-busy]) .date { color: var(--ds-title-color); }\n:host([compact][selectable]) .day[data-day]:not([aria-disabled=\"true\"], [data-today], [data-selected]):hover .date { background: var(--ds-fill); }\n\n/* Today and the selected day: a disc nine tenths of a square, centred on the date's square. */\n.day:is([data-today], [data-selected]) .date-line { align-items: center; padding-inline-end: calc(var(--ds-unit) * 0.05); }\n:host([compact]) .day:is([data-today], [data-selected]) .date-line { padding: 0; }\n.date {\n box-sizing: border-box;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: var(--ds-calendar-mark);\n height: var(--ds-calendar-mark);\n border-radius: 999px;\n}\n.day:not([data-today], [data-selected]) .date { min-width: 0; height: auto; }\n:host([compact]) .day .date { min-width: var(--ds-calendar-mark); height: var(--ds-calendar-mark); }\n.day[data-today] .date { background: var(--ds-calendar-today); color: var(--ds-calendar-today-ink); letter-spacing: 0; }\n/* The selected day, and each end of a range: a square with the system's corners in the selection's blue. */\n.day[data-selected] .date { border-radius: var(--ds-radius); background: var(--ds-calendar-selected); color: var(--ds-calendar-selected-ink); letter-spacing: 0; }\n\n.day[aria-disabled=\"true\"] .date { color: var(--ds-title-color); opacity: 0.5; }\n\n/* Events: one square each, stacked edge to edge on the day's lines, drawn one pixel short. */\n.events {\n display: flex;\n flex-direction: column;\n}\n.event,\n.more {\n box-sizing: border-box;\n display: flex;\n align-items: flex-end;\n gap: var(--ds-calendar-pad);\n height: calc(var(--ds-unit) - 1px);\n margin: 0.5px;\n padding-inline: var(--ds-calendar-pad);\n overflow: hidden;\n white-space: nowrap;\n}\n.event {\n background: var(--ds-calendar-event-fill, var(--ds-fill));\n color: var(--ds-calendar-event-ink, var(--ds-text-color));\n cursor: pointer;\n}\n.event .name { min-width: 0; overflow: hidden; text-overflow: ellipsis; }\n.event .time { flex: 0 0 auto; opacity: 0.6; font-variant-numeric: tabular-nums; }\n.more { color: var(--ds-title-color); cursor: pointer; }\n.more:hover { color: var(--ds-text-color); }\n\n/* A day whose hidden events are unfolded lays them over the days below. */\n.day[data-open] { z-index: 1; }\n.day[data-open] .events {\n position: absolute;\n inset-block-start: var(--ds-unit);\n inset-inline: 0;\n background: var(--ds-paper);\n box-shadow: 0 0 0 1px var(--ds-border-color);\n}\n\n@media (prefers-reduced-motion: no-preference) {\n .event, .more { transition: opacity 120ms ease; }\n}\n\n/* A compact day's date stands in the middle of its square once the square is two or more wide. */\n:host([compact][data-roomy]) .date-line { align-items: center; height: var(--ds-calendar-day-height); }\n\n/* The days of the months before and after, in the title grey. */\n.day[data-outside] .date { color: var(--ds-title-color); }\n:host([compact]) .day[data-outside] .date { color: var(--ds-title-color); }\n\n/* A range: the days between its ends on a band of the selection's blue; the band runs into each end's square. */\n.day[data-inside] { background: var(--ds-calendar-band); }\n:host(:not([compact])) .day:is([data-first], [data-last]) { background: var(--ds-calendar-band); }\n:host([compact]) .day[data-first] { background: linear-gradient(to right, transparent 50%, var(--ds-calendar-band) 50%); }\n:host([compact]) .day[data-last] { background: linear-gradient(to left, transparent 50%, var(--ds-calendar-band) 50%); }\n:host([compact]:dir(rtl)) .day[data-first] { background: linear-gradient(to left, transparent 50%, var(--ds-calendar-band) 50%); }\n:host([compact]:dir(rtl)) .day[data-last] { background: linear-gradient(to right, transparent 50%, var(--ds-calendar-band) 50%); }\n:host([compact][selectable]) .day[data-inside]:hover .date { background: transparent; }\n\n/* Today, as words in the title grey beside the arrows. */\n.today-button {\n height: var(--ds-control-height);\n padding-inline: calc(4px * var(--ds-sf, 1));\n border: 0;\n background: transparent;\n color: var(--ds-title-color);\n}\n.today-button:hover { background: var(--ds-fill); color: var(--ds-text-color); }\n\n/* In a compact month the pointer's day and the chosen day take squares with the system's corners; today keeps its disc. */\n:host([compact]) .date { border-radius: var(--ds-radius); }\n:host([compact]) .day[data-today]:not([data-selected]) .date { border-radius: 999px; }\n";
10931
+ var ds_calendar_default = "/* ds-calendar.css\n * A month on whole squares. A day is --ds-calendar-day-squares wide (four,\n * one when compact) and, beside its date's square, holds --ds-calendar-lines\n * one-square lines of events and one square to spare. Every line of words\n * stands on the lower sixteen of its square. The boxes are hairlines centred\n * on the grid lines; today is a red disc nine tenths of a square across.\n *\n * A picker is the select's face: one control high at the foot of its square,\n * ten squares wide by default, the calendar icon at its start and the down\n * arrow at its end. Its month opens on the menu's frame, half a square from\n * the field: a 1px line, half a square inside it, no shadow, so eight squares\n * wide. Inside, on whole squares: the first and last day to type (each with\n * its time below when times are on), one square spare, the compact month one\n * square a day, then Include time and Clear, each one square, set off by\n * one-square separators with a line through them. */\n\n:host {\n --ds-calendar-day: calc(var(--ds-unit) * var(--ds-calendar-day-squares, 4));\n --ds-calendar-day-height: calc(var(--ds-unit) * (var(--ds-calendar-lines, 2) + 2));\n --ds-calendar-line: 1px solid var(--ds-border-color);\n --ds-calendar-today: var(--ds-red-for-blood);\n --ds-calendar-today-ink: var(--ds-white, #fff);\n --ds-calendar-selected: var(--ds-sharp-blue);\n --ds-calendar-selected-ink: var(--ds-white, #fff);\n --ds-calendar-band: color-mix(in srgb, var(--ds-calendar-selected) 16%, var(--ds-paper));\n --ds-calendar-mark: calc(var(--ds-unit) * 0.9);\n --ds-calendar-pad: calc(4px * var(--ds-sf, 1));\n box-sizing: border-box;\n display: inline-block;\n vertical-align: top;\n color: var(--ds-text-color);\n font-family: var(--ds-typeface-regular);\n font-feature-settings: var(--ds-font-features);\n font-size: var(--ds-type-size-default);\n font-weight: var(--ds-type-weight-default);\n line-height: var(--ds-type-lineheight-default);\n letter-spacing: var(--ds-type-letterspacing-default);\n}\n:host([hidden]) { display: none !important; }\n/* A compact day is square: one square, or two where the month has room. Its marks grow with it. */\n:host([compact]) {\n --ds-calendar-day-height: var(--ds-calendar-day);\n --ds-calendar-mark: calc(var(--ds-unit) * (var(--ds-calendar-day-squares, 1) * 0.75 + 0.15));\n}\n\n.calendar { width: calc(7 * var(--ds-calendar-day)); }\n\n/* The title and the arrows: one square, one square above the days (none when compact). */\n.header {\n box-sizing: border-box;\n display: flex;\n align-items: flex-end;\n gap: var(--ds-calendar-pad);\n height: var(--ds-unit);\n margin-block-end: var(--ds-unit);\n}\n:host([compact]) .header { margin-block-end: 0; }\n.title {\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n font-family: var(--ds-typeface-medium, var(--ds-typeface-regular));\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.controls {\n display: flex;\n flex: 0 0 auto;\n align-items: flex-end;\n gap: 0;\n}\n/* Each arrow is a chevron on its own square, one control high at the square's foot,\n * in the title grey; the pointer fills it with the system's corners and inks it. */\n.nav {\n flex: 0 0 auto;\n width: var(--ds-unit);\n height: var(--ds-control-height);\n border-radius: var(--ds-radius);\n color: var(--ds-title-color);\n}\n.nav:hover:not(:disabled) { background: var(--ds-fill); color: var(--ds-text-color); }\n:host(:dir(rtl)) .nav > ds-icon { transform: scaleX(-1); }\n\n.grid { outline: none; }\n.weekdays,\n.week {\n display: grid;\n grid-template-columns: repeat(7, var(--ds-calendar-day));\n}\n\n/* The weekday names: a block line in the title grey, at the start of a wide day, centred on a compact one. */\n.weekday {\n box-sizing: border-box;\n height: var(--ds-unit);\n padding: calc(4px * var(--ds-sf, 1)) var(--ds-calendar-pad) 0;\n overflow: hidden;\n color: var(--ds-title-color);\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n:host([compact]) .weekday { padding-inline: 0; text-align: center; }\n\n/* A day's box: hairlines on the grid lines, top and start; the last column\n * and the last week close it at the end and the foot. */\n.day {\n position: relative;\n box-sizing: border-box;\n min-width: 0;\n height: var(--ds-calendar-day-height);\n outline: none;\n}\n.day::after {\n position: absolute;\n inset: -0.5px;\n border-block-start: var(--ds-calendar-line);\n border-inline-start: var(--ds-calendar-line);\n content: \"\";\n pointer-events: none;\n}\n.day:nth-child(7)::after { border-inline-end: var(--ds-calendar-line); }\n.week:last-child .day::after { border-block-end: var(--ds-calendar-line); }\n:host([compact]) .day::after { content: none; }\n\n.day[data-day] { cursor: default; }\n:host([selectable]) .day[data-day]:not([aria-disabled=\"true\"]) { cursor: pointer; }\n:host([selectable]:not([compact])) .day[data-day]:not([aria-disabled=\"true\"]):hover { background: var(--ds-row-fill-hover, var(--ds-fill)); }\n.day:focus-visible { outline: 2px solid var(--ds-focus-ring); outline-offset: -2px; }\n:host([compact]) .day:focus-visible { outline: none; }\n:host([compact]) .day:focus-visible .date { outline: 2px solid var(--ds-focus-ring); outline-offset: -1px; }\n\n/* The date: on the first square, at the end of a wide day, in the title grey. */\n.date-line {\n box-sizing: border-box;\n display: flex;\n align-items: flex-end;\n justify-content: flex-end;\n gap: var(--ds-calendar-pad);\n height: var(--ds-unit);\n padding-inline: var(--ds-calendar-pad);\n}\n.date {\n color: var(--ds-title-color);\n font-variant-numeric: tabular-nums;\n}\n.week-number {\n margin-inline-end: auto;\n color: var(--ds-title-color);\n font-family: var(--ds-typeface-compressed);\n}\n\n/* A compact day: the date centred in its square, in the ink; with events,\n * the empty days fall back to the title grey and the busy ones keep the ink. */\n:host([compact]) .date-line { justify-content: center; padding: 0; }\n:host([compact]) .date { color: var(--ds-text-color); }\n:host([compact][data-events]) .day:not([data-busy]) .date { color: var(--ds-title-color); }\n:host([compact][selectable]) .day[data-day]:not([aria-disabled=\"true\"], [data-today], [data-selected]):hover .date { background: var(--ds-fill); }\n\n/* Today and the selected day: a disc nine tenths of a square, centred on the date's square. */\n.day:is([data-today], [data-selected]) .date-line { align-items: center; padding-inline-end: calc(var(--ds-unit) * 0.05); }\n:host([compact]) .day:is([data-today], [data-selected]) .date-line { padding: 0; }\n.date {\n box-sizing: border-box;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: var(--ds-calendar-mark);\n height: var(--ds-calendar-mark);\n border-radius: 999px;\n}\n.day:not([data-today], [data-selected]) .date { min-width: 0; height: auto; }\n:host([compact]) .day .date { min-width: var(--ds-calendar-mark); height: var(--ds-calendar-mark); }\n.day[data-today] .date { background: var(--ds-calendar-today); color: var(--ds-calendar-today-ink); letter-spacing: 0; }\n/* The selected day, and each end of a range: a square with the system's corners in the selection's blue. */\n.day[data-selected] .date { border-radius: var(--ds-radius); background: var(--ds-calendar-selected); color: var(--ds-calendar-selected-ink); letter-spacing: 0; }\n\n.day[aria-disabled=\"true\"] .date { color: var(--ds-title-color); opacity: 0.5; }\n\n/* Events: one square each, stacked edge to edge on the day's lines, drawn one pixel short. */\n.events {\n display: flex;\n flex-direction: column;\n}\n.event,\n.more {\n box-sizing: border-box;\n display: flex;\n align-items: flex-end;\n gap: var(--ds-calendar-pad);\n height: calc(var(--ds-unit) - 1px);\n margin: 0.5px;\n padding-inline: var(--ds-calendar-pad);\n overflow: hidden;\n white-space: nowrap;\n}\n.event {\n background: var(--ds-calendar-event-fill, var(--ds-fill));\n color: var(--ds-calendar-event-ink, var(--ds-text-color));\n cursor: pointer;\n}\n.event .name { min-width: 0; overflow: hidden; text-overflow: ellipsis; }\n.event .time { flex: 0 0 auto; opacity: 0.6; font-variant-numeric: tabular-nums; }\n.more { color: var(--ds-title-color); cursor: pointer; }\n.more:hover { color: var(--ds-text-color); }\n\n/* A day whose hidden events are unfolded lays them over the days below. */\n.day[data-open] { z-index: 1; }\n.day[data-open] .events {\n position: absolute;\n inset-block-start: var(--ds-unit);\n inset-inline: 0;\n background: var(--ds-paper);\n box-shadow: 0 0 0 1px var(--ds-border-color);\n}\n\n@media (prefers-reduced-motion: no-preference) {\n .event, .more { transition: opacity 120ms ease; }\n}\n\n/* A compact day's date stands in the middle of its square once the square is two or more wide. */\n:host([compact][data-roomy]) .date-line { align-items: center; height: var(--ds-calendar-day-height); }\n\n/* The days of the months before and after, in the title grey. */\n.day[data-outside] .date { color: var(--ds-title-color); }\n:host([compact]) .day[data-outside] .date { color: var(--ds-title-color); }\n\n/* A range: the days between its ends on a band of the selection's blue; the band runs into each end's square. */\n.day[data-inside] { background: var(--ds-calendar-band); }\n:host(:not([compact])) .day:is([data-first], [data-last]) { background: var(--ds-calendar-band); }\n:host([compact]) .day[data-first] { background: linear-gradient(to right, transparent 50%, var(--ds-calendar-band) 50%); }\n:host([compact]) .day[data-last] { background: linear-gradient(to left, transparent 50%, var(--ds-calendar-band) 50%); }\n:host([compact]:dir(rtl)) .day[data-first] { background: linear-gradient(to left, transparent 50%, var(--ds-calendar-band) 50%); }\n:host([compact]:dir(rtl)) .day[data-last] { background: linear-gradient(to right, transparent 50%, var(--ds-calendar-band) 50%); }\n:host([compact][selectable]) .day[data-inside]:hover .date { background: transparent; }\n\n/* Today, as words in the title grey beside the arrows. */\n.today-button {\n height: var(--ds-control-height);\n padding-inline: calc(4px * var(--ds-sf, 1));\n border: 0;\n background: transparent;\n color: var(--ds-title-color);\n}\n.today-button:hover { background: var(--ds-fill); color: var(--ds-text-color); }\n\n/* In a compact month the pointer's day and the chosen day take squares with the system's corners; today keeps its disc. */\n:host([compact]) .date { border-radius: var(--ds-radius); }\n:host([compact]) .day[data-today]:not([data-selected]) .date { border-radius: 999px; }\n\n/* The picker: a field that opens the small month. */\n:host([picker]) {\n --ds-calendar-picker-width: calc(10 * var(--ds-unit));\n display: inline-flex;\n align-items: flex-end;\n vertical-align: bottom;\n height: var(--ds-unit);\n}\n\n.trigger {\n display: inline-flex;\n align-items: flex-end;\n gap: calc(4px * var(--ds-sf, 1));\n width: var(--ds-calendar-picker-width);\n padding-inline: 0 calc(4px * var(--ds-sf, 1));\n text-align: start;\n cursor: pointer;\n}\n.trigger:disabled { cursor: default; }\n.trigger .icon,\n.trigger .arrow { flex: 0 0 auto; }\n.trigger .text {\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.trigger[data-empty] .text { color: var(--ds-title-color); }\n.trigger[aria-expanded=\"true\"] .arrow { transform: rotate(180deg); }\n\n/* The month's panel, on the menu's frame. */\n.popover {\n position: fixed;\n inset: auto;\n box-sizing: border-box;\n margin: 0;\n padding: calc(var(--ds-unit) / 2 - 1px);\n border: 1px solid var(--ds-border-color);\n border-radius: var(--ds-radius-card);\n background: var(--ds-paper);\n color: var(--ds-text-color);\n box-shadow: none;\n overflow: visible;\n}\n.popover:not(:popover-open) { display: none; }\n\n/* The days to type: a row of one square for each end, its time on the row below; one square spare after. */\n.fields { margin-block-end: var(--ds-unit); }\n.field-row {\n display: flex;\n align-items: flex-end;\n gap: calc(var(--ds-unit) / 2);\n height: var(--ds-unit);\n}\n.field { flex: 1 1 auto; min-width: 0; }\n.clock { flex: 1 1 auto; min-width: 0; }\n.fields, .options { width: calc(7 * var(--ds-unit)); }\n\n/* Include time and Clear, each one square, the words on the sixteen. */\n.option {\n box-sizing: border-box;\n display: flex;\n align-items: flex-end;\n justify-content: space-between;\n gap: var(--ds-unit);\n width: 100%;\n height: var(--ds-unit);\n margin: 0;\n padding: 0 calc(4px * var(--ds-sf, 1));\n border: 0;\n border-radius: var(--ds-radius);\n background: transparent;\n color: var(--ds-text-color);\n font: inherit;\n letter-spacing: inherit;\n text-align: start;\n cursor: pointer;\n}\n.option:hover:not(:disabled) { background: var(--ds-row-fill-hover, var(--ds-fill)); }\n.option:focus-visible { outline: 2px solid var(--ds-focus-ring); outline-offset: -2px; }\n.option:disabled { color: var(--ds-title-color); cursor: default; }\n.option .ds-switch { flex: 0 0 auto; }\n\n/* A separator: one square with a 1px line through its middle. */\n.separator {\n position: relative;\n height: var(--ds-unit);\n}\n.separator::after {\n position: absolute;\n inset-inline: 0;\n top: calc(50% - 0.5px);\n height: 1px;\n background: var(--ds-border-color);\n content: \"\";\n}\n\n@media (prefers-reduced-motion: no-preference) {\n .popover:popover-open {\n animation: ds-calendar-picker-in 140ms cubic-bezier(0.22, 1, 0.36, 1);\n }\n .trigger .arrow { transition: transform 140ms ease; }\n}\n@keyframes ds-calendar-picker-in {\n from { opacity: 0; transform: translateY(calc(-4px * var(--ds-sf, 1))); }\n to { opacity: 1; transform: none; }\n}\n";
10934
10932
  //#endregion
10935
10933
  //#region DS1/3-unit/ds-calendar.ts
10936
10934
  var _Calendar;
10937
10935
  var whole$1 = (value, least, most, fallback) => typeof value === "number" && Number.isFinite(value) ? Math.max(least, Math.min(most, Math.floor(value))) : fallback;
10936
+ var TIME = /^([01]\d|2[0-3]):([0-5]\d)$/;
10937
+ /** Reads a value: `2026-10-21`, `2026-10-21T14:30`, or two of them joined by `/`. */
10938
+ function readDateValue(value) {
10939
+ const read = (part) => {
10940
+ const [day = "", time = ""] = (part ?? "").trim().split("T");
10941
+ if (!isDay(day)) return void 0;
10942
+ const clock = time.slice(0, 5);
10943
+ return {
10944
+ day,
10945
+ time: TIME.test(clock) ? clock : ""
10946
+ };
10947
+ };
10948
+ const [first, second] = (value ?? "").split("/");
10949
+ let start = read(first), end = read(second);
10950
+ if (start && end && `${end.day}T${end.time}` < `${start.day}T${start.time}`) [start, end] = [end, start];
10951
+ return {
10952
+ start,
10953
+ end
10954
+ };
10955
+ }
10956
+ /**
10957
+ * Writes a value from its ends, the times only with `time`. An end that is
10958
+ * the same moment as the start leaves the value one day.
10959
+ */
10960
+ function writeDateValue(start, end, options) {
10961
+ if (!start) return "";
10962
+ const part = (side) => options.time && side.time ? `${side.day}T${side.time}` : side.day;
10963
+ return end && part(end) !== part(start) ? `${part(start)}/${part(end)}` : part(start);
10964
+ }
10965
+ /** Reads what a person typed: a day written `YYYY-MM-DD`, or a date the browser can read ("Oct 21, 2026"). */
10966
+ function parseTypedDay(text) {
10967
+ const trimmed = text.trim();
10968
+ if (isDay(trimmed)) return trimmed;
10969
+ const moment = Date.parse(trimmed);
10970
+ return Number.isFinite(moment) ? dayOf(new Date(moment)) : null;
10971
+ }
10972
+ var openPickers = /* @__PURE__ */ new Set();
10938
10973
  /**
10939
10974
  * @element ds-calendar
10940
10975
  * @prop {string} month - The month shown, `YYYY-MM` (or a day in it). The
10941
10976
  * selected day's month, or today's, when left out; the arrows change it.
10942
- * @prop {string} value - The selected day, `YYYY-MM-DD`.
10943
- * @prop {boolean} selectable - Pressing a day selects it and fires `change`.
10977
+ * @prop {string} value - The selected day, `YYYY-MM-DD`, or a range,
10978
+ * `YYYY-MM-DD/YYYY-MM-DD`; in a picker either end may carry a time
10979
+ * (`2026-10-21T09:00`).
10980
+ * @prop {boolean} selectable - Pressing a day selects it and fires `change`:
10981
+ * the first press one day, the next the end of a range from it.
10982
+ * @prop {boolean} picker - A field one control high that opens the small
10983
+ * month below it, with the days to type, Include time and Clear.
10944
10984
  * @prop {boolean} compact - One square per day, no events written out: the
10945
10985
  * small month.
10946
10986
  * @prop {number} dayWidth - Squares per day (`day-width`): 4 by default, 1
@@ -10951,8 +10991,6 @@ var whole$1 = (value, least, most, fallback) => typeof value === "number" && Num
10951
10991
  * @prop {boolean} fixed - Always draw six weeks, so the month keeps its height.
10952
10992
  * @prop {boolean} outsideDays - Draw the days of the months before and after
10953
10993
  * in the title grey (`outside-days`); pressing one moves there.
10954
- * @prop {boolean} range - A selectable calendar chooses a first and a last
10955
- * day; `value` is then `YYYY-MM-DD/YYYY-MM-DD`.
10956
10994
  * @prop {boolean} noHeader - Leave out the title and arrows (`no-header`).
10957
10995
  * @prop {boolean} noToday - Leave out the Today button (`no-today`).
10958
10996
  * @prop {string} min - The first day a person may choose.
@@ -10960,24 +10998,42 @@ var whole$1 = (value, least, most, fallback) => typeof value === "number" && Num
10960
10998
  * @prop {string} weekStart - The week's first day (`week-start`), 0–6 from
10961
10999
  * Sunday or its English name; the language's own by default.
10962
11000
  * @prop {string} today - The day drawn as today; the reader's own by default.
11001
+ * @prop {string} label - The grid's name, or a picker's: a translation key,
11002
+ * or literal words with `literal`.
11003
+ * @prop {string} name - The form field's name.
11004
+ * @prop {string} placeholder - A picker's words before a day is chosen; the
11005
+ * shared "Choose a date" by default.
11006
+ * @prop {string} length - How a picker writes a day: `short`, `medium` (the
11007
+ * default) or `long`.
11008
+ * @prop {boolean} time - A picker chooses a time with each day (Include time).
11009
+ * @prop {boolean} required - A form needs a value; a picker leaves Clear out.
11010
+ * @prop {boolean} disabled - A picker cannot be opened.
11011
+ * @prop {boolean} open - A picker's month is open.
10963
11012
  * @prop {CalendarEvent[]} events - What happens on the days (property only).
10964
11013
  *
10965
11014
  * @fires day-press - A day was pressed or chosen with Enter or Space;
10966
11015
  * `{ day, events }`. Cancelable: cancelled, a selectable calendar keeps its value.
10967
- * @fires change - A person selected a day; `{ value, previous }`.
11016
+ * @fires change - A person selected a day or ended a range;
11017
+ * `{ value, previous, start, end }`, `end` empty while it is still to come.
11018
+ * @fires input - A picker's value changed, the same moment as `change`.
10968
11019
  * @fires event-press - An event was pressed; `{ event, day }`.
10969
11020
  * @fires navigate - A person moved to another month; `{ month }`.
11021
+ * @fires ds-calendar-toggle - A picker's month opened or closed; `{ open }`.
10970
11022
  *
10971
11023
  * @example
10972
11024
  * <ds-calendar month="2026-10" week-numbers></ds-calendar>
10973
- * <ds-calendar compact selectable value="2026-10-18"></ds-calendar>
10974
- * <ds-calendar compact selectable range value="2026-10-18/2026-10-24"></ds-calendar>
11025
+ * <ds-calendar compact selectable value="2026-10-18/2026-10-24"></ds-calendar>
11026
+ * <ds-calendar picker label="When" literal name="when" value="2026-10-21/2026-10-24"></ds-calendar>
10975
11027
  */
10976
11028
  var Calendar = class extends CalendarBase {
10977
11029
  constructor() {
10978
11030
  super();
10979
11031
  this.weeksRun = "rows";
10980
11032
  this.byDay = /* @__PURE__ */ new Map();
11033
+ this.internals = typeof this.attachInternals === "function" ? this.attachInternals() : null;
11034
+ this.initial = "";
11035
+ this.shown = false;
11036
+ this.focusMonth = false;
10981
11037
  this.onKeyDown = (event) => {
10982
11038
  const cell = event.target.closest("[data-day]");
10983
11039
  if (!cell) return;
@@ -10987,9 +11043,9 @@ var Calendar = class extends CalendarBase {
10987
11043
  this.press(day);
10988
11044
  return;
10989
11045
  }
10990
- if (event.key === "Escape" && this.open) {
11046
+ if (event.key === "Escape" && this.unfolded) {
10991
11047
  event.stopPropagation();
10992
- this.open = void 0;
11048
+ this.unfolded = void 0;
10993
11049
  return;
10994
11050
  }
10995
11051
  const target = this.keyTarget(event, day);
@@ -11005,7 +11061,7 @@ var Calendar = class extends CalendarBase {
11005
11061
  if (!cell) return;
11006
11062
  const day = cell.dataset.day;
11007
11063
  if (find("[data-more]")) {
11008
- this.open = this.open === day ? void 0 : day;
11064
+ this.unfolded = this.unfolded === day ? void 0 : day;
11009
11065
  return;
11010
11066
  }
11011
11067
  const chip = find("[data-event]");
@@ -11019,14 +11075,93 @@ var Calendar = class extends CalendarBase {
11019
11075
  if (!sameMonth(day, this.shownMonth)) this.showMonth(day, true);
11020
11076
  };
11021
11077
  this.onPointerOver = (event) => {
11022
- if (!this.range || !this.selectable) return;
11023
- const { start, end } = readRange(this.value);
11024
- if (!start || end) return;
11078
+ if (!this.endPending || !this.selectable) return;
11025
11079
  const day = event.composedPath().find((node) => node instanceof HTMLElement && node.dataset?.day)?.dataset.day;
11026
11080
  if (day !== this.hoverDay && (!day || this.allowed(day))) this.hoverDay = day;
11027
11081
  };
11028
11082
  this.onFocusOut = (event) => {
11029
- if (this.open && !(event.relatedTarget instanceof Node && this.renderRoot.contains(event.relatedTarget))) this.open = void 0;
11083
+ if (this.unfolded && !(event.relatedTarget instanceof Node && this.renderRoot.contains(event.relatedTarget))) this.unfolded = void 0;
11084
+ };
11085
+ this.place = () => {
11086
+ const panel = this.panel, trigger = this.trigger;
11087
+ if (!this.shown || !panel || !trigger) return;
11088
+ const rect = trigger.getBoundingClientRect();
11089
+ const view = this.ownerDocument.defaultView;
11090
+ const viewport = view.visualViewport;
11091
+ const left = viewport?.offsetLeft ?? 0, top = viewport?.offsetTop ?? 0;
11092
+ const width = viewport?.width ?? view.innerWidth, height = viewport?.height ?? view.innerHeight;
11093
+ const box = panel.getBoundingClientRect();
11094
+ const gap = (parseFloat(getComputedStyle(panel).paddingTop) || 9) + 1;
11095
+ const square = Math.max(rect.height, gap * 2);
11096
+ const foot = rect.bottom, head = rect.bottom - square;
11097
+ const below = top + height - foot - 2 * gap, above = head - top - 2 * gap;
11098
+ const up = below < box.height && above > below;
11099
+ const x = getComputedStyle(this).direction === "rtl" ? rect.right - box.width : rect.left;
11100
+ panel.style.left = `${Math.max(left + gap, Math.min(x, left + width - gap - box.width))}px`;
11101
+ panel.style.top = `${Math.max(top + gap, up ? head - gap - box.height : foot + gap)}px`;
11102
+ };
11103
+ this.onOutside = (event) => {
11104
+ if (!event.composedPath().includes(this)) this.open = false;
11105
+ };
11106
+ this.toggle = () => {
11107
+ if (this.disabled) return;
11108
+ this.focusMonth = false;
11109
+ this.open = !this.open;
11110
+ };
11111
+ this.onTriggerKey = (event) => {
11112
+ if (this.disabled) return;
11113
+ if (event.key === "ArrowDown" || event.key === "ArrowUp" || (event.key === "Enter" || event.key === " ") && !this.open) {
11114
+ event.preventDefault();
11115
+ this.focusMonth = true;
11116
+ if (this.open) this.focusPickerDay();
11117
+ else this.open = true;
11118
+ }
11119
+ };
11120
+ this.onPanelKey = (event) => {
11121
+ if (event.key === "Escape" && !event.defaultPrevented) {
11122
+ event.preventDefault();
11123
+ event.stopPropagation();
11124
+ this.open = false;
11125
+ }
11126
+ };
11127
+ this.onPanelFocusOut = (event) => {
11128
+ const next = event.relatedTarget;
11129
+ if (this.open && next instanceof Node && !this.contains(next) && !this.renderRoot.contains(next)) this.open = false;
11130
+ };
11131
+ this.picked = (event) => {
11132
+ event.stopPropagation();
11133
+ const { start, end } = this.ends;
11134
+ const first = {
11135
+ day: event.detail.start,
11136
+ time: start?.time || (this.time ? "09:00" : "")
11137
+ };
11138
+ if (!event.detail.end) {
11139
+ this.write(first, void 0);
11140
+ return;
11141
+ }
11142
+ const last = {
11143
+ day: event.detail.end,
11144
+ time: end?.time || (this.time ? "10:00" : "")
11145
+ };
11146
+ this.write(first, last);
11147
+ if (!this.time) this.open = false;
11148
+ };
11149
+ this.setTime = () => {
11150
+ this.time = !this.time;
11151
+ const { start, end } = this.ends;
11152
+ const timed = (side, clock) => side ? {
11153
+ ...side,
11154
+ time: this.time ? side.time || clock : ""
11155
+ } : void 0;
11156
+ this.write(timed(start, "09:00"), timed(end ?? (this.time ? start : void 0), "10:00"));
11157
+ };
11158
+ this.passPress = (event) => {
11159
+ const control = event.currentTarget.querySelector(".ds-switch");
11160
+ if (control && !event.composedPath().includes(control)) control.click();
11161
+ };
11162
+ this.clear = () => {
11163
+ this.commit("");
11164
+ this.open = false;
11030
11165
  };
11031
11166
  this.compact = false;
11032
11167
  this.weekNumbers = false;
@@ -11034,6 +11169,25 @@ var Calendar = class extends CalendarBase {
11034
11169
  this.outsideDays = false;
11035
11170
  this.noHeader = false;
11036
11171
  this.noToday = false;
11172
+ this.picker = false;
11173
+ this.time = false;
11174
+ this.required = false;
11175
+ this.disabled = false;
11176
+ this.open = false;
11177
+ }
11178
+ disconnectedCallback() {
11179
+ this.hide(false);
11180
+ super.disconnectedCallback();
11181
+ }
11182
+ firstUpdated() {
11183
+ this.initial = this.value;
11184
+ }
11185
+ /** The form was reset: back to the value it started with. */
11186
+ formResetCallback() {
11187
+ this.value = this.initial;
11188
+ }
11189
+ formDisabledCallback(disabled) {
11190
+ this.disabled = disabled;
11037
11191
  }
11038
11192
  /** The first of the month shown. */
11039
11193
  get shownMonth() {
@@ -11054,13 +11208,29 @@ var Calendar = class extends CalendarBase {
11054
11208
  this.style.setProperty("--ds-calendar-lines", String(this.eventLines));
11055
11209
  this.toggleAttribute("data-events", (this.events ?? []).length > 0);
11056
11210
  this.toggleAttribute("data-roomy", this.squaresPerDay >= 2);
11211
+ if (this.picker && changed.has("value") && this.value) {
11212
+ const { start, end } = this.ends;
11213
+ if (!start) this.value = "";
11214
+ else if (start.time || end?.time) this.time = true;
11215
+ }
11216
+ if (changed.has("disabled") && this.disabled) this.open = false;
11217
+ }
11218
+ updated(changed) {
11219
+ super.updated(changed);
11220
+ if (changed.has("value") || changed.has("required")) {
11221
+ this.internals?.setFormValue(this.value || null);
11222
+ if (this.required && !this.value) this.internals?.setValidity({ valueMissing: true }, this.pickerWords(void 0, "Choose a date"), this.trigger ?? void 0);
11223
+ else this.internals?.setValidity({});
11224
+ }
11225
+ if (changed.has("open") || changed.has("picker")) if (this.open && this.picker) this.show();
11226
+ else this.hide(changed.get("open") === true);
11057
11227
  }
11058
11228
  /** Shows another month and says so with `navigate`. */
11059
11229
  showMonth(month, announce = false) {
11060
11230
  const first = readMonth(month);
11061
11231
  if (!first || first === this.shownMonth) return;
11062
11232
  this.month = first.slice(0, 7);
11063
- this.open = void 0;
11233
+ this.unfolded = void 0;
11064
11234
  if (announce) this.announceMonth(first);
11065
11235
  }
11066
11236
  /** The month before, as the previous arrow does. */
@@ -11118,7 +11288,7 @@ var Calendar = class extends CalendarBase {
11118
11288
  const isToday = day === this.todayDay;
11119
11289
  const { selected, inside, first, last } = this.selection(day);
11120
11290
  const allowed = this.allowed(day);
11121
- const unfolded = this.open === day;
11291
+ const unfolded = this.unfolded === day;
11122
11292
  const shown = unfolded || events.length <= lines ? events : events.slice(0, Math.max(0, lines - 1));
11123
11293
  const hidden = events.length - shown.length;
11124
11294
  return b$1`<div
@@ -11160,6 +11330,7 @@ var Calendar = class extends CalendarBase {
11160
11330
  </div>`;
11161
11331
  }
11162
11332
  render() {
11333
+ if (this.picker) return this.renderPicker();
11163
11334
  const month = this.shownMonth;
11164
11335
  const first = this.firstDay;
11165
11336
  const weeks = monthWeeks(month, first, this.fixed);
@@ -11173,7 +11344,7 @@ var Calendar = class extends CalendarBase {
11173
11344
  part="grid"
11174
11345
  role="grid"
11175
11346
  aria-label=${this.nameOr(monthTitle(month))}
11176
- aria-multiselectable=${this.selectable ? String(this.range) : A}
11347
+ aria-multiselectable=${this.selectable ? "true" : A}
11177
11348
  @keydown=${this.onKeyDown}
11178
11349
  @click=${this.onClick}
11179
11350
  >
@@ -11184,355 +11355,276 @@ var Calendar = class extends CalendarBase {
11184
11355
  </div>
11185
11356
  </div>`;
11186
11357
  }
11187
- };
11188
- _Calendar = Calendar;
11189
- _Calendar.properties = {
11190
- ...CalendarBase.properties,
11191
- month: {
11192
- type: String,
11193
- reflect: true
11194
- },
11195
- compact: {
11196
- type: Boolean,
11197
- reflect: true
11198
- },
11199
- dayWidth: {
11200
- type: Number,
11201
- attribute: "day-width"
11202
- },
11203
- lines: { type: Number },
11204
- weekNumbers: {
11205
- type: Boolean,
11206
- attribute: "week-numbers",
11207
- reflect: true
11208
- },
11209
- fixed: {
11210
- type: Boolean,
11211
- reflect: true
11212
- },
11213
- outsideDays: {
11214
- type: Boolean,
11215
- attribute: "outside-days",
11216
- reflect: true
11217
- },
11218
- noHeader: {
11219
- type: Boolean,
11220
- attribute: "no-header",
11221
- reflect: true
11222
- },
11223
- noToday: {
11224
- type: Boolean,
11225
- attribute: "no-today",
11226
- reflect: true
11227
- },
11228
- open: { state: true }
11229
- };
11230
- _Calendar.styles = [
11231
- r$3(component_metrics_default),
11232
- r$3(controls_default),
11233
- r$3(ds_calendar_default)
11234
- ];
11235
- define("ds-calendar", Calendar);
11236
- //#endregion
11237
- //#region DS1/3-unit/styles/ds-calendar-large.css?inline
11238
- var ds_calendar_large_default = "/* ds-calendar-large.css\n * The grid drawn --ds-calendar-scale times (one and a half by default): a\n * cell is a square of 30px, its lines 1.5px, its words the heading size\n * times the scale (19.5px). Weeks run in columns and the days down them; the\n * frame's corners are four tenths of a cell. Today is a red disc nine tenths\n * of a cell across, the selected day an ink square of the same size. */\n\n:host {\n --ds-calendar-cell: calc(var(--ds-unit) * var(--ds-calendar-scale, 1.5));\n --ds-calendar-stroke: calc(1px * var(--ds-calendar-scale, 1.5));\n --ds-calendar-ink: var(--ds-text-color);\n --ds-calendar-event-fill: var(--ds-apple-green);\n --ds-calendar-today: var(--ds-red-for-blood);\n --ds-calendar-today-ink: var(--ds-white, #fff);\n --ds-calendar-mark: calc(var(--ds-calendar-cell) * 0.9);\n --ds-calendar-corner: calc(var(--ds-calendar-cell) * 0.4);\n --ds-calendar-rail: calc(var(--ds-calendar-cell) * 2);\n box-sizing: border-box;\n display: block;\n min-width: 0;\n color: var(--ds-text-color);\n font-family: var(--ds-typeface-regular);\n font-feature-settings: var(--ds-font-features);\n font-size: calc(var(--ds-type-size-heading) * var(--ds-calendar-scale, 1.5));\n font-weight: var(--ds-type-weight-default);\n line-height: 1;\n letter-spacing: var(--ds-type-letterspacing-default);\n}\n:host([hidden]) { display: none !important; }\n\n.calendar { min-width: 0; }\n\n/* The title and the arrows: one cell high, over the months. */\n.header {\n display: flex;\n align-items: flex-end;\n gap: calc(4px * var(--ds-sf, 1));\n height: var(--ds-calendar-cell);\n margin-inline-start: calc(var(--ds-calendar-rail) + var(--ds-unit) / 2);\n font-size: var(--ds-type-size-default);\n line-height: var(--ds-type-lineheight-default);\n}\n.title {\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n font-size: calc(var(--ds-type-size-heading) * var(--ds-calendar-scale, 1.5));\n line-height: var(--ds-calendar-cell);\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.controls { display: flex; flex: 0 0 auto; align-items: flex-end; gap: 0; }\n/* Each arrow is a chevron on its own square, as in a month. */\n.nav { flex: 0 0 auto; width: var(--ds-unit); height: var(--ds-control-height); border-radius: var(--ds-radius); color: var(--ds-title-color); }\n.nav:hover:not(:disabled) { background: var(--ds-fill); color: var(--ds-text-color); }\n:host(:dir(rtl)) .nav > ds-icon { transform: scaleX(-1); }\n\n.body {\n display: grid;\n grid-template-columns: var(--ds-calendar-rail) minmax(0, 1fr);\n column-gap: calc(var(--ds-unit) / 2);\n}\n\n/* The weekdays down the start, outside the scroll: every other one named. */\n.rail {\n display: grid;\n grid-template-rows: repeat(7, var(--ds-calendar-cell));\n padding-block-start: var(--ds-calendar-cell);\n color: var(--ds-title-color);\n font-size: var(--ds-type-size-default);\n line-height: var(--ds-type-lineheight-default);\n}\n.weekday {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n white-space: nowrap;\n}\n\n.scroller {\n overflow-x: auto;\n overflow-y: hidden;\n scrollbar-width: none;\n overscroll-behavior-x: contain;\n}\n.scroller::-webkit-scrollbar { display: none; }\n\n/* The months over their weeks, one cell high, their names on its foot. */\n.months {\n display: grid;\n grid-template-columns: repeat(var(--ds-calendar-weeks), var(--ds-calendar-cell));\n height: var(--ds-calendar-cell);\n}\n.month {\n display: flex;\n align-items: flex-end;\n min-width: 0;\n padding: 0 var(--ds-calendar-stroke) calc(3px * var(--ds-calendar-scale, 1.5));\n overflow: hidden;\n white-space: nowrap;\n}\n\n/* The frame: a line all round with rounded corners; the cells draw the lines inside. */\n.frame {\n box-sizing: border-box;\n width: max-content;\n border: var(--ds-calendar-stroke) solid var(--ds-calendar-ink);\n border-radius: var(--ds-calendar-corner);\n overflow: hidden;\n}\n.grid {\n display: grid;\n grid-template-columns: repeat(var(--ds-calendar-weeks), var(--ds-calendar-cell));\n grid-template-rows: repeat(7, var(--ds-calendar-cell));\n}\n.row { display: contents; }\n.hidden {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n}\n\n.cell {\n box-sizing: border-box;\n display: flex;\n align-items: center;\n justify-content: center;\n width: var(--ds-calendar-cell);\n height: var(--ds-calendar-cell);\n border-block-start: var(--ds-calendar-stroke) solid var(--ds-calendar-ink);\n border-inline-start: var(--ds-calendar-stroke) solid var(--ds-calendar-ink);\n background: var(--ds-calendar-cell-fill, transparent);\n font-variant-numeric: tabular-nums;\n outline: none;\n}\n.cell[data-first-row] { border-block-start-width: 0; }\n.cell[data-first-column] { border-inline-start-width: 0; }\n.cell[data-filled] { color: var(--ds-slate); }\n:host([selectable]) .cell:not([aria-disabled=\"true\"]) { cursor: pointer; }\n:host([selectable]) .cell:not([aria-disabled=\"true\"], [data-filled]):hover { background: var(--ds-row-fill-hover, var(--ds-fill)); }\n.cell:focus-visible { outline: 2px solid var(--ds-focus-ring); outline-offset: -3px; }\n.cell[aria-disabled=\"true\"] { color: var(--ds-title-color); }\n\n.date {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: var(--ds-calendar-mark);\n height: var(--ds-calendar-mark);\n letter-spacing: 0;\n}\n.cell[data-today] .date { border-radius: 999px; background: var(--ds-calendar-today); color: var(--ds-calendar-today-ink); }\n.cell[data-selected] .date { background: var(--ds-calendar-ink); color: var(--ds-paper); }\n.cell[data-inside]:not([data-filled]) { background: color-mix(in srgb, var(--ds-calendar-ink) 12%, var(--ds-paper)); }\n.cell[data-today][data-selected] .date { border-radius: 999px; box-shadow: inset 0 0 0 var(--ds-calendar-stroke) var(--ds-calendar-today); }\n\n/* On a narrow screen the weekdays take one letter and one square. */\n@media (max-width: 720px) {\n :host { --ds-calendar-rail: var(--ds-unit); }\n .rail .weekday:not(:empty) { font-size: 0; }\n .rail .weekday:not(:empty)::after { content: attr(data-narrow); font-size: var(--ds-type-size-default); }\n}\n\n/* Activity keeps the day grid, with quiet separated squares and event colours. */\n:host([activity]) .frame { border: 0; border-radius: 0; }\n:host([activity]) .cell { position: relative; border: 0; background: none; }\n:host([activity]) .cell::before {\n content: \"\";\n position: absolute;\n inset: calc(2px * var(--ds-sf, 1));\n border-radius: calc(2px * var(--ds-sf, 1));\n background: var(--ds-calendar-cell-fill, var(--ds-fill));\n}\n:host([activity]) .date { display: none; }\n:host([activity]) .cell[data-selected]::before { outline: 1px solid var(--ds-calendar-ink); }\n";
11239
- //#endregion
11240
- //#region DS1/3-unit/ds-calendar-large.ts
11241
- var _CalendarLarge;
11242
- /** The fill for a day: one colour, or bands side by side. */
11243
- function fillOf(events) {
11244
- if (!events.length) return void 0;
11245
- const colours = [...new Set(events.map((event) => event.color || "var(--ds-calendar-event-fill)"))];
11246
- if (colours.length === 1) return colours[0];
11247
- const share = 100 / colours.length;
11248
- return `linear-gradient(90deg, ${colours.map((colour, index) => `${colour} ${index * share}% ${(index + 1) * share}%`).join(", ")})`;
11249
- }
11250
- /**
11251
- * @element ds-calendar-large
11252
- * @prop {string} start - The first month drawn, `YYYY-MM`; the month before
11253
- * today's by default.
11254
- * @prop {number} months - How many months are drawn: 6 by default.
11255
- * @prop {number} scale - How many times the grid it is drawn: 1.5 by default.
11256
- * @prop {string} value - The selected day, `YYYY-MM-DD`.
11257
- * @prop {boolean} selectable - Pressing a day selects it and fires `change`.
11258
- * @prop {boolean} noHeader - Leave out the title and arrows (`no-header`).
11259
- * @prop {string} min - The first day a person may choose.
11260
- * @prop {string} max - The last day a person may choose.
11261
- * @prop {string} weekStart - The week's first day (`week-start`); the
11262
- * language's own by default.
11263
- * @prop {string} today - The day drawn as today; the reader's own by default.
11264
- * @prop {CalendarEvent[]} events - What happens on the days (property only).
11265
- *
11266
- * @fires day-press - A day was pressed or chosen with Enter or Space; `{ day, events }`.
11267
- * @fires change - A person selected a day; `{ value, previous }`.
11268
- * @fires navigate - The months drawn moved on or back; `{ month }`, the first one.
11269
- *
11270
- * @example
11271
- * <ds-calendar-large start="2026-09" months="6"></ds-calendar-large>
11272
- */
11273
- var CalendarLarge = class extends CalendarBase {
11274
- constructor() {
11275
- super();
11276
- this.weeksRun = "columns";
11277
- this.byDay = /* @__PURE__ */ new Map();
11278
- this.placed = false;
11279
- this.frame = 0;
11280
- this.onScroll = () => {
11281
- cancelAnimationFrame(this.frame);
11282
- this.frame = requestAnimationFrame(() => {
11283
- const scroller = this.scroller;
11284
- const cell = scroller?.querySelector(".cell")?.getBoundingClientRect().width;
11285
- if (!scroller || !cell) return;
11286
- const column = Math.min(this.weekStarts.length - 1, Math.max(0, Math.round(Math.abs(scroller.scrollLeft) / cell)));
11287
- this.visible = startOfMonth(addDays(this.weekStarts[column], 3));
11288
- });
11289
- };
11290
- this.onKeyDown = (event) => {
11291
- const cell = event.target.closest("[data-day]");
11292
- if (!cell) return;
11293
- const day = cell.dataset.day;
11294
- if (event.key === "Enter" || event.key === " ") {
11295
- event.preventDefault();
11296
- this.press(day);
11297
- return;
11298
- }
11299
- const target = this.keyTarget(event, day);
11300
- if (!target) return;
11301
- event.preventDefault();
11302
- if (target < this.weekStarts[0] || target > this.lastDay) {
11303
- const by = target < this.weekStarts[0] ? -1 : 1;
11304
- const first = addMonths(this.firstMonth, by);
11305
- this.start = first.slice(0, 7);
11306
- this.announceMonth(first);
11307
- }
11308
- this.focusCell(target);
11309
- };
11310
- this.onClick = (event) => {
11311
- const cell = event.composedPath().find((node) => node instanceof HTMLElement && node.dataset?.day);
11312
- if (!cell) return;
11313
- const day = cell.dataset.day;
11314
- this.focusDay = day;
11315
- this.press(day);
11316
- };
11317
- this.noHeader = false;
11318
- this.activity = false;
11319
- }
11320
- /** The first of the first month drawn. */
11321
- get firstMonth() {
11322
- return readMonth(this.start) ?? addMonths(startOfMonth(this.todayDay), -1);
11323
- }
11324
- get monthCount() {
11325
- const count = this.months;
11326
- return typeof count === "number" && Number.isFinite(count) ? Math.max(1, Math.min(36, Math.floor(count))) : 6;
11358
+ pickerWords(value, fallback) {
11359
+ if (value && this.literal) return value;
11360
+ return getText(value || fallback) || value || fallback;
11327
11361
  }
11328
- /** The first day of each week drawn, from the week holding the first month's first day. */
11329
- get weekStarts() {
11330
- const first = this.firstMonth;
11331
- const from = addDays(first, -((weekdayOf(first) - this.firstDay + 7) % 7));
11332
- const last = addDays(addMonths(first, this.monthCount), -1);
11333
- const weeks = [];
11334
- for (let day = from; day <= last; day = addDays(day, 7)) weeks.push(day);
11335
- return weeks;
11362
+ get trigger() {
11363
+ return this.renderRoot.querySelector(".trigger");
11336
11364
  }
11337
- get lastDay() {
11338
- return addDays(this.weekStarts.at(-1), 6);
11365
+ get panel() {
11366
+ return this.renderRoot.querySelector(".popover");
11339
11367
  }
11340
- eventsOn(day) {
11341
- return this.byDay.get(day) ?? [];
11368
+ /** The small month inside the picker. */
11369
+ get smallMonth() {
11370
+ return this.renderRoot.querySelector("ds-calendar");
11342
11371
  }
11343
- willUpdate(changed) {
11344
- super.willUpdate(changed);
11345
- const scale = typeof this.scale === "number" && Number.isFinite(this.scale) && this.scale > 0 ? this.scale : this.activity ? 1 : 1.5;
11346
- this.style.setProperty("--ds-calendar-scale", String(scale));
11372
+ get ends() {
11373
+ return readDateValue(this.value);
11347
11374
  }
11348
- firstUpdated() {
11349
- const today = this.todayDay;
11350
- this.scrollToDay(today >= this.weekStarts[0] && today <= this.lastDay ? startOfMonth(today) : this.firstMonth, false);
11351
- this.placed = true;
11375
+ /** Opens a picker's month. */
11376
+ show() {
11377
+ if (this.shown || !this.picker || this.disabled || !this.isConnected) return;
11378
+ const panel = this.panel;
11379
+ if (!panel) return;
11380
+ for (const other of openPickers) if (other !== this) other.open = false;
11381
+ openPickers.add(this);
11382
+ this.shown = true;
11383
+ this.open = true;
11384
+ const month = this.smallMonth;
11385
+ if (month) {
11386
+ month.month = (this.ends.start?.day || this.today || this.todayDay).slice(0, 7);
11387
+ month.endPending = false;
11388
+ }
11389
+ panel.showPopover?.();
11390
+ this.place();
11391
+ if (month) month.updateComplete.then(this.place);
11392
+ this.ownerDocument.addEventListener("pointerdown", this.onOutside, true);
11393
+ this.ownerDocument.addEventListener("scroll", this.place, true);
11394
+ window.addEventListener("resize", this.place);
11395
+ this.dispatchEvent(new CustomEvent("ds-calendar-toggle", {
11396
+ bubbles: true,
11397
+ composed: true,
11398
+ detail: { open: true }
11399
+ }));
11400
+ if (this.focusMonth) this.focusPickerDay();
11352
11401
  }
11353
- disconnectedCallback() {
11354
- cancelAnimationFrame(this.frame);
11355
- super.disconnectedCallback();
11402
+ /** Closes a picker's month, giving focus back to the field when it was inside. */
11403
+ hide(restore = true) {
11404
+ if (!this.shown) return;
11405
+ this.shown = false;
11406
+ openPickers.delete(this);
11407
+ const panel = this.panel;
11408
+ const within = panel?.matches(":focus-within");
11409
+ if (panel?.matches(":popover-open")) panel.hidePopover?.();
11410
+ this.ownerDocument.removeEventListener("pointerdown", this.onOutside, true);
11411
+ this.ownerDocument.removeEventListener("scroll", this.place, true);
11412
+ window.removeEventListener("resize", this.place);
11413
+ this.open = false;
11414
+ if (restore && within) this.trigger?.focus({ preventScroll: true });
11415
+ this.dispatchEvent(new CustomEvent("ds-calendar-toggle", {
11416
+ bubbles: true,
11417
+ composed: true,
11418
+ detail: { open: false }
11419
+ }));
11356
11420
  }
11357
- get scroller() {
11358
- return this.renderRoot.querySelector(".scroller");
11421
+ async focusPickerDay() {
11422
+ const month = this.smallMonth;
11423
+ if (!month) return;
11424
+ await month.updateComplete;
11425
+ month.renderRoot.querySelector("[data-day][tabindex=\"0\"]")?.focus({ preventScroll: true });
11359
11426
  }
11360
- columnOf(day) {
11361
- return this.weekStarts.findIndex((start) => day >= start && day <= addDays(start, 6));
11427
+ /** Takes a new value and says so. */
11428
+ commit(value) {
11429
+ const previous = this.value || "";
11430
+ if (value === previous) return;
11431
+ this.value = value;
11432
+ const { start, end } = readDateValue(value);
11433
+ const detail = {
11434
+ value,
11435
+ previous,
11436
+ start: start?.day ?? "",
11437
+ end: end?.day ?? start?.day ?? ""
11438
+ };
11439
+ this.dispatchEvent(new CustomEvent("input", {
11440
+ bubbles: true,
11441
+ composed: true,
11442
+ detail
11443
+ }));
11444
+ this.dispatchEvent(new CustomEvent("change", {
11445
+ bubbles: true,
11446
+ composed: true,
11447
+ detail
11448
+ }));
11362
11449
  }
11363
- /** Scrolls the week holding `day` to the start of the view. */
11364
- scrollToDay(day, smooth = true) {
11365
- const scroller = this.scroller;
11366
- const column = this.columnOf(day);
11367
- if (!scroller || column < 0) return;
11368
- const cell = scroller.querySelector(".cell")?.getBoundingClientRect().width ?? 30;
11369
- const reduce = matchMedia?.("(prefers-reduced-motion: reduce)").matches;
11370
- const rtl = getComputedStyle(this).direction === "rtl";
11371
- scroller.scrollTo({
11372
- left: (rtl ? -1 : 1) * column * cell,
11373
- behavior: smooth && !reduce ? "smooth" : "auto"
11374
- });
11375
- this.visible = startOfMonth(day);
11450
+ write(start, end) {
11451
+ this.commit(writeDateValue(start, end, { time: this.time }));
11376
11452
  }
11377
- /** Moves the view a month back or on; past the months drawn, the months move with it. */
11378
- step(by) {
11379
- const target = addMonths(this.visible ?? this.firstMonth, by);
11380
- const first = this.firstMonth;
11381
- const end = addMonths(first, this.monthCount);
11382
- if (target < first || target >= end) {
11383
- this.start = addMonths(first, by).slice(0, 7);
11384
- this.announceMonth(addMonths(first, by));
11385
- this.updateComplete.then(() => this.scrollToDay(target, false));
11453
+ /** A day typed in a field, read when the person leaves it or presses Enter. */
11454
+ typed(side, input) {
11455
+ const { start, end } = this.ends;
11456
+ const text = input.value.trim();
11457
+ if (!text) {
11458
+ if (side === "start") this.commit("");
11459
+ else this.write(start, void 0);
11386
11460
  return;
11387
11461
  }
11388
- this.scrollToDay(target);
11462
+ const day = parseTypedDay(text);
11463
+ if (!day) {
11464
+ input.value = this.fieldText(side === "start" ? start : end);
11465
+ return;
11466
+ }
11467
+ const next = {
11468
+ day,
11469
+ time: (side === "start" ? start : end)?.time || (this.time ? side === "start" ? "09:00" : "10:00" : "")
11470
+ };
11471
+ if (side === "start") this.write(next, end);
11472
+ else this.write(start ?? next, next);
11473
+ const month = this.smallMonth;
11474
+ if (month) month.month = day.slice(0, 7);
11389
11475
  }
11390
- /** The month before, as the previous arrow does. */
11391
- previousMonth() {
11392
- this.step(-1);
11476
+ timed(side, input) {
11477
+ const { start, end } = this.ends;
11478
+ const clock = TIME.test(input.value) ? input.value : "";
11479
+ if (side === "start" && start) this.write({
11480
+ ...start,
11481
+ time: clock
11482
+ }, end);
11483
+ if (side === "end" && start) this.write(start, {
11484
+ day: (end ?? start).day,
11485
+ time: clock
11486
+ });
11393
11487
  }
11394
- /** The month after, as the next arrow does. */
11395
- nextMonth() {
11396
- this.step(1);
11488
+ moment(side) {
11489
+ const [year, month, date] = side.day.split("-").map(Number);
11490
+ const [hours, minutes] = (side.time || "12:00").split(":").map(Number);
11491
+ return Date.UTC(year, month - 1, date, hours, minutes);
11397
11492
  }
11398
- /** Brings today into view, moving the months drawn when it is outside them. */
11399
- showToday() {
11400
- const today = this.todayDay;
11401
- if (today < this.weekStarts[0] || today > this.lastDay) {
11402
- const first = addMonths(startOfMonth(today), -1);
11403
- this.start = first.slice(0, 7);
11404
- this.announceMonth(first);
11405
- this.updateComplete.then(() => this.scrollToDay(startOfMonth(today), false));
11406
- } else this.scrollToDay(startOfMonth(today));
11407
- this.focusDay = today;
11493
+ formatter(withTime) {
11494
+ return dateFormatter({
11495
+ dateStyle: this.length ?? "medium",
11496
+ ...withTime ? { timeStyle: "short" } : {},
11497
+ timeZone: "UTC"
11498
+ });
11408
11499
  }
11409
- get tabDay() {
11410
- const first = this.weekStarts[0], last = this.lastDay;
11411
- for (const day of [
11412
- this.focusDay,
11413
- this.chosen.start,
11414
- this.todayDay
11415
- ]) if (day && day >= first && day <= last) return day;
11416
- return this.firstMonth;
11500
+ fieldText(side) {
11501
+ return side ? this.formatter(false).format(this.moment(side)) : "";
11417
11502
  }
11418
- renderHeader() {
11419
- if (this.noHeader) return A;
11420
- const arrow = (which) => b$1`<button
11421
- class="ds-button ds-button--icon nav"
11422
- part=${which}
11423
- type="button"
11424
- aria-label=${this.words(which === "previous" ? "Previous month" : "Next month")}
11425
- @click=${() => which === "previous" ? this.previousMonth() : this.nextMonth()}
11426
- ><ds-icon type=${which === "previous" ? "chevron-left" : "chevron-right"} size="large" aria-hidden="true"></ds-icon></button>`;
11427
- return b$1`<div class="header" part="header">
11428
- <span class="title" aria-live="polite">${monthTitle(this.visible ?? this.firstMonth)}</span>
11429
- <span class="controls">
11430
- ${arrow("previous")}
11431
- <button class="ds-button ds-button--secondary" part="today" type="button" @click=${() => this.showToday()}>${this.words("Today")}</button>
11432
- ${arrow("next")}
11433
- </span>
11434
- </div>`;
11503
+ /** The face's words: one day, or a range written as the language writes one ("Oct 21 – 24, 2026"). */
11504
+ faceText() {
11505
+ const { start, end } = this.ends;
11506
+ if (!start) return this.pickerWords(this.placeholder, "Choose a date");
11507
+ const formatter = this.formatter(Boolean(start.time || end?.time));
11508
+ if (!end) return formatter.format(this.moment(start));
11509
+ return formatter.formatRange ? formatter.formatRange(this.moment(start), this.moment(end)) : `${formatter.format(this.moment(start))} – ${formatter.format(this.moment(end))}`;
11435
11510
  }
11436
- render() {
11437
- const weeks = this.weekStarts;
11438
- const first = weeks[0], last = this.lastDay;
11439
- this.byDay = eventsByDay(this.events ?? [], first, last);
11440
- const short = weekdayNames(this.firstDay, "short");
11441
- const narrow = weekdayNames(this.firstDay, "narrow");
11442
- const long = weekdayNames(this.firstDay, "long");
11443
- const today = this.todayDay;
11444
- const tab = this.tabDay;
11445
- const labels = [];
11446
- for (let month = this.firstMonth, index = 0; index < this.monthCount; index++, month = addMonths(month, 1)) {
11447
- const column = this.columnOf(month);
11448
- const next = this.columnOf(addMonths(month, 1));
11449
- labels.push({
11450
- column,
11451
- span: Math.max(1, (next < 0 ? weeks.length : next) - column),
11452
- day: month
11453
- });
11454
- }
11455
- const ends = /* @__PURE__ */ new Set();
11456
- for (const event of this.events ?? []) {
11457
- ends.add(event.start);
11458
- if (event.end) ends.add(event.end);
11459
- }
11460
- return b$1`<div class="calendar" part="calendar" style=${`--ds-calendar-weeks: ${weeks.length}`}>
11461
- ${this.renderHeader()}
11462
- <div class="body">
11463
- <div class="rail" part="weekdays" aria-hidden="true">
11464
- ${short.map((name, row) => b$1`<span class="weekday" data-narrow=${narrow[row]}>${row % 2 === 1 ? name : ""}</span>`)}
11511
+ renderEnd(side, value, name) {
11512
+ const label = side === "start" ? name : `${name}, ${this.pickerWords(void 0, "End date")}`;
11513
+ return b$1`<div class="field-row" part="field">
11514
+ <input
11515
+ class="ds-input field"
11516
+ type="text"
11517
+ autocomplete="off"
11518
+ aria-label=${label}
11519
+ placeholder=${side === "start" ? this.pickerWords(this.placeholder, "Choose a date") : this.pickerWords(void 0, "End date")}
11520
+ .value=${this.fieldText(value)}
11521
+ @change=${(event) => this.typed(side, event.target)}
11522
+ @keydown=${(event) => {
11523
+ if (event.key === "Enter") {
11524
+ event.preventDefault();
11525
+ this.typed(side, event.target);
11526
+ }
11527
+ }}
11528
+ />
11529
+ </div>
11530
+ ${this.time ? b$1`<div class="field-row" part="field">
11531
+ <input
11532
+ class="ds-input clock"
11533
+ type="time"
11534
+ aria-label=${`${label}, ${this.pickerWords(void 0, "Time")}`}
11535
+ .value=${value?.time ?? ""}
11536
+ ?disabled=${!this.ends.start}
11537
+ @change=${(event) => this.timed(side, event.target)}
11538
+ />
11539
+ </div>` : A}`;
11540
+ }
11541
+ renderPicker() {
11542
+ const { start, end } = this.ends;
11543
+ const text = this.faceText();
11544
+ const name = this.label ? this.pickerWords(this.label, this.label) : this.pickerWords(void 0, "Date");
11545
+ const days = start ? end ? `${start.day}/${end.day}` : start.day : "";
11546
+ return b$1`<button
11547
+ class="ds-select trigger"
11548
+ part="trigger"
11549
+ type="button"
11550
+ aria-haspopup="dialog"
11551
+ aria-expanded=${String(this.open)}
11552
+ aria-label=${start ? `${name}, ${text}` : name}
11553
+ ?disabled=${this.disabled}
11554
+ ?data-empty=${!start}
11555
+ @click=${this.toggle}
11556
+ @keydown=${this.onTriggerKey}
11557
+ >
11558
+ <ds-icon class="icon" type="calendar" aria-hidden="true"></ds-icon>
11559
+ <span class="text">${text}</span>
11560
+ <ds-icon class="arrow" type="down" aria-hidden="true"></ds-icon>
11561
+ </button>
11562
+ <div
11563
+ class="popover"
11564
+ part="popover"
11565
+ popover="manual"
11566
+ role="dialog"
11567
+ aria-label=${name}
11568
+ @keydown=${this.onPanelKey}
11569
+ @focusout=${this.onPanelFocusOut}
11570
+ >
11571
+ <div class="fields">
11572
+ ${this.renderEnd("start", start, name)}
11573
+ ${this.renderEnd("end", start ? end ?? start : void 0, name)}
11465
11574
  </div>
11466
- <div class="scroller" part="scroller" @scroll=${this.onScroll}>
11467
- <div class="months" aria-hidden="true">
11468
- ${labels.map(({ column, span, day }) => b$1`<span class="month" style=${`grid-column: ${column + 1} / span ${span}`}>${day.slice(5, 7) === "01" || day === this.firstMonth ? monthTitle(day, "short") : monthName(day, "short")}</span>`)}
11469
- </div>
11470
- <div class="frame" part="frame">
11471
- <div
11472
- class="grid"
11473
- part="grid"
11474
- role="grid"
11475
- aria-label=${this.nameOr(`${monthTitle(this.firstMonth)} – ${monthTitle(addMonths(this.firstMonth, this.monthCount - 1))}`)}
11476
- @keydown=${this.onKeyDown}
11477
- @click=${this.onClick}
11478
- >
11479
- ${short.map((_, row) => b$1`<div role="row" class="row">
11480
- <span role="rowheader" class="hidden">${long[row]}</span>
11481
- ${weeks.map((start, column) => {
11482
- const day = addDays(start, row);
11483
- const events = this.eventsOn(day);
11484
- const isToday = day === today;
11485
- const { selected, inside } = this.selection(day);
11486
- const written = isToday || selected || events.length > 0 && ends.has(day);
11487
- const fill = fillOf(events);
11488
- return b$1`<div
11489
- role="gridcell"
11490
- class="cell"
11491
- part=${[
11492
- "cell",
11493
- isToday ? "today" : "",
11494
- selected ? "selected" : "",
11495
- events.length ? "filled" : ""
11496
- ].filter(Boolean).join(" ")}
11497
- data-day=${day}
11498
- tabindex=${day === tab ? 0 : -1}
11499
- aria-label=${this.cellLabel(day, events)}
11500
- title=${this.activity ? this.cellLabel(day, events) : A}
11501
- aria-selected=${this.selectable ? String(selected || inside) : A}
11502
- aria-current=${isToday ? "date" : A}
11503
- aria-disabled=${this.allowed(day) ? A : "true"}
11504
- ?data-today=${isToday}
11505
- ?data-selected=${selected}
11506
- ?data-inside=${inside}
11507
- ?data-filled=${events.length > 0}
11508
- ?data-first-row=${row === 0}
11509
- ?data-first-column=${column === 0}
11510
- style=${`grid-row: ${row + 1}; grid-column: ${column + 1};${fill ? ` --ds-calendar-cell-fill: ${fill};` : ""}`}
11511
- >${written ? b$1`<span class="date" aria-hidden="true">${dateNumber(day)}</span>` : A}</div>`;
11512
- })}
11513
- </div>`)}
11514
- </div>
11575
+ <ds-calendar
11576
+ compact
11577
+ selectable
11578
+ fixed
11579
+ outside-days
11580
+ .value=${days}
11581
+ .min=${this.min}
11582
+ .max=${this.max}
11583
+ .weekStart=${this.weekStart}
11584
+ .today=${this.today}
11585
+ @change=${this.picked}
11586
+ ></ds-calendar>
11587
+ <div class="options" part="options">
11588
+ <div class="separator" aria-hidden="true"></div>
11589
+ <div class="option" @click=${this.passPress}>
11590
+ <span>${this.pickerWords(void 0, "Include time")}</span>
11591
+ <button class="ds-switch" type="button" role="switch" aria-label=${this.pickerWords(void 0, "Include time")} aria-checked=${String(this.time)} @click=${this.setTime}></button>
11515
11592
  </div>
11593
+ ${this.required ? A : b$1`<div class="separator" aria-hidden="true"></div>
11594
+ <button class="option clear" part="clear" type="button" ?disabled=${!start} @click=${this.clear}>${this.pickerWords(void 0, "Clear")}</button>`}
11516
11595
  </div>
11517
- </div>
11518
- </div>`;
11519
- }
11520
- updated(changed) {
11521
- super.updated(changed);
11522
- if (this.placed && changed.has("start") && this.visible && !this.focusDay) this.scrollToDay(this.visible, false);
11596
+ </div>`;
11523
11597
  }
11524
11598
  };
11525
- _CalendarLarge = CalendarLarge;
11526
- _CalendarLarge.properties = {
11599
+ _Calendar = Calendar;
11600
+ _Calendar.formAssociated = true;
11601
+ _Calendar.properties = {
11527
11602
  ...CalendarBase.properties,
11528
- start: {
11603
+ month: {
11529
11604
  type: String,
11530
11605
  reflect: true
11531
11606
  },
11532
- months: { type: Number },
11533
- scale: { type: Number },
11534
- activity: {
11607
+ compact: {
11608
+ type: Boolean,
11609
+ reflect: true
11610
+ },
11611
+ dayWidth: {
11612
+ type: Number,
11613
+ attribute: "day-width"
11614
+ },
11615
+ lines: { type: Number },
11616
+ weekNumbers: {
11617
+ type: Boolean,
11618
+ attribute: "week-numbers",
11619
+ reflect: true
11620
+ },
11621
+ fixed: {
11622
+ type: Boolean,
11623
+ reflect: true
11624
+ },
11625
+ outsideDays: {
11535
11626
  type: Boolean,
11627
+ attribute: "outside-days",
11536
11628
  reflect: true
11537
11629
  },
11538
11630
  noHeader: {
@@ -11540,530 +11632,578 @@ _CalendarLarge.properties = {
11540
11632
  attribute: "no-header",
11541
11633
  reflect: true
11542
11634
  },
11543
- visible: { state: true }
11635
+ noToday: {
11636
+ type: Boolean,
11637
+ attribute: "no-today",
11638
+ reflect: true
11639
+ },
11640
+ picker: {
11641
+ type: Boolean,
11642
+ reflect: true
11643
+ },
11644
+ name: {
11645
+ type: String,
11646
+ reflect: true
11647
+ },
11648
+ placeholder: { type: String },
11649
+ length: { type: String },
11650
+ time: {
11651
+ type: Boolean,
11652
+ reflect: true
11653
+ },
11654
+ required: {
11655
+ type: Boolean,
11656
+ reflect: true
11657
+ },
11658
+ disabled: {
11659
+ type: Boolean,
11660
+ reflect: true
11661
+ },
11662
+ open: {
11663
+ type: Boolean,
11664
+ reflect: true
11665
+ },
11666
+ unfolded: { state: true }
11544
11667
  };
11545
- _CalendarLarge.styles = [
11668
+ _Calendar.styles = [
11546
11669
  r$3(component_metrics_default),
11547
11670
  r$3(controls_default),
11548
- r$3(ds_calendar_large_default)
11671
+ r$3(ds_calendar_default)
11549
11672
  ];
11550
- define("ds-calendar-large", CalendarLarge);
11551
- //#endregion
11552
- //#region DS1/1-root/carousel.css?inline
11553
- var carousel_default = "/* Shape is a surface preference. Keep these base lengths unscaled: each\n * component resolves them with its own --ds-sf in component-metrics.css. */\n[data-ds-shape=\"square\"],\n:host([data-ds-shape=\"square\"]) {\n --ds-corner-size: 0px;\n --ds-corner-shape: round;\n --ds-button-inset: 0.5px;\n}\n\n[data-ds-shape=\"rounded\"],\n:host([data-ds-shape=\"rounded\"]) {\n --ds-corner-size: 4px;\n --ds-corner-shape: round;\n --ds-button-inset: 2.5px;\n}\n\n[data-ds-shape=\"circular\"],\n:host([data-ds-shape=\"circular\"]) {\n --ds-corner-size: 999px;\n --ds-corner-shape: round;\n --ds-button-inset: 5px;\n}\n\n/* A squircle needs twice an arc's radius for the same apparent corner.\n * Circular controls keep true semicircles even in a squircle browser. */\n@supports (corner-shape: squircle) {\n [data-ds-shape=\"rounded\"],\n :host([data-ds-shape=\"rounded\"]) {\n --ds-corner-size: 8px;\n --ds-corner-shape: squircle;\n }\n}\n\n/* Resolve dimensions where a component is drawn. Inherited calc() values have\n * already used their ancestor's scale. Base tokens stay unscaled and inherit;\n * resolved tokens can still be overridden directly on a component.\n * data-ds-surface gives ordinary layout elements the same scaled measures.\n */\n:root,\n:host,\n:where([data-ds-surface]) {\n --ds-unit: calc(var(--ds-1, 20px) * var(--ds-sf, 1));\n --ds-control-height: calc(var(--ds-control-size, var(--ds-08, 16px)) * var(--ds-sf, 1));\n --ds-row-height: calc(var(--ds-row-size, var(--ds-1, 20px)) * var(--ds-sf, 1));\n --ds-type-size-default: calc(var(--ds-text-size, 14px) * var(--ds-sf, 1));\n --ds-type-size-small: calc(var(--ds-small-text-size, 10px) * var(--ds-sf, 1));\n --ds-type-size-heading: calc(var(--ds-heading-text-size, 13px) * var(--ds-sf, 1));\n --ds-type-size-display: calc(var(--ds-display-text-size, 20px) * var(--ds-sf, 1));\n --ds-type-lineheight-default: calc(var(--ds-text-leading, 16px) * var(--ds-sf, 1));\n --ds-type-letterspacing-default: calc(var(--ds-text-tracking, -0.3px) * var(--ds-sf, 1));\n --ds-radius: calc(var(--ds-corner-size, 4px) * var(--ds-sf, 1));\n --ds-radius-card: calc(var(--ds-card-corner-size, 4px) * var(--ds-sf, 1));\n}\n\n/*\n * A carousel, as plain classes on native elements.\n *\n * A carousel is a row of slides that snaps sideways, one slide to a stop,\n * with a row of controls one square high under it: optional previous and\n * next buttons and a dot for each slide. Slides are the track's width, or\n * whole squares wide, a whole number of squares apart (one by default). The\n * controls stand one square below the track, on the grid from its start.\n * The `ds-carousel` element draws the same parts, and\n * `0-face/carousel.ts` gives the native markup its behaviour.\n *\n * ds-carousel the region: track, controls, status;\n * `data-slides=\"auto\"` for slides of their\n * own width, `data-bleed` `end` or `both`,\n * `data-snap=\"none\"` to slide freely\n * ds-carousel__track the scroller; every child is a slide\n * ds-carousel__controls the row of buttons and dots\n * ds-carousel__previous, __next, icon buttons, on `.ds-button.ds-button--icon`\n * __pause\n * ds-carousel__status the polite announcement, kept out of sight\n *\n * The dots are `ds-progress--dots` from progress.css.\n *\n * A strip of slides of their own width can run on past its box to the\n * window's end edge, or to both edges, while its first slide keeps to the\n * box's start, so it lines up with the page's column; `--ds-carousel-before`\n * and `--ds-carousel-after` are the room to the edges, which the script\n * measures.\n *\n * Tokens: `--ds-carousel-slide` (a slide's width, the track's by default)\n * and `--ds-carousel-gap` (between slides, one square by default).\n */\n\n.ds-carousel {\n box-sizing: border-box;\n display: grid;\n grid-template-columns: minmax(0, 1fr);\n row-gap: var(--ds-unit);\n position: relative;\n min-width: 0;\n margin: 0;\n}\n\n.ds-carousel__track {\n box-sizing: border-box;\n display: grid;\n grid-auto-flow: column;\n grid-auto-columns: var(--ds-carousel-slide, 100%);\n column-gap: var(--ds-carousel-gap, var(--ds-unit));\n min-width: 0;\n margin: 0;\n padding: 0;\n list-style: none;\n overflow-x: auto;\n overflow-y: hidden;\n overscroll-behavior-x: contain;\n scroll-snap-type: x mandatory;\n scrollbar-width: none;\n}\n\n.ds-carousel__track::-webkit-scrollbar {\n display: none;\n}\n\n/* Slides of their own width, several in view. */\n.ds-carousel[data-slides=\"auto\"] > .ds-carousel__track {\n grid-auto-columns: max-content;\n}\n\n.ds-carousel[data-snap=\"none\"] > .ds-carousel__track {\n scroll-snap-type: none;\n}\n\n/* To the window's end edge: the track reaches out over the room after the\n box and keeps a gap at its end, so the last slide stops short of the edge. */\n.ds-carousel[data-bleed=\"end\"] > .ds-carousel__track {\n margin-inline-end: calc(-1 * var(--ds-carousel-after, 0px));\n padding-inline-end: var(--ds-carousel-gap, var(--ds-unit));\n}\n\n/* To both edges: the track reaches out both ways, and the first slide, and\n each one it stops at, still stands on the box's start. */\n.ds-carousel[data-bleed=\"both\"] > .ds-carousel__track {\n margin-inline: calc(-1 * var(--ds-carousel-before, 0px)) calc(-1 * var(--ds-carousel-after, 0px));\n padding-inline: var(--ds-carousel-before, 0px) var(--ds-carousel-gap, var(--ds-unit));\n scroll-padding-inline-start: var(--ds-carousel-before, 0px);\n}\n\n.ds-carousel__track:focus-visible {\n outline: 2px solid var(--ds-focus-ring);\n outline-offset: 1px;\n}\n\n.ds-carousel__track > * {\n box-sizing: border-box;\n min-width: 0;\n margin: 0;\n scroll-snap-align: start;\n}\n\n/* One slide to a stop; a strip of several in view may pass a few. */\n.ds-carousel:not([data-slides=\"auto\"]) > .ds-carousel__track > * {\n scroll-snap-stop: always;\n}\n\n.ds-carousel__controls {\n display: flex;\n align-items: center;\n height: var(--ds-row-height);\n min-width: 0;\n}\n\n/* The buttons take a one-square cell each, as the dots do. Two classes, so\n the button's own reset of its margin does not undo it. */\n.ds-carousel .ds-carousel__previous,\n.ds-carousel .ds-carousel__next,\n.ds-carousel .ds-carousel__pause {\n flex: none;\n margin: calc((var(--ds-unit) - var(--ds-control-height)) / 2);\n}\n\n/* Previous points back along the reading direction. */\n.ds-carousel:dir(rtl) :is(.ds-carousel__previous, .ds-carousel__next) > * {\n transform: scaleX(-1);\n}\n\n.ds-carousel__status {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n}\n";
11673
+ define("ds-calendar", Calendar);
11554
11674
  //#endregion
11555
- //#region DS1/3-unit/styles/ds-carousel.css?inline
11556
- var ds_carousel_default = "/* ds-carousel.css */\n/* The element only places the parts; 1-root/carousel.css draws them and\n controls.css the buttons. The slides are the element's children, so the\n track's rules for its children are said again for slotted ones. */\n\n:host {\n display: block;\n min-width: 0;\n}\n\n:host([hidden]) {\n display: none !important;\n}\n\n::slotted(*) {\n box-sizing: border-box;\n flex: none;\n min-width: 0;\n margin: 0;\n scroll-snap-align: start;\n}\n\n/* One slide to a stop; a strip of slides of their own width may pass a few. */\n:host(:not([slide-width=\"auto\"])) ::slotted(*) {\n scroll-snap-stop: always;\n}\n\n:host(:dir(rtl)) :is(.ds-carousel__previous, .ds-carousel__next) > ds-icon {\n transform: scaleX(-1);\n}\n";
11675
+ //#region DS1/3-unit/styles/ds-calendar-large.css?inline
11676
+ var ds_calendar_large_default = "/* ds-calendar-large.css\n * The grid drawn --ds-calendar-scale times (one and a half by default): a\n * cell is a square of 30px, its lines 1.5px, its words the heading size\n * times the scale (19.5px). Weeks run in columns and the days down them; the\n * frame's corners are four tenths of a cell. Today is a red disc nine tenths\n * of a cell across, the selected day an ink square of the same size. */\n\n:host {\n --ds-calendar-cell: calc(var(--ds-unit) * var(--ds-calendar-scale, 1.5));\n --ds-calendar-stroke: calc(1px * var(--ds-calendar-scale, 1.5));\n --ds-calendar-ink: var(--ds-text-color);\n --ds-calendar-event-fill: var(--ds-apple-green);\n --ds-calendar-today: var(--ds-red-for-blood);\n --ds-calendar-today-ink: var(--ds-white, #fff);\n --ds-calendar-mark: calc(var(--ds-calendar-cell) * 0.9);\n --ds-calendar-corner: calc(var(--ds-calendar-cell) * 0.4);\n --ds-calendar-rail: calc(var(--ds-calendar-cell) * 2);\n box-sizing: border-box;\n display: block;\n min-width: 0;\n color: var(--ds-text-color);\n font-family: var(--ds-typeface-regular);\n font-feature-settings: var(--ds-font-features);\n font-size: calc(var(--ds-type-size-heading) * var(--ds-calendar-scale, 1.5));\n font-weight: var(--ds-type-weight-default);\n line-height: 1;\n letter-spacing: var(--ds-type-letterspacing-default);\n}\n:host([hidden]) { display: none !important; }\n\n.calendar { min-width: 0; }\n\n/* The title and the arrows: one cell high, over the months. */\n.header {\n display: flex;\n align-items: flex-end;\n gap: calc(4px * var(--ds-sf, 1));\n height: var(--ds-calendar-cell);\n margin-inline-start: calc(var(--ds-calendar-rail) + var(--ds-unit) / 2);\n font-size: var(--ds-type-size-default);\n line-height: var(--ds-type-lineheight-default);\n}\n.title {\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n font-size: calc(var(--ds-type-size-heading) * var(--ds-calendar-scale, 1.5));\n line-height: var(--ds-calendar-cell);\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.controls { display: flex; flex: 0 0 auto; align-items: flex-end; gap: 0; }\n/* Each arrow is a chevron on its own square, as in a month. */\n.nav { flex: 0 0 auto; width: var(--ds-unit); height: var(--ds-control-height); border-radius: var(--ds-radius); color: var(--ds-title-color); }\n.nav:hover:not(:disabled) { background: var(--ds-fill); color: var(--ds-text-color); }\n:host(:dir(rtl)) .nav > ds-icon { transform: scaleX(-1); }\n\n.body {\n display: grid;\n grid-template-columns: var(--ds-calendar-rail) minmax(0, 1fr);\n column-gap: calc(var(--ds-unit) / 2);\n}\n\n/* The weekdays down the start, outside the scroll: every other one named. */\n.rail {\n display: grid;\n grid-template-rows: repeat(7, var(--ds-calendar-cell));\n padding-block-start: var(--ds-calendar-cell);\n color: var(--ds-title-color);\n font-size: var(--ds-type-size-default);\n line-height: var(--ds-type-lineheight-default);\n}\n.weekday {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n white-space: nowrap;\n}\n\n.scroller {\n overflow-x: auto;\n overflow-y: hidden;\n scrollbar-width: none;\n overscroll-behavior-x: contain;\n}\n.scroller::-webkit-scrollbar { display: none; }\n\n/* The months over their weeks, one cell high, their names on its foot. */\n.months {\n display: grid;\n grid-template-columns: repeat(var(--ds-calendar-weeks), var(--ds-calendar-cell));\n height: var(--ds-calendar-cell);\n}\n.month {\n display: flex;\n align-items: flex-end;\n min-width: 0;\n padding: 0 var(--ds-calendar-stroke) calc(3px * var(--ds-calendar-scale, 1.5));\n overflow: hidden;\n white-space: nowrap;\n}\n\n/* The frame: a line all round with rounded corners; the cells draw the lines inside. */\n.frame {\n box-sizing: border-box;\n width: max-content;\n border: var(--ds-calendar-stroke) solid var(--ds-calendar-ink);\n border-radius: var(--ds-calendar-corner);\n overflow: hidden;\n}\n.grid {\n display: grid;\n grid-template-columns: repeat(var(--ds-calendar-weeks), var(--ds-calendar-cell));\n grid-template-rows: repeat(7, var(--ds-calendar-cell));\n}\n.row { display: contents; }\n.hidden {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n}\n\n.cell {\n box-sizing: border-box;\n display: flex;\n align-items: center;\n justify-content: center;\n width: var(--ds-calendar-cell);\n height: var(--ds-calendar-cell);\n border-block-start: var(--ds-calendar-stroke) solid var(--ds-calendar-ink);\n border-inline-start: var(--ds-calendar-stroke) solid var(--ds-calendar-ink);\n background: var(--ds-calendar-cell-fill, transparent);\n font-variant-numeric: tabular-nums;\n outline: none;\n}\n.cell[data-first-row] { border-block-start-width: 0; }\n.cell[data-first-column] { border-inline-start-width: 0; }\n.cell[data-filled] { color: var(--ds-slate); }\n:host([selectable]) .cell:not([aria-disabled=\"true\"]) { cursor: pointer; }\n:host([selectable]) .cell:not([aria-disabled=\"true\"], [data-filled]):hover { background: var(--ds-row-fill-hover, var(--ds-fill)); }\n.cell:focus-visible { outline: 2px solid var(--ds-focus-ring); outline-offset: -3px; }\n.cell[aria-disabled=\"true\"] { color: var(--ds-title-color); }\n\n.date {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: var(--ds-calendar-mark);\n height: var(--ds-calendar-mark);\n letter-spacing: 0;\n}\n.cell[data-today] .date { border-radius: 999px; background: var(--ds-calendar-today); color: var(--ds-calendar-today-ink); }\n.cell[data-selected] .date { background: var(--ds-calendar-ink); color: var(--ds-paper); }\n.cell[data-inside]:not([data-filled]) { background: color-mix(in srgb, var(--ds-calendar-ink) 12%, var(--ds-paper)); }\n.cell[data-today][data-selected] .date { border-radius: 999px; box-shadow: inset 0 0 0 var(--ds-calendar-stroke) var(--ds-calendar-today); }\n\n/* On a narrow screen the weekdays take one letter and one square. */\n@media (max-width: 720px) {\n :host { --ds-calendar-rail: var(--ds-unit); }\n .rail .weekday:not(:empty) { font-size: 0; }\n .rail .weekday:not(:empty)::after { content: attr(data-narrow); font-size: var(--ds-type-size-default); }\n}\n\n/* Activity keeps the day grid, with quiet separated squares and event colours. */\n:host([activity]) .frame { border: 0; border-radius: 0; }\n:host([activity]) .cell { position: relative; border: 0; background: none; }\n:host([activity]) .cell::before {\n content: \"\";\n position: absolute;\n inset: calc(2px * var(--ds-sf, 1));\n border-radius: calc(2px * var(--ds-sf, 1));\n background: var(--ds-calendar-cell-fill, var(--ds-fill));\n}\n:host([activity]) .date { display: none; }\n:host([activity]) .cell[data-selected]::before { outline: 1px solid var(--ds-calendar-ink); }\n";
11557
11677
  //#endregion
11558
- //#region DS1/3-unit/ds-carousel.ts
11559
- var _Carousel;
11560
- /** A whole number of squares, or `undefined` for a value that is not one. */
11561
- var squares = (value, least) => typeof value === "number" && Number.isFinite(value) && value >= least ? Math.floor(value) : void 0;
11678
+ //#region DS1/3-unit/ds-calendar-large.ts
11679
+ var _CalendarLarge;
11680
+ /** The fill for a day: one colour, or bands side by side. */
11681
+ function fillOf(events) {
11682
+ if (!events.length) return void 0;
11683
+ const colours = [...new Set(events.map((event) => event.color || "var(--ds-calendar-event-fill)"))];
11684
+ if (colours.length === 1) return colours[0];
11685
+ const share = 100 / colours.length;
11686
+ return `linear-gradient(90deg, ${colours.map((colour, index) => `${colour} ${index * share}% ${(index + 1) * share}%`).join(", ")})`;
11687
+ }
11562
11688
  /**
11563
- * @element ds-carousel
11564
- * @prop {string} label - The region's name: a translation key, or literal
11565
- * words. Name what the slides show ("Colours", "What people say").
11566
- * @prop {boolean} literal - Show `label` as written, never looked up.
11567
- * @prop {number} current - The slide in view, from 0. Setting it scrolls there.
11568
- * @prop {boolean} buttons - Show previous and next buttons beside the dots.
11569
- * @prop {boolean} noDots - Leave the dots out (`no-dots`).
11570
- * @prop {boolean} end - Draw the last dot as an end marker.
11571
- * @prop {number | "auto"} slideWidth - Each slide's width in whole squares
11572
- * (`slide-width`); the carousel's own width when not set; `auto` for slides
11573
- * of their own width, a strip of several in view.
11574
- * @prop {number} gap - Squares between slides; one by default.
11575
- * @prop {string} bleed - `none` (the default) keeps the strip in its box;
11576
- * `end` runs it on to the window's end edge, the first slide still on the
11577
- * box's start; `both` runs it to both edges.
11578
- * @prop {boolean} loop - Hold the slides three times over, so the strip has no
11579
- * end. The copies are hidden from assistive technology and never focused;
11580
- * a looping carousel draws no dots.
11581
- * @prop {string} autoplay - Move on a slide at a time by itself: empty for
11582
- * every five seconds, or a number of milliseconds. Held while a person is
11583
- * in the strip or cannot see it, never under reduced motion, and always
11584
- * with a pause button.
11585
- * @prop {boolean} free - Slide freely, without stopping at slides.
11586
- *
11587
- * @slot - The slides, one element each.
11588
- *
11589
- * @fires change - A person moved to another slide: by swiping, scrolling, a
11590
- * dot or a button. `detail` is `{ index, previous }`. Setting `current` or
11591
- * calling `goTo()` does not fire it.
11689
+ * @element ds-calendar-large
11690
+ * @prop {string} start - The first month drawn, `YYYY-MM`; the month before
11691
+ * today's by default.
11692
+ * @prop {number} months - How many months are drawn: 6 by default.
11693
+ * @prop {number} scale - How many times the grid it is drawn: 1.5 by default.
11694
+ * @prop {string} value - The selected day, `YYYY-MM-DD`.
11695
+ * @prop {boolean} selectable - Pressing a day selects it and fires `change`.
11696
+ * @prop {boolean} noHeader - Leave out the title and arrows (`no-header`).
11697
+ * @prop {string} min - The first day a person may choose.
11698
+ * @prop {string} max - The last day a person may choose.
11699
+ * @prop {string} weekStart - The week's first day (`week-start`); the
11700
+ * language's own by default.
11701
+ * @prop {string} today - The day drawn as today; the reader's own by default.
11702
+ * @prop {CalendarEvent[]} events - What happens on the days (property only).
11592
11703
  *
11593
- * The element is a region described as a carousel, named by `label`; each
11594
- * slide is a group labelled "2 of 5" unless the page labels it, and the
11595
- * current slide is announced politely when it changes. It moves on its own
11596
- * only with `autoplay`. `bleed`, `loop` and `autoplay` are read when the
11597
- * element first draws; put a new element in its place to change them.
11704
+ * @fires day-press - A day was pressed or chosen with Enter or Space; `{ day, events }`.
11705
+ * @fires change - A person selected a day; `{ value, previous }`.
11706
+ * @fires navigate - The months drawn moved on or back; `{ month }`, the first one.
11598
11707
  *
11599
11708
  * @example
11600
- * <ds-carousel label="Colours" slide-width="12" buttons>
11601
- * <figure>…</figure>
11602
- * <figure>…</figure>
11603
- * </ds-carousel>
11709
+ * <ds-calendar-large start="2026-09" months="6"></ds-calendar-large>
11604
11710
  */
11605
- var Carousel = class extends i$2 {
11711
+ var CalendarLarge = class extends CalendarBase {
11606
11712
  constructor() {
11607
11713
  super();
11608
- this.drive = null;
11609
- this.own = /* @__PURE__ */ new Map();
11714
+ this.weeksRun = "columns";
11715
+ this.byDay = /* @__PURE__ */ new Map();
11716
+ this.placed = false;
11610
11717
  this.frame = 0;
11611
- this.moving = false;
11612
- this.relabel = () => {
11613
- this.labelSlides();
11614
- this.requestUpdate();
11615
- };
11616
- this.scrolled = () => {
11718
+ this.onScroll = () => {
11617
11719
  cancelAnimationFrame(this.frame);
11618
- if (this.loop) return;
11619
11720
  this.frame = requestAnimationFrame(() => {
11620
- if (this.pending !== void 0) {
11621
- clearTimeout(this.settle);
11622
- this.settle = setTimeout(() => {
11623
- this.pending = void 0;
11624
- }, 150);
11625
- return;
11626
- }
11627
- const track = this.track;
11628
- if (track) this.show(nearestSlide(track, this.slides, this.index));
11721
+ const scroller = this.scroller;
11722
+ const cell = scroller?.querySelector(".cell")?.getBoundingClientRect().width;
11723
+ if (!scroller || !cell) return;
11724
+ const column = Math.min(this.weekStarts.length - 1, Math.max(0, Math.round(Math.abs(scroller.scrollLeft) / cell)));
11725
+ this.visible = startOfMonth(addDays(this.weekStarts[column], 3));
11629
11726
  });
11630
11727
  };
11631
- this.ended = () => {
11632
- this.pending = void 0;
11633
- };
11634
- this.picked = (event) => {
11635
- event.stopPropagation();
11728
+ this.onKeyDown = (event) => {
11729
+ const cell = event.target.closest("[data-day]");
11730
+ if (!cell) return;
11731
+ const day = cell.dataset.day;
11732
+ if (event.key === "Enter" || event.key === " ") {
11733
+ event.preventDefault();
11734
+ this.press(day);
11735
+ return;
11736
+ }
11737
+ const target = this.keyTarget(event, day);
11738
+ if (!target) return;
11636
11739
  event.preventDefault();
11637
- this.move(event.detail.index);
11740
+ if (target < this.weekStarts[0] || target > this.lastDay) {
11741
+ const by = target < this.weekStarts[0] ? -1 : 1;
11742
+ const first = addMonths(this.firstMonth, by);
11743
+ this.start = first.slice(0, 7);
11744
+ this.announceMonth(first);
11745
+ }
11746
+ this.focusCell(target);
11638
11747
  };
11639
- this.literal = false;
11640
- this.current = 0;
11641
- this.buttons = false;
11642
- this.noDots = false;
11643
- this.end = false;
11644
- this.bleed = "none";
11645
- this.loop = false;
11646
- this.autoplay = null;
11647
- this.free = false;
11648
- this.count = 0;
11649
- this.announcement = "";
11650
- this.paused = false;
11748
+ this.onClick = (event) => {
11749
+ const cell = event.composedPath().find((node) => node instanceof HTMLElement && node.dataset?.day);
11750
+ if (!cell) return;
11751
+ const day = cell.dataset.day;
11752
+ this.focusDay = day;
11753
+ this.press(day);
11754
+ };
11755
+ this.noHeader = false;
11756
+ this.activity = false;
11651
11757
  }
11652
- /** The slides: the elements given to the carousel, without a loop's copies. */
11653
- get slides() {
11654
- return [...this.children].filter((child) => child instanceof HTMLElement && !child.hasAttribute("slot") && !child.hasAttribute("data-ds-carousel-copy"));
11758
+ /** The first of the first month drawn. */
11759
+ get firstMonth() {
11760
+ return readMonth(this.start) ?? addMonths(startOfMonth(this.todayDay), -1);
11655
11761
  }
11656
- /** Milliseconds between autoplay steps; 0 for none. */
11657
- get every() {
11658
- return autoplayInterval(this.autoplay);
11762
+ get monthCount() {
11763
+ const count = this.months;
11764
+ return typeof count === "number" && Number.isFinite(count) ? Math.max(1, Math.min(36, Math.floor(count))) : 6;
11659
11765
  }
11660
- /** Slides of their own width: a strip. */
11661
- get strip() {
11662
- return this.slideWidth === "auto";
11766
+ /** The first day of each week drawn, from the week holding the first month's first day. */
11767
+ get weekStarts() {
11768
+ const first = this.firstMonth;
11769
+ const from = addDays(first, -((weekdayOf(first) - this.firstDay + 7) % 7));
11770
+ const last = addDays(addMonths(first, this.monthCount), -1);
11771
+ const weeks = [];
11772
+ for (let day = from; day <= last; day = addDays(day, 7)) weeks.push(day);
11773
+ return weeks;
11663
11774
  }
11664
- get track() {
11665
- return this.renderRoot.querySelector(".ds-carousel__track");
11775
+ get lastDay() {
11776
+ return addDays(this.weekStarts.at(-1), 6);
11666
11777
  }
11667
- /** The slide in view, inside the count. */
11668
- get index() {
11669
- const current = Number.isFinite(this.current) ? Math.floor(this.current) : 0;
11670
- return Math.max(0, Math.min(this.count - 1, current));
11778
+ eventsOn(day) {
11779
+ return this.byDay.get(day) ?? [];
11671
11780
  }
11672
- connectedCallback() {
11673
- super.connectedCallback();
11674
- window.addEventListener("language-changed", this.relabel);
11675
- window.addEventListener("translations-loaded", this.relabel);
11781
+ willUpdate(changed) {
11782
+ super.willUpdate(changed);
11783
+ const scale = typeof this.scale === "number" && Number.isFinite(this.scale) && this.scale > 0 ? this.scale : this.activity ? 1 : 1.5;
11784
+ this.style.setProperty("--ds-calendar-scale", String(scale));
11785
+ }
11786
+ firstUpdated() {
11787
+ const today = this.todayDay;
11788
+ this.scrollToDay(today >= this.weekStarts[0] && today <= this.lastDay ? startOfMonth(today) : this.firstMonth, false);
11789
+ this.placed = true;
11676
11790
  }
11677
11791
  disconnectedCallback() {
11678
- window.removeEventListener("language-changed", this.relabel);
11679
- window.removeEventListener("translations-loaded", this.relabel);
11680
11792
  cancelAnimationFrame(this.frame);
11681
- clearTimeout(this.settle);
11682
- this.drive?.stop();
11683
- this.drive = null;
11684
11793
  super.disconnectedCallback();
11685
11794
  }
11686
- words(value, fallback, params) {
11687
- const text = value && this.literal ? value : getText(value || fallback, params) || value || fallback;
11688
- return params ? text.replace(/\{\{?(\w+)\}?\}/g, (whole, name) => name in params ? String(params[name]) : whole) : text;
11689
- }
11690
- /** Writes an attribute the page has not written, and remembers it as this element's. */
11691
- write(element, name, value) {
11692
- const mine = this.own.get(element) ?? /* @__PURE__ */ new Set();
11693
- if (element.hasAttribute(name) && !mine.has(name)) return;
11694
- mine.add(name);
11695
- this.own.set(element, mine);
11696
- if (element.getAttribute(name) !== value) element.setAttribute(name, value);
11697
- }
11698
- labelSlides() {
11699
- const slides = this.slides;
11700
- const labelled = (slide) => slide.hasAttribute("aria-labelledby");
11701
- slides.forEach((slide, index) => {
11702
- this.write(slide, "role", "group");
11703
- this.write(slide, "aria-roledescription", this.words(void 0, "Slide"));
11704
- if (!labelled(slide)) this.write(slide, "aria-label", this.words(void 0, "{index} of {count}", {
11705
- index: index + 1,
11706
- count: slides.length
11707
- }));
11708
- });
11709
- this.count = slides.length;
11795
+ get scroller() {
11796
+ return this.renderRoot.querySelector(".scroller");
11710
11797
  }
11711
- willUpdate(changed) {
11712
- super.willUpdate(changed);
11713
- if (!this.hasUpdated) this.labelSlides();
11714
- this.write(this, "role", "region");
11715
- this.write(this, "aria-roledescription", this.words(void 0, "Carousel"));
11716
- if (!this.hasAttribute("aria-labelledby")) this.write(this, "aria-label", this.words(this.label, "Carousel"));
11717
- const width = squares(typeof this.slideWidth === "string" && this.slideWidth !== "auto" ? Number(this.slideWidth) : typeof this.slideWidth === "number" ? this.slideWidth : void 0, 1);
11718
- const gap = squares(this.gap, 0);
11719
- if (this.strip) this.style.setProperty("--ds-carousel-slide", "max-content");
11720
- else if (width) this.style.setProperty("--ds-carousel-slide", `calc(var(--ds-unit) * ${width})`);
11721
- else this.style.removeProperty("--ds-carousel-slide");
11722
- if (gap !== void 0) this.style.setProperty("--ds-carousel-gap", `calc(var(--ds-unit) * ${gap})`);
11723
- else this.style.removeProperty("--ds-carousel-gap");
11798
+ columnOf(day) {
11799
+ return this.weekStarts.findIndex((start) => day >= start && day <= addDays(start, 6));
11724
11800
  }
11725
- firstUpdated() {
11726
- const track = this.track;
11727
- const bleed = bleedOf(this.bleed);
11728
- if (track && (bleed !== "none" || this.loop || this.every > 0)) this.drive = driveCarousel(this, track, this, {
11729
- bleed,
11730
- loop: this.loop,
11731
- autoplay: this.every
11801
+ /** Scrolls the week holding `day` to the start of the view. */
11802
+ scrollToDay(day, smooth = true) {
11803
+ const scroller = this.scroller;
11804
+ const column = this.columnOf(day);
11805
+ if (!scroller || column < 0) return;
11806
+ const cell = scroller.querySelector(".cell")?.getBoundingClientRect().width ?? 30;
11807
+ const reduce = matchMedia?.("(prefers-reduced-motion: reduce)").matches;
11808
+ const rtl = getComputedStyle(this).direction === "rtl";
11809
+ scroller.scrollTo({
11810
+ left: (rtl ? -1 : 1) * column * cell,
11811
+ behavior: smooth && !reduce ? "smooth" : "auto"
11732
11812
  });
11813
+ this.visible = startOfMonth(day);
11733
11814
  }
11734
- updated(changed) {
11735
- super.updated(changed);
11736
- if (this.loop) return;
11737
- if (changed.has("current")) {
11738
- if (this.moving) {
11739
- this.moving = false;
11740
- return;
11741
- }
11742
- this.travel(this.index, changed.get("current") !== void 0);
11743
- } else if (changed.has("count") && !changed.get("count") && this.index > 0) this.travel(this.index, false);
11744
- }
11745
- /** Goes to a slide: smoothly, unless the reader asks for reduced motion. Fires no `change`. */
11746
- goTo(index) {
11747
- this.current = index;
11748
- }
11749
- /** Goes to the next slide, as the next button does. */
11750
- next() {
11751
- if (this.drive) this.drive.step(1);
11752
- else this.move(this.index + 1);
11753
- }
11754
- /** Goes to the previous slide, as the previous button does. */
11755
- previous() {
11756
- if (this.drive) this.drive.step(-1);
11757
- else this.move(this.index - 1);
11815
+ /** Moves the view a month back or on; past the months drawn, the months move with it. */
11816
+ step(by) {
11817
+ const target = addMonths(this.visible ?? this.firstMonth, by);
11818
+ const first = this.firstMonth;
11819
+ const end = addMonths(first, this.monthCount);
11820
+ if (target < first || target >= end) {
11821
+ this.start = addMonths(first, by).slice(0, 7);
11822
+ this.announceMonth(addMonths(first, by));
11823
+ this.updateComplete.then(() => this.scrollToDay(target, false));
11824
+ return;
11825
+ }
11826
+ this.scrollToDay(target);
11758
11827
  }
11759
- togglePause() {
11760
- this.paused = !this.paused;
11761
- this.drive?.pause(this.paused);
11828
+ /** The month before, as the previous arrow does. */
11829
+ previousMonth() {
11830
+ this.step(-1);
11762
11831
  }
11763
- travel(index, smooth) {
11764
- const track = this.track;
11765
- const slide = this.slides[index];
11766
- if (!track || !slide || !scrollToSlide(track, slide, smooth)) return;
11767
- this.pending = index;
11768
- clearTimeout(this.settle);
11769
- this.settle = setTimeout(() => {
11770
- this.pending = void 0;
11771
- }, 600);
11832
+ /** The month after, as the next arrow does. */
11833
+ nextMonth() {
11834
+ this.step(1);
11772
11835
  }
11773
- /** A person moved to `index`: show it, say it, and fire `change`. */
11774
- show(index) {
11775
- const target = Math.max(0, Math.min(this.count - 1, index));
11776
- const previous = this.index;
11777
- if (target === previous || this.count === 0) return;
11778
- this.moving = true;
11779
- this.current = target;
11780
- this.announcement = this.words(void 0, "{index} of {count}", {
11781
- index: target + 1,
11782
- count: this.count
11783
- });
11784
- this.dispatchEvent(new CustomEvent("change", {
11785
- bubbles: true,
11786
- composed: true,
11787
- detail: {
11788
- index: target,
11789
- previous
11790
- }
11791
- }));
11836
+ /** Brings today into view, moving the months drawn when it is outside them. */
11837
+ showToday() {
11838
+ const today = this.todayDay;
11839
+ if (today < this.weekStarts[0] || today > this.lastDay) {
11840
+ const first = addMonths(startOfMonth(today), -1);
11841
+ this.start = first.slice(0, 7);
11842
+ this.announceMonth(first);
11843
+ this.updateComplete.then(() => this.scrollToDay(startOfMonth(today), false));
11844
+ } else this.scrollToDay(startOfMonth(today));
11845
+ this.focusDay = today;
11792
11846
  }
11793
- move(index) {
11794
- const target = Math.max(0, Math.min(this.count - 1, index));
11795
- this.travel(target, true);
11796
- this.show(target);
11847
+ get tabDay() {
11848
+ const first = this.weekStarts[0], last = this.lastDay;
11849
+ for (const day of [
11850
+ this.focusDay,
11851
+ this.chosen.start,
11852
+ this.todayDay
11853
+ ]) if (day && day >= first && day <= last) return day;
11854
+ return this.firstMonth;
11797
11855
  }
11798
- render() {
11799
- const index = this.index;
11800
- const driven = this.drive !== null || bleedOf(this.bleed) !== "none" || this.loop || this.every > 0;
11801
- const button = (which) => b$1`<button
11802
- class="ds-button ds-button--icon ds-carousel__${which}"
11856
+ renderHeader() {
11857
+ if (this.noHeader) return A;
11858
+ const arrow = (which) => b$1`<button
11859
+ class="ds-button ds-button--icon nav"
11803
11860
  part=${which}
11804
11861
  type="button"
11805
- aria-label=${this.words(void 0, which === "previous" ? "Previous slide" : "Next slide")}
11806
- ?disabled=${!driven && (which === "previous" ? index <= 0 : index >= this.count - 1)}
11807
- @click=${() => which === "previous" ? this.previous() : this.next()}
11808
- ><ds-icon type=${which === "previous" ? "left" : "right"} aria-hidden="true"></ds-icon></button>`;
11809
- const moving = this.every > 0 && !prefersReducedMotion();
11810
- const dots = !this.noDots && !this.loop;
11811
- const controls = this.buttons || dots || moving;
11812
- const bleed = bleedOf(this.bleed);
11813
- return b$1`<div class="ds-carousel" part="carousel" data-slides=${this.strip ? "auto" : A} data-bleed=${bleed === "none" ? A : bleed} data-snap=${this.free ? "none" : A}>
11814
- <div class="ds-carousel__track" part="track" @scroll=${this.scrolled} @scrollend=${this.ended}>
11815
- <slot @slotchange=${() => this.labelSlides()}></slot>
11862
+ aria-label=${this.words(which === "previous" ? "Previous month" : "Next month")}
11863
+ @click=${() => which === "previous" ? this.previousMonth() : this.nextMonth()}
11864
+ ><ds-icon type=${which === "previous" ? "chevron-left" : "chevron-right"} size="large" aria-hidden="true"></ds-icon></button>`;
11865
+ return b$1`<div class="header" part="header">
11866
+ <span class="title" aria-live="polite">${monthTitle(this.visible ?? this.firstMonth)}</span>
11867
+ <span class="controls">
11868
+ ${arrow("previous")}
11869
+ <button class="ds-button ds-button--secondary" part="today" type="button" @click=${() => this.showToday()}>${this.words("Today")}</button>
11870
+ ${arrow("next")}
11871
+ </span>
11872
+ </div>`;
11873
+ }
11874
+ render() {
11875
+ const weeks = this.weekStarts;
11876
+ const first = weeks[0], last = this.lastDay;
11877
+ this.byDay = eventsByDay(this.events ?? [], first, last);
11878
+ const short = weekdayNames(this.firstDay, "short");
11879
+ const narrow = weekdayNames(this.firstDay, "narrow");
11880
+ const long = weekdayNames(this.firstDay, "long");
11881
+ const today = this.todayDay;
11882
+ const tab = this.tabDay;
11883
+ const labels = [];
11884
+ for (let month = this.firstMonth, index = 0; index < this.monthCount; index++, month = addMonths(month, 1)) {
11885
+ const column = this.columnOf(month);
11886
+ const next = this.columnOf(addMonths(month, 1));
11887
+ labels.push({
11888
+ column,
11889
+ span: Math.max(1, (next < 0 ? weeks.length : next) - column),
11890
+ day: month
11891
+ });
11892
+ }
11893
+ const ends = /* @__PURE__ */ new Set();
11894
+ for (const event of this.events ?? []) {
11895
+ ends.add(event.start);
11896
+ if (event.end) ends.add(event.end);
11897
+ }
11898
+ return b$1`<div class="calendar" part="calendar" style=${`--ds-calendar-weeks: ${weeks.length}`}>
11899
+ ${this.renderHeader()}
11900
+ <div class="body">
11901
+ <div class="rail" part="weekdays" aria-hidden="true">
11902
+ ${short.map((name, row) => b$1`<span class="weekday" data-narrow=${narrow[row]}>${row % 2 === 1 ? name : ""}</span>`)}
11903
+ </div>
11904
+ <div class="scroller" part="scroller" @scroll=${this.onScroll}>
11905
+ <div class="months" aria-hidden="true">
11906
+ ${labels.map(({ column, span, day }) => b$1`<span class="month" style=${`grid-column: ${column + 1} / span ${span}`}>${day.slice(5, 7) === "01" || day === this.firstMonth ? monthTitle(day, "short") : monthName(day, "short")}</span>`)}
11907
+ </div>
11908
+ <div class="frame" part="frame">
11909
+ <div
11910
+ class="grid"
11911
+ part="grid"
11912
+ role="grid"
11913
+ aria-label=${this.nameOr(`${monthTitle(this.firstMonth)} – ${monthTitle(addMonths(this.firstMonth, this.monthCount - 1))}`)}
11914
+ @keydown=${this.onKeyDown}
11915
+ @click=${this.onClick}
11916
+ >
11917
+ ${short.map((_, row) => b$1`<div role="row" class="row">
11918
+ <span role="rowheader" class="hidden">${long[row]}</span>
11919
+ ${weeks.map((start, column) => {
11920
+ const day = addDays(start, row);
11921
+ const events = this.eventsOn(day);
11922
+ const isToday = day === today;
11923
+ const { selected, inside } = this.selection(day);
11924
+ const written = isToday || selected || events.length > 0 && ends.has(day);
11925
+ const fill = fillOf(events);
11926
+ return b$1`<div
11927
+ role="gridcell"
11928
+ class="cell"
11929
+ part=${[
11930
+ "cell",
11931
+ isToday ? "today" : "",
11932
+ selected ? "selected" : "",
11933
+ events.length ? "filled" : ""
11934
+ ].filter(Boolean).join(" ")}
11935
+ data-day=${day}
11936
+ tabindex=${day === tab ? 0 : -1}
11937
+ aria-label=${this.cellLabel(day, events)}
11938
+ title=${this.activity ? this.cellLabel(day, events) : A}
11939
+ aria-selected=${this.selectable ? String(selected || inside) : A}
11940
+ aria-current=${isToday ? "date" : A}
11941
+ aria-disabled=${this.allowed(day) ? A : "true"}
11942
+ ?data-today=${isToday}
11943
+ ?data-selected=${selected}
11944
+ ?data-inside=${inside}
11945
+ ?data-filled=${events.length > 0}
11946
+ ?data-first-row=${row === 0}
11947
+ ?data-first-column=${column === 0}
11948
+ style=${`grid-row: ${row + 1}; grid-column: ${column + 1};${fill ? ` --ds-calendar-cell-fill: ${fill};` : ""}`}
11949
+ >${written ? b$1`<span class="date" aria-hidden="true">${dateNumber(day)}</span>` : A}</div>`;
11950
+ })}
11951
+ </div>`)}
11952
+ </div>
11953
+ </div>
11954
+ </div>
11816
11955
  </div>
11817
- ${controls && this.count > 1 ? b$1`<div class="ds-carousel__controls" part="controls">
11818
- ${moving ? b$1`<button
11819
- class="ds-button ds-button--icon ds-carousel__pause"
11820
- part="pause"
11821
- type="button"
11822
- aria-pressed=${String(this.paused)}
11823
- aria-label=${this.words(void 0, this.paused ? "Play" : "Pause")}
11824
- @click=${this.togglePause}
11825
- ><ds-icon type=${this.paused ? "play-alt" : "pause"} aria-hidden="true"></ds-icon></button>` : A}
11826
- ${this.buttons ? button("previous") : A}
11827
- ${dots ? b$1`<ds-progress part="dots" .dots=${this.count} .active=${index} ?end=${this.end} @select=${this.picked}></ds-progress>` : A}
11828
- ${this.buttons ? button("next") : A}
11829
- </div>` : A}
11830
- <span class="ds-carousel__status" part="status" aria-live="polite" aria-atomic="true">${this.announcement}</span>
11831
11956
  </div>`;
11832
11957
  }
11958
+ updated(changed) {
11959
+ super.updated(changed);
11960
+ if (this.placed && changed.has("start") && this.visible && !this.focusDay) this.scrollToDay(this.visible, false);
11961
+ }
11833
11962
  };
11834
- _Carousel = Carousel;
11835
- _Carousel.properties = {
11836
- label: { type: String },
11837
- literal: {
11838
- type: Boolean,
11839
- reflect: true
11840
- },
11841
- current: {
11842
- type: Number,
11843
- reflect: true
11844
- },
11845
- buttons: {
11846
- type: Boolean,
11847
- reflect: true
11848
- },
11849
- noDots: {
11850
- type: Boolean,
11851
- reflect: true,
11852
- attribute: "no-dots"
11853
- },
11854
- end: {
11855
- type: Boolean,
11856
- reflect: true
11857
- },
11858
- slideWidth: {
11859
- attribute: "slide-width",
11860
- reflect: true,
11861
- converter: {
11862
- fromAttribute: (value) => value === null ? void 0 : value === "auto" ? "auto" : Number(value),
11863
- toAttribute: (value) => value === void 0 ? null : String(value)
11864
- }
11865
- },
11866
- gap: { type: Number },
11867
- bleed: {
11963
+ _CalendarLarge = CalendarLarge;
11964
+ _CalendarLarge.properties = {
11965
+ ...CalendarBase.properties,
11966
+ start: {
11868
11967
  type: String,
11869
11968
  reflect: true
11870
11969
  },
11871
- loop: {
11970
+ months: { type: Number },
11971
+ scale: { type: Number },
11972
+ activity: {
11872
11973
  type: Boolean,
11873
11974
  reflect: true
11874
11975
  },
11875
- autoplay: {
11876
- type: String,
11877
- reflect: true
11878
- },
11879
- free: {
11976
+ noHeader: {
11880
11977
  type: Boolean,
11978
+ attribute: "no-header",
11881
11979
  reflect: true
11882
11980
  },
11883
- count: { state: true },
11884
- announcement: { state: true },
11885
- paused: { state: true }
11981
+ visible: { state: true }
11886
11982
  };
11887
- _Carousel.styles = [
11983
+ _CalendarLarge.styles = [
11888
11984
  r$3(component_metrics_default),
11889
11985
  r$3(controls_default),
11890
- r$3(carousel_default),
11891
- r$3(ds_carousel_default)
11986
+ r$3(ds_calendar_large_default)
11892
11987
  ];
11893
- define("ds-carousel", Carousel);
11894
- //#endregion
11895
- //#region DS1/3-unit/styles/ds-date-picker.css?inline
11896
- var ds_date_picker_default = "/* ds-date-picker.css\n * The field is the select's face: one control high at the foot of its\n * square, ten squares wide by default, the calendar icon at its start and\n * the down arrow at its end. The panel opens on the menu's frame, half a\n * square from the field: a 1px line, half a square inside it, no shadow, so\n * eight squares wide. Inside, on whole squares: the first and last day to\n * type (each with its time below when times are on), one square spare, the\n * compact month one square a day with 18px marks, then Include time and\n * Clear, each one square, set off by one-square separators with a line\n * through them. */\n\n:host {\n --ds-date-picker-width: calc(10 * var(--ds-unit));\n box-sizing: border-box;\n display: inline-flex;\n align-items: flex-end;\n vertical-align: bottom;\n height: var(--ds-unit);\n color: var(--ds-text-color);\n font-family: var(--ds-typeface-regular);\n font-size: var(--ds-type-size-default);\n line-height: var(--ds-type-lineheight-default);\n}\n:host([hidden]) { display: none !important; }\n\n.trigger {\n display: inline-flex;\n align-items: flex-end;\n gap: calc(4px * var(--ds-sf, 1));\n width: var(--ds-date-picker-width);\n padding-inline: 0 calc(4px * var(--ds-sf, 1));\n text-align: start;\n cursor: pointer;\n}\n.trigger:disabled { cursor: default; }\n.trigger .icon,\n.trigger .arrow { flex: 0 0 auto; }\n.trigger .text {\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.trigger[data-empty] .text { color: var(--ds-title-color); }\n.trigger[aria-expanded=\"true\"] .arrow { transform: rotate(180deg); }\n\n/* The panel, on the menu's frame. */\n.popover {\n position: fixed;\n inset: auto;\n box-sizing: border-box;\n margin: 0;\n padding: calc(var(--ds-unit) / 2 - 1px);\n border: 1px solid var(--ds-border-color);\n border-radius: var(--ds-radius-card);\n background: var(--ds-paper);\n color: var(--ds-text-color);\n box-shadow: none;\n overflow: visible;\n}\n.popover:not(:popover-open) { display: none; }\n\n/* The days to type: a row of one square for each end, its time on the row below; one square spare after. */\n.fields { margin-block-end: var(--ds-unit); }\n.field-row {\n display: flex;\n align-items: flex-end;\n gap: calc(var(--ds-unit) / 2);\n height: var(--ds-unit);\n}\n.field { flex: 1 1 auto; min-width: 0; }\n.clock { flex: 1 1 auto; min-width: 0; }\n.fields, .options { width: calc(7 * var(--ds-unit)); }\n\n/* The switches and Clear, each one square, the words on the sixteen. */\n.option {\n box-sizing: border-box;\n display: flex;\n align-items: flex-end;\n justify-content: space-between;\n gap: var(--ds-unit);\n width: 100%;\n height: var(--ds-unit);\n margin: 0;\n padding: 0 calc(4px * var(--ds-sf, 1));\n border: 0;\n border-radius: var(--ds-radius);\n background: transparent;\n color: var(--ds-text-color);\n font: inherit;\n letter-spacing: inherit;\n text-align: start;\n cursor: pointer;\n}\n.option:hover:not(:disabled) { background: var(--ds-row-fill-hover, var(--ds-fill)); }\n.option:focus-visible { outline: 2px solid var(--ds-focus-ring); outline-offset: -2px; }\n.option:disabled { color: var(--ds-title-color); cursor: default; }\n.option .ds-switch { flex: 0 0 auto; }\n\n/* A separator: one square with a 1px line through its middle. */\n.separator {\n position: relative;\n height: var(--ds-unit);\n}\n.separator::after {\n position: absolute;\n inset-inline: 0;\n top: calc(50% - 0.5px);\n height: 1px;\n background: var(--ds-border-color);\n content: \"\";\n}\n\n@media (prefers-reduced-motion: no-preference) {\n .popover:popover-open {\n animation: ds-date-picker-in 140ms cubic-bezier(0.22, 1, 0.36, 1);\n }\n .trigger .arrow { transition: transform 140ms ease; }\n}\n@keyframes ds-date-picker-in {\n from { opacity: 0; transform: translateY(calc(-4px * var(--ds-sf, 1))); }\n to { opacity: 1; transform: none; }\n}\n";
11988
+ define("ds-calendar-large", CalendarLarge);
11897
11989
  //#endregion
11898
- //#region DS1/3-unit/ds-date-picker.ts
11899
- var _DatePicker;
11900
- var TIME = /^([01]\d|2[0-3]):([0-5]\d)$/;
11901
- /** Reads a value: `2026-10-21`, `2026-10-21T14:30`, or two of them joined by `/`. */
11902
- function readDateValue(value) {
11903
- const read = (part) => {
11904
- const [day = "", time = ""] = (part ?? "").trim().split("T");
11905
- if (!isDay(day)) return void 0;
11906
- const clock = time.slice(0, 5);
11990
+ //#region DS1/0-face/bento.ts
11991
+ /** Reads `data-place` ("x y w h"); `undefined` unless it is four whole numbers with a size. */
11992
+ function bentoPlace(value) {
11993
+ const parts = (value ?? "").trim().split(/[\s,]+/).map(Number);
11994
+ if (parts.length !== 4 || parts.some((part) => !Number.isInteger(part) || part < 0)) return void 0;
11995
+ const [x, y, w, h] = parts;
11996
+ return w > 0 && h > 0 ? [
11997
+ x,
11998
+ y,
11999
+ w,
12000
+ h
12001
+ ] : void 0;
12002
+ }
12003
+ var overlaps = (a0, a1, b0, b1) => a0 < b1 && b0 < a1;
12004
+ /** For each place, the sides that face another tile across a one-square gap. */
12005
+ function bentoReach(places) {
12006
+ return places.map(([x, y, w, h], index) => {
12007
+ const others = places.filter((_, other) => other !== index);
11907
12008
  return {
11908
- day,
11909
- time: TIME.test(clock) ? clock : ""
12009
+ start: others.some(([ox, oy, ow, oh]) => ox + ow + 1 === x && overlaps(y, y + h, oy, oy + oh)),
12010
+ end: others.some(([ox, oy, , oh]) => x + w + 1 === ox && overlaps(y, y + h, oy, oy + oh)),
12011
+ top: others.some(([ox, oy, ow, oh]) => oy + oh + 1 === y && overlaps(x, x + w, ox, ox + ow)),
12012
+ bottom: others.some(([ox, oy, ow]) => y + h + 1 === oy && overlaps(x, x + w, ox, ox + ow))
11910
12013
  };
11911
- };
11912
- const [first, second] = (value ?? "").split("/");
11913
- let start = read(first), end = read(second);
11914
- if (start && end && `${end.day}T${end.time}` < `${start.day}T${start.time}`) [start, end] = [end, start];
11915
- return {
11916
- start,
11917
- end
11918
- };
11919
- }
11920
- /**
11921
- * Writes a value from its ends, the times only with `time`. An end that is
11922
- * the same moment as the start leaves the value one day.
11923
- */
11924
- function writeDateValue(start, end, options) {
11925
- if (!start) return "";
11926
- const part = (side) => options.time && side.time ? `${side.day}T${side.time}` : side.day;
11927
- return end && part(end) !== part(start) ? `${part(start)}/${part(end)}` : part(start);
12014
+ });
11928
12015
  }
11929
- /** Reads what a person typed: a day written `YYYY-MM-DD`, or a date the browser can read ("Oct 21, 2026"). */
11930
- function parseTypedDay(text) {
11931
- const trimmed = text.trim();
11932
- if (isDay(trimmed)) return trimmed;
11933
- const moment = Date.parse(trimmed);
11934
- return Number.isFinite(moment) ? dayOf(new Date(moment)) : null;
12016
+ /** Places one squares bento's tiles: their squares and their reach, as custom properties. */
12017
+ function placeBento(bento) {
12018
+ const placed = [...bento.children].filter((child) => child instanceof HTMLElement && !child.hidden).map((tile) => ({
12019
+ tile,
12020
+ place: bentoPlace(tile.dataset.place)
12021
+ })).filter((entry) => !!entry.place);
12022
+ const reach = bentoReach(placed.map((entry) => entry.place));
12023
+ placed.forEach(({ tile, place: [x, y, w, h] }, index) => {
12024
+ const sides = reach[index];
12025
+ tile.style.setProperty("--ds-bento-x", String(x));
12026
+ tile.style.setProperty("--ds-bento-y", String(y));
12027
+ tile.style.setProperty("--ds-bento-w", String(w));
12028
+ tile.style.setProperty("--ds-bento-h", String(h));
12029
+ for (const side of [
12030
+ "start",
12031
+ "top",
12032
+ "end",
12033
+ "bottom"
12034
+ ]) tile.style.setProperty(`--ds-bento-${side}`, sides[side] ? "1" : "0");
12035
+ });
11935
12036
  }
11936
- var openPickers = /* @__PURE__ */ new Set();
12037
+ //#endregion
12038
+ //#region DS1/3-unit/styles/ds-bento.css?inline
12039
+ var ds_bento_default = "/* ds-bento.css */\n/* The element's bento: the same as 1-root/bento.css's .ds-bento, on the host and its slotted tiles. */\n\n:host {\n --ds-bento-gap: calc(var(--ds-unit) / 2);\n box-sizing: border-box;\n display: grid;\n grid-template-columns: repeat(var(--ds-bento-columns, 3), minmax(0, 1fr));\n grid-auto-rows: minmax(var(--ds-bento-row, calc(10 * var(--ds-unit))), auto);\n grid-auto-flow: row dense;\n gap: var(--ds-bento-gap);\n}\n:host([hidden]) { display: none !important; }\n\n::slotted(*) {\n --ds-card-width: auto;\n --ds-card-height: auto;\n box-sizing: border-box;\n min-width: 0;\n min-height: 0;\n grid-column: span var(--ds-bento-span, 1);\n grid-row: span var(--ds-bento-rows, 1);\n}\n::slotted([data-span=\"2\"]) { --ds-bento-span: 2; }\n::slotted([data-span=\"3\"]) { --ds-bento-span: 3; }\n::slotted([data-span=\"4\"]) { --ds-bento-span: 4; }\n::slotted([data-span=\"5\"]) { --ds-bento-span: 5; }\n::slotted([data-span=\"6\"]) { --ds-bento-span: 6; }\n::slotted([data-rows=\"2\"]) { --ds-bento-rows: 2; }\n::slotted([data-rows=\"3\"]) { --ds-bento-rows: 3; }\n::slotted([data-rows=\"4\"]) { --ds-bento-rows: 4; }\n::slotted([data-rows=\"5\"]) { --ds-bento-rows: 5; }\n::slotted([data-rows=\"6\"]) { --ds-bento-rows: 6; }\n\n@media (max-width: 600px) {\n :host(:not([squares])) { grid-template-columns: minmax(0, 1fr); }\n :host(:not([squares])) ::slotted(*) { grid-column: auto; }\n}\n\n:host([squares]) {\n --ds-bento-reach: calc(var(--ds-unit) / 4);\n /* Exactly its squares wide: each column one square. */\n width: calc(var(--ds-bento-columns, 48) * var(--ds-unit));\n max-width: none;\n grid-template-columns: repeat(var(--ds-bento-columns, 48), var(--ds-unit));\n grid-auto-rows: var(--ds-unit);\n grid-auto-flow: row;\n gap: 0;\n}\n:host([squares]) ::slotted(*) {\n --ds-bento-inset-top: calc(var(--ds-bento-reach) * var(--ds-bento-top, 0));\n --ds-bento-inset-end: calc(var(--ds-bento-reach) * var(--ds-bento-end, 0));\n --ds-bento-inset-bottom: calc(var(--ds-bento-reach) * var(--ds-bento-bottom, 0));\n --ds-bento-inset-start: calc(var(--ds-bento-reach) * var(--ds-bento-start, 0));\n grid-column: calc(var(--ds-bento-x, 0) + 1) / span var(--ds-bento-w, 4);\n grid-row: calc(var(--ds-bento-y, 0) + 1) / span var(--ds-bento-h, 4);\n margin: calc(-1 * var(--ds-bento-inset-top)) calc(-1 * var(--ds-bento-inset-end)) calc(-1 * var(--ds-bento-inset-bottom)) calc(-1 * var(--ds-bento-inset-start));\n}\n/* A card grows its own padding by the reach (card.css); any other tile is padded here, a square inside. */\n:host([squares]) ::slotted(:not(.ds-card, ds-card)) {\n padding:\n calc(var(--ds-unit) + var(--ds-bento-inset-top))\n calc(var(--ds-unit) + var(--ds-bento-inset-end))\n calc(var(--ds-unit) + var(--ds-bento-inset-bottom))\n calc(var(--ds-unit) + var(--ds-bento-inset-start));\n}\n";
12040
+ //#endregion
12041
+ //#region DS1/3-unit/ds-bento.ts
12042
+ var _Bento;
12043
+ var Bento = class extends i$2 {
12044
+ constructor() {
12045
+ super();
12046
+ this.squares = false;
12047
+ }
12048
+ connectedCallback() {
12049
+ super.connectedCallback();
12050
+ this.observer = new MutationObserver(() => this.place());
12051
+ this.observer.observe(this, {
12052
+ childList: true,
12053
+ subtree: true,
12054
+ attributes: true,
12055
+ attributeFilter: ["data-place", "hidden"]
12056
+ });
12057
+ }
12058
+ disconnectedCallback() {
12059
+ this.observer?.disconnect();
12060
+ super.disconnectedCallback();
12061
+ }
12062
+ updated() {
12063
+ if (this.columns) this.style.setProperty("--ds-bento-columns", String(this.columns));
12064
+ else this.style.removeProperty("--ds-bento-columns");
12065
+ this.place();
12066
+ }
12067
+ place() {
12068
+ if (this.squares) placeBento(this);
12069
+ }
12070
+ render() {
12071
+ return b$1`<slot @slotchange=${() => this.place()}></slot>`;
12072
+ }
12073
+ };
12074
+ _Bento = Bento;
12075
+ _Bento.properties = {
12076
+ columns: {
12077
+ type: Number,
12078
+ reflect: true
12079
+ },
12080
+ squares: {
12081
+ type: Boolean,
12082
+ reflect: true
12083
+ }
12084
+ };
12085
+ _Bento.styles = [r$3(component_metrics_default), r$3(ds_bento_default)];
12086
+ define("ds-bento", Bento);
12087
+ //#endregion
12088
+ //#region DS1/1-root/carousel.css?inline
12089
+ var carousel_default = "/* Shape is a surface preference. Keep these base lengths unscaled: each\n * component resolves them with its own --ds-sf in component-metrics.css. */\n[data-ds-shape=\"square\"],\n:host([data-ds-shape=\"square\"]) {\n --ds-corner-size: 0px;\n --ds-corner-shape: round;\n --ds-button-inset: 0.5px;\n}\n\n[data-ds-shape=\"rounded\"],\n:host([data-ds-shape=\"rounded\"]) {\n --ds-corner-size: 4px;\n --ds-corner-shape: round;\n --ds-button-inset: 2.5px;\n}\n\n[data-ds-shape=\"circular\"],\n:host([data-ds-shape=\"circular\"]) {\n --ds-corner-size: 999px;\n --ds-corner-shape: round;\n --ds-button-inset: 5px;\n}\n\n/* A squircle needs twice an arc's radius for the same apparent corner.\n * Circular controls keep true semicircles even in a squircle browser. */\n@supports (corner-shape: squircle) {\n [data-ds-shape=\"rounded\"],\n :host([data-ds-shape=\"rounded\"]) {\n --ds-corner-size: 8px;\n --ds-corner-shape: squircle;\n }\n}\n\n/* Resolve dimensions where a component is drawn. Inherited calc() values have\n * already used their ancestor's scale. Base tokens stay unscaled and inherit;\n * resolved tokens can still be overridden directly on a component.\n * data-ds-surface gives ordinary layout elements the same scaled measures.\n */\n:root,\n:host,\n:where([data-ds-surface]) {\n --ds-unit: calc(var(--ds-1, 20px) * var(--ds-sf, 1));\n --ds-control-height: calc(var(--ds-control-size, var(--ds-08, 16px)) * var(--ds-sf, 1));\n --ds-row-height: calc(var(--ds-row-size, var(--ds-1, 20px)) * var(--ds-sf, 1));\n --ds-type-size-default: calc(var(--ds-text-size, 14px) * var(--ds-sf, 1));\n --ds-type-size-small: calc(var(--ds-small-text-size, 10px) * var(--ds-sf, 1));\n --ds-type-size-heading: calc(var(--ds-heading-text-size, 13px) * var(--ds-sf, 1));\n --ds-type-size-display: calc(var(--ds-display-text-size, 20px) * var(--ds-sf, 1));\n --ds-type-lineheight-default: calc(var(--ds-text-leading, 16px) * var(--ds-sf, 1));\n --ds-type-letterspacing-default: calc(var(--ds-text-tracking, -0.3px) * var(--ds-sf, 1));\n --ds-radius: calc(var(--ds-corner-size, 4px) * var(--ds-sf, 1));\n --ds-radius-card: calc(var(--ds-card-corner-size, 4px) * var(--ds-sf, 1));\n}\n\n/*\n * A carousel, as plain classes on native elements.\n *\n * A carousel is a row of slides that snaps sideways, one slide to a stop,\n * with a row of controls one square high under it: optional previous and\n * next buttons and a dot for each slide. Slides are the track's width, or\n * whole squares wide, a whole number of squares apart (one by default). The\n * controls stand one square below the track, on the grid from its start.\n * The `ds-carousel` element draws the same parts, and\n * `0-face/carousel.ts` gives the native markup its behaviour.\n *\n * ds-carousel the region: track, controls, status;\n * `data-slides=\"auto\"` for slides of their\n * own width, `data-bleed` `end` or `both`,\n * `data-snap=\"none\"` to slide freely\n * ds-carousel__track the scroller; every child is a slide\n * ds-carousel__controls the row of buttons and dots\n * ds-carousel__previous, __next, icon buttons, on `.ds-button.ds-button--icon`\n * __pause\n * ds-carousel__status the polite announcement, kept out of sight\n *\n * The dots are `ds-progress--dots` from progress.css.\n *\n * A strip of slides of their own width can run on past its box to the\n * window's end edge, or to both edges, while its first slide keeps to the\n * box's start, so it lines up with the page's column; `--ds-carousel-before`\n * and `--ds-carousel-after` are the room to the edges, which the script\n * measures.\n *\n * Tokens: `--ds-carousel-slide` (a slide's width, the track's by default)\n * and `--ds-carousel-gap` (between slides, one square by default).\n */\n\n.ds-carousel {\n box-sizing: border-box;\n display: grid;\n grid-template-columns: minmax(0, 1fr);\n row-gap: var(--ds-unit);\n position: relative;\n min-width: 0;\n margin: 0;\n}\n\n.ds-carousel__track {\n box-sizing: border-box;\n display: grid;\n grid-auto-flow: column;\n grid-auto-columns: var(--ds-carousel-slide, 100%);\n column-gap: var(--ds-carousel-gap, var(--ds-unit));\n min-width: 0;\n margin: 0;\n padding: 0;\n list-style: none;\n overflow-x: auto;\n overflow-y: hidden;\n overscroll-behavior-x: contain;\n scroll-snap-type: x mandatory;\n scrollbar-width: none;\n}\n\n.ds-carousel__track::-webkit-scrollbar {\n display: none;\n}\n\n/* Slides of their own width, several in view. */\n.ds-carousel[data-slides=\"auto\"] > .ds-carousel__track {\n grid-auto-columns: max-content;\n}\n\n.ds-carousel[data-snap=\"none\"] > .ds-carousel__track {\n scroll-snap-type: none;\n}\n\n/* To the window's end edge: the track reaches out over the room after the\n box and keeps a gap at its end, so the last slide stops short of the edge. */\n.ds-carousel[data-bleed=\"end\"] > .ds-carousel__track {\n margin-inline-end: calc(-1 * var(--ds-carousel-after, 0px));\n padding-inline-end: var(--ds-carousel-gap, var(--ds-unit));\n}\n\n/* To both edges: the track reaches out both ways, and the first slide, and\n each one it stops at, still stands on the box's start. */\n.ds-carousel[data-bleed=\"both\"] > .ds-carousel__track {\n margin-inline: calc(-1 * var(--ds-carousel-before, 0px)) calc(-1 * var(--ds-carousel-after, 0px));\n padding-inline: var(--ds-carousel-before, 0px) var(--ds-carousel-gap, var(--ds-unit));\n scroll-padding-inline-start: var(--ds-carousel-before, 0px);\n}\n\n.ds-carousel__track:focus-visible {\n outline: 2px solid var(--ds-focus-ring);\n outline-offset: 1px;\n}\n\n.ds-carousel__track > * {\n box-sizing: border-box;\n min-width: 0;\n margin: 0;\n scroll-snap-align: start;\n}\n\n/* One slide to a stop; a strip of several in view may pass a few. */\n.ds-carousel:not([data-slides=\"auto\"]) > .ds-carousel__track > * {\n scroll-snap-stop: always;\n}\n\n.ds-carousel__controls {\n display: flex;\n align-items: center;\n height: var(--ds-row-height);\n min-width: 0;\n}\n\n/* The buttons take a one-square cell each, as the dots do. Two classes, so\n the button's own reset of its margin does not undo it. */\n.ds-carousel .ds-carousel__previous,\n.ds-carousel .ds-carousel__next,\n.ds-carousel .ds-carousel__pause {\n flex: none;\n margin: calc((var(--ds-unit) - var(--ds-control-height)) / 2);\n}\n\n/* Previous points back along the reading direction. */\n.ds-carousel:dir(rtl) :is(.ds-carousel__previous, .ds-carousel__next) > * {\n transform: scaleX(-1);\n}\n\n.ds-carousel__status {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n}\n";
12090
+ //#endregion
12091
+ //#region DS1/3-unit/styles/ds-carousel.css?inline
12092
+ var ds_carousel_default = "/* ds-carousel.css */\n/* The element only places the parts; 1-root/carousel.css draws them and\n controls.css the buttons. The slides are the element's children, so the\n track's rules for its children are said again for slotted ones. */\n\n:host {\n display: block;\n min-width: 0;\n}\n\n:host([hidden]) {\n display: none !important;\n}\n\n::slotted(*) {\n box-sizing: border-box;\n flex: none;\n min-width: 0;\n margin: 0;\n scroll-snap-align: start;\n}\n\n/* One slide to a stop; a strip of slides of their own width may pass a few. */\n:host(:not([slide-width=\"auto\"])) ::slotted(*) {\n scroll-snap-stop: always;\n}\n\n:host(:dir(rtl)) :is(.ds-carousel__previous, .ds-carousel__next) > ds-icon {\n transform: scaleX(-1);\n}\n";
12093
+ //#endregion
12094
+ //#region DS1/3-unit/ds-carousel.ts
12095
+ var _Carousel;
12096
+ /** A whole number of squares, or `undefined` for a value that is not one. */
12097
+ var squares = (value, least) => typeof value === "number" && Number.isFinite(value) && value >= least ? Math.floor(value) : void 0;
11937
12098
  /**
11938
- * @element ds-date-picker
11939
- * @prop {string} value - `YYYY-MM-DD`, `YYYY-MM-DDTHH:MM`, or two joined by
11940
- * `/` for a range; empty for none.
11941
- * @prop {string} name - The form field's name.
11942
- * @prop {string} label - The field's accessible name: a translation key, or
11943
- * literal words.
11944
- * @prop {boolean} literal - Show `label` and `placeholder` as written.
11945
- * @prop {string} placeholder - The words before a day is chosen; the shared
11946
- * "Choose a date" by default.
11947
- * @prop {string} length - How a day is written: `short`, `medium` (the
11948
- * default) or `long`.
11949
- * @prop {boolean} time - Choose a time with each day (the Include time switch).
11950
- * @prop {boolean} noOptions - Leave the Include time switch out (`no-options`);
11951
- * `time` still applies.
11952
- * @prop {string} min - The first day a person may choose.
11953
- * @prop {string} max - The last day a person may choose.
11954
- * @prop {string} weekStart - The week's first day (`week-start`); the
11955
- * language's own by default.
11956
- * @prop {string} today - The day drawn as today; the reader's own by default.
11957
- * @prop {boolean} required - A form needs a value; Clear is left out.
11958
- * @prop {boolean} disabled - The field cannot be opened.
11959
- * @prop {boolean} open - The panel is open.
12099
+ * @element ds-carousel
12100
+ * @prop {string} label - The region's name: a translation key, or literal
12101
+ * words. Name what the slides show ("Colours", "What people say").
12102
+ * @prop {boolean} literal - Show `label` as written, never looked up.
12103
+ * @prop {number} current - The slide in view, from 0. Setting it scrolls there.
12104
+ * @prop {boolean} buttons - Show previous and next buttons beside the dots.
12105
+ * @prop {boolean} noDots - Leave the dots out (`no-dots`).
12106
+ * @prop {boolean} end - Draw the last dot as an end marker.
12107
+ * @prop {number | "auto"} slideWidth - Each slide's width in whole squares
12108
+ * (`slide-width`); the carousel's own width when not set; `auto` for slides
12109
+ * of their own width, a strip of several in view.
12110
+ * @prop {number} gap - Squares between slides; one by default.
12111
+ * @prop {string} bleed - `none` (the default) keeps the strip in its box;
12112
+ * `end` runs it on to the window's end edge, the first slide still on the
12113
+ * box's start; `both` runs it to both edges.
12114
+ * @prop {boolean} loop - Hold the slides three times over, so the strip has no
12115
+ * end. The copies are hidden from assistive technology and never focused;
12116
+ * a looping carousel draws no dots.
12117
+ * @prop {string} autoplay - Move on a slide at a time by itself: empty for
12118
+ * every five seconds, or a number of milliseconds. Held while a person is
12119
+ * in the strip or cannot see it, never under reduced motion, and always
12120
+ * with a pause button.
12121
+ * @prop {boolean} free - Slide freely, without stopping at slides.
11960
12122
  *
11961
- * @fires change - A person changed the value; `{ value, previous }`.
11962
- * @fires input - The same moment, for code listening as to a field.
11963
- * @fires ds-date-picker-toggle - The panel opened or closed; `{ open }`.
12123
+ * @slot - The slides, one element each.
12124
+ *
12125
+ * @fires change - A person moved to another slide: by swiping, scrolling, a
12126
+ * dot or a button. `detail` is `{ index, previous }`. Setting `current` or
12127
+ * calling `goTo()` does not fire it.
12128
+ *
12129
+ * The element is a region described as a carousel, named by `label`; each
12130
+ * slide is a group labelled "2 of 5" unless the page labels it, and the
12131
+ * current slide is announced politely when it changes. It moves on its own
12132
+ * only with `autoplay`. `bleed`, `loop` and `autoplay` are read when the
12133
+ * element first draws; put a new element in its place to change them.
11964
12134
  *
11965
12135
  * @example
11966
- * <ds-date-picker label="When" literal name="when" value="2026-10-21/2026-10-24"></ds-date-picker>
12136
+ * <ds-carousel label="Colours" slide-width="12" buttons>
12137
+ * <figure>…</figure>
12138
+ * <figure>…</figure>
12139
+ * </ds-carousel>
11967
12140
  */
11968
- var DatePicker = class extends i$2 {
11969
- constructor() {
11970
- super();
11971
- this.internals = typeof this.attachInternals === "function" ? this.attachInternals() : null;
11972
- this.initial = "";
11973
- this.shown = false;
11974
- this.focusMonth = false;
11975
- this.relabel = () => this.requestUpdate();
11976
- this.place = () => {
11977
- const panel = this.panel, trigger = this.trigger;
11978
- if (!this.shown || !panel || !trigger) return;
11979
- const rect = trigger.getBoundingClientRect();
11980
- const view = this.ownerDocument.defaultView;
11981
- const viewport = view.visualViewport;
11982
- const left = viewport?.offsetLeft ?? 0, top = viewport?.offsetTop ?? 0;
11983
- const width = viewport?.width ?? view.innerWidth, height = viewport?.height ?? view.innerHeight;
11984
- const box = panel.getBoundingClientRect();
11985
- const gap = (parseFloat(getComputedStyle(panel).paddingTop) || 9) + 1;
11986
- const square = Math.max(rect.height, gap * 2);
11987
- const foot = rect.bottom, head = rect.bottom - square;
11988
- const below = top + height - foot - 2 * gap, above = head - top - 2 * gap;
11989
- const up = below < box.height && above > below;
11990
- const x = getComputedStyle(this).direction === "rtl" ? rect.right - box.width : rect.left;
11991
- panel.style.left = `${Math.max(left + gap, Math.min(x, left + width - gap - box.width))}px`;
11992
- panel.style.top = `${Math.max(top + gap, up ? head - gap - box.height : foot + gap)}px`;
11993
- };
11994
- this.onOutside = (event) => {
11995
- if (!event.composedPath().includes(this)) this.open = false;
11996
- };
11997
- this.toggle = () => {
11998
- if (this.disabled) return;
11999
- this.focusMonth = false;
12000
- this.open = !this.open;
12001
- };
12002
- this.onTriggerKey = (event) => {
12003
- if (this.disabled) return;
12004
- if (event.key === "ArrowDown" || event.key === "ArrowUp" || (event.key === "Enter" || event.key === " ") && !this.open) {
12005
- event.preventDefault();
12006
- this.focusMonth = true;
12007
- if (this.open) this.focusDay();
12008
- else this.open = true;
12009
- }
12141
+ var Carousel = class extends i$2 {
12142
+ constructor() {
12143
+ super();
12144
+ this.drive = null;
12145
+ this.own = /* @__PURE__ */ new Map();
12146
+ this.frame = 0;
12147
+ this.moving = false;
12148
+ this.relabel = () => {
12149
+ this.labelSlides();
12150
+ this.requestUpdate();
12010
12151
  };
12011
- this.onPanelKey = (event) => {
12012
- if (event.key === "Escape" && !event.defaultPrevented) {
12013
- event.preventDefault();
12014
- event.stopPropagation();
12015
- this.open = false;
12016
- }
12152
+ this.scrolled = () => {
12153
+ cancelAnimationFrame(this.frame);
12154
+ if (this.loop) return;
12155
+ this.frame = requestAnimationFrame(() => {
12156
+ if (this.pending !== void 0) {
12157
+ clearTimeout(this.settle);
12158
+ this.settle = setTimeout(() => {
12159
+ this.pending = void 0;
12160
+ }, 150);
12161
+ return;
12162
+ }
12163
+ const track = this.track;
12164
+ if (track) this.show(nearestSlide(track, this.slides, this.index));
12165
+ });
12017
12166
  };
12018
- this.onFocusOut = (event) => {
12019
- const next = event.relatedTarget;
12020
- if (this.open && next instanceof Node && !this.contains(next) && !this.renderRoot.contains(next)) this.open = false;
12167
+ this.ended = () => {
12168
+ this.pending = void 0;
12021
12169
  };
12022
12170
  this.picked = (event) => {
12023
12171
  event.stopPropagation();
12024
- const { start, end } = this.ends;
12025
- const first = {
12026
- day: event.detail.start,
12027
- time: start?.time || (this.time ? "09:00" : "")
12028
- };
12029
- if (!event.detail.end) {
12030
- this.awaitingEnd = true;
12031
- this.write(first, void 0);
12032
- return;
12033
- }
12034
- this.awaitingEnd = false;
12035
- const last = {
12036
- day: event.detail.end,
12037
- time: end?.time || (this.time ? "10:00" : "")
12038
- };
12039
- this.write(first, last);
12040
- if (!this.time) this.open = false;
12041
- };
12042
- this.setTime = () => {
12043
- this.time = !this.time;
12044
- const { start, end } = this.ends;
12045
- const timed = (side, clock) => side ? {
12046
- ...side,
12047
- time: this.time ? side.time || clock : ""
12048
- } : void 0;
12049
- this.write(timed(start, "09:00"), timed(end ?? (this.time ? start : void 0), "10:00"));
12050
- };
12051
- this.passPress = (event) => {
12052
- const control = event.currentTarget.querySelector(".ds-switch");
12053
- if (control && !event.composedPath().includes(control)) control.click();
12054
- };
12055
- this.clear = () => {
12056
- this.commit("");
12057
- this.open = false;
12172
+ event.preventDefault();
12173
+ this.move(event.detail.index);
12058
12174
  };
12059
- this.value = "";
12060
12175
  this.literal = false;
12061
- this.time = false;
12062
- this.noOptions = false;
12063
- this.required = false;
12064
- this.disabled = false;
12065
- this.open = false;
12066
- this.awaitingEnd = false;
12176
+ this.current = 0;
12177
+ this.buttons = false;
12178
+ this.noDots = false;
12179
+ this.end = false;
12180
+ this.bleed = "none";
12181
+ this.loop = false;
12182
+ this.autoplay = null;
12183
+ this.free = false;
12184
+ this.count = 0;
12185
+ this.announcement = "";
12186
+ this.paused = false;
12187
+ }
12188
+ /** The slides: the elements given to the carousel, without a loop's copies. */
12189
+ get slides() {
12190
+ return [...this.children].filter((child) => child instanceof HTMLElement && !child.hasAttribute("slot") && !child.hasAttribute("data-ds-carousel-copy"));
12191
+ }
12192
+ /** Milliseconds between autoplay steps; 0 for none. */
12193
+ get every() {
12194
+ return autoplayInterval(this.autoplay);
12195
+ }
12196
+ /** Slides of their own width: a strip. */
12197
+ get strip() {
12198
+ return this.slideWidth === "auto";
12199
+ }
12200
+ get track() {
12201
+ return this.renderRoot.querySelector(".ds-carousel__track");
12202
+ }
12203
+ /** The slide in view, inside the count. */
12204
+ get index() {
12205
+ const current = Number.isFinite(this.current) ? Math.floor(this.current) : 0;
12206
+ return Math.max(0, Math.min(this.count - 1, current));
12067
12207
  }
12068
12208
  connectedCallback() {
12069
12209
  super.connectedCallback();
@@ -12073,330 +12213,220 @@ var DatePicker = class extends i$2 {
12073
12213
  disconnectedCallback() {
12074
12214
  window.removeEventListener("language-changed", this.relabel);
12075
12215
  window.removeEventListener("translations-loaded", this.relabel);
12076
- this.hide(false);
12216
+ cancelAnimationFrame(this.frame);
12217
+ clearTimeout(this.settle);
12218
+ this.drive?.stop();
12219
+ this.drive = null;
12077
12220
  super.disconnectedCallback();
12078
12221
  }
12079
- firstUpdated() {
12080
- this.initial = this.value;
12081
- }
12082
- /** The form was reset: back to the value it started with. */
12083
- formResetCallback() {
12084
- this.value = this.initial;
12085
- }
12086
- formDisabledCallback(disabled) {
12087
- this.disabled = disabled;
12088
- }
12089
- words(value, fallback) {
12090
- if (value && this.literal) return value;
12091
- return getText(value || fallback) || value || fallback;
12092
- }
12093
- get trigger() {
12094
- return this.renderRoot.querySelector(".trigger");
12095
- }
12096
- get panel() {
12097
- return this.renderRoot.querySelector(".popover");
12098
- }
12099
- get calendar() {
12100
- return this.renderRoot.querySelector("ds-calendar");
12101
- }
12102
- get ends() {
12103
- return readDateValue(this.value);
12104
- }
12105
- willUpdate(changed) {
12106
- super.willUpdate(changed);
12107
- if (changed.has("value") && this.value) {
12108
- const { start, end } = this.ends;
12109
- if (!start) this.value = "";
12110
- else if (start.time || end?.time) this.time = true;
12111
- }
12112
- if (changed.has("disabled") && this.disabled) this.open = false;
12113
- }
12114
- updated(changed) {
12115
- super.updated(changed);
12116
- if (changed.has("value") || changed.has("required")) {
12117
- this.internals?.setFormValue(this.value || null);
12118
- if (this.required && !this.value) this.internals?.setValidity({ valueMissing: true }, this.words(void 0, "Choose a date"), this.trigger ?? void 0);
12119
- else this.internals?.setValidity({});
12120
- }
12121
- if (changed.has("open")) if (this.open) this.show();
12122
- else this.hide(changed.get("open") === true);
12123
- }
12124
- /** Opens the panel. */
12125
- show() {
12126
- if (this.shown || this.disabled || !this.isConnected) return;
12127
- const panel = this.panel;
12128
- if (!panel) return;
12129
- for (const other of openPickers) if (other !== this) other.open = false;
12130
- openPickers.add(this);
12131
- this.shown = true;
12132
- this.open = true;
12133
- this.awaitingEnd = false;
12134
- const calendar = this.calendar;
12135
- if (calendar) calendar.month = (this.ends.start?.day || this.today || today()).slice(0, 7);
12136
- panel.showPopover?.();
12137
- this.place();
12138
- if (calendar) calendar.updateComplete.then(this.place);
12139
- this.ownerDocument.addEventListener("pointerdown", this.onOutside, true);
12140
- this.ownerDocument.addEventListener("scroll", this.place, true);
12141
- window.addEventListener("resize", this.place);
12142
- this.dispatchEvent(new CustomEvent("ds-date-picker-toggle", {
12143
- bubbles: true,
12144
- composed: true,
12145
- detail: { open: true }
12146
- }));
12147
- if (this.focusMonth) this.focusDay();
12148
- }
12149
- /** Closes the panel, giving focus back to the field when it was inside. */
12150
- hide(restore = true) {
12151
- if (!this.shown) return;
12152
- this.shown = false;
12153
- openPickers.delete(this);
12154
- this.awaitingEnd = false;
12155
- const panel = this.panel;
12156
- const within = panel?.matches(":focus-within");
12157
- if (panel?.matches(":popover-open")) panel.hidePopover?.();
12158
- this.ownerDocument.removeEventListener("pointerdown", this.onOutside, true);
12159
- this.ownerDocument.removeEventListener("scroll", this.place, true);
12160
- window.removeEventListener("resize", this.place);
12161
- this.open = false;
12162
- if (restore && within) this.trigger?.focus({ preventScroll: true });
12163
- this.dispatchEvent(new CustomEvent("ds-date-picker-toggle", {
12164
- bubbles: true,
12165
- composed: true,
12166
- detail: { open: false }
12167
- }));
12168
- }
12169
- async focusDay() {
12170
- const calendar = this.calendar;
12171
- if (!calendar) return;
12172
- await calendar.updateComplete;
12173
- calendar.renderRoot.querySelector("[data-day][tabindex=\"0\"]")?.focus({ preventScroll: true });
12174
- }
12175
- /** Takes a new value and says so. */
12176
- commit(value) {
12177
- const previous = this.value || "";
12178
- if (value === previous) return;
12179
- this.value = value;
12180
- const detail = {
12181
- value,
12182
- previous
12183
- };
12184
- this.dispatchEvent(new CustomEvent("input", {
12185
- bubbles: true,
12186
- composed: true,
12187
- detail
12188
- }));
12189
- this.dispatchEvent(new CustomEvent("change", {
12190
- bubbles: true,
12191
- composed: true,
12192
- detail
12193
- }));
12194
- }
12195
- write(start, end) {
12196
- this.commit(writeDateValue(start, end, { time: this.time }));
12222
+ words(value, fallback, params) {
12223
+ const text = value && this.literal ? value : getText(value || fallback, params) || value || fallback;
12224
+ return params ? text.replace(/\{\{?(\w+)\}?\}/g, (whole, name) => name in params ? String(params[name]) : whole) : text;
12197
12225
  }
12198
- /** A day typed in a field, read when the person leaves it or presses Enter. */
12199
- typed(side, input) {
12200
- const { start, end } = this.ends;
12201
- const text = input.value.trim();
12202
- if (!text) {
12203
- if (side === "start") this.commit("");
12204
- else this.write(start, void 0);
12205
- return;
12206
- }
12207
- const day = parseTypedDay(text);
12208
- if (!day) {
12209
- input.value = this.fieldText(side === "start" ? start : end);
12210
- return;
12211
- }
12212
- const next = {
12213
- day,
12214
- time: (side === "start" ? start : end)?.time || (this.time ? side === "start" ? "09:00" : "10:00" : "")
12215
- };
12216
- if (side === "start") this.write(next, end);
12217
- else this.write(start ?? next, next);
12218
- this.awaitingEnd = false;
12219
- const calendar = this.calendar;
12220
- if (calendar) calendar.month = day.slice(0, 7);
12226
+ /** Writes an attribute the page has not written, and remembers it as this element's. */
12227
+ write(element, name, value) {
12228
+ const mine = this.own.get(element) ?? /* @__PURE__ */ new Set();
12229
+ if (element.hasAttribute(name) && !mine.has(name)) return;
12230
+ mine.add(name);
12231
+ this.own.set(element, mine);
12232
+ if (element.getAttribute(name) !== value) element.setAttribute(name, value);
12221
12233
  }
12222
- timed(side, input) {
12223
- const { start, end } = this.ends;
12224
- const clock = TIME.test(input.value) ? input.value : "";
12225
- if (side === "start" && start) this.write({
12226
- ...start,
12227
- time: clock
12228
- }, end);
12229
- if (side === "end" && start) this.write(start, {
12230
- day: (end ?? start).day,
12231
- time: clock
12234
+ labelSlides() {
12235
+ const slides = this.slides;
12236
+ const labelled = (slide) => slide.hasAttribute("aria-labelledby");
12237
+ slides.forEach((slide, index) => {
12238
+ this.write(slide, "role", "group");
12239
+ this.write(slide, "aria-roledescription", this.words(void 0, "Slide"));
12240
+ if (!labelled(slide)) this.write(slide, "aria-label", this.words(void 0, "{index} of {count}", {
12241
+ index: index + 1,
12242
+ count: slides.length
12243
+ }));
12232
12244
  });
12245
+ this.count = slides.length;
12233
12246
  }
12234
- moment(side) {
12235
- const [year, month, date] = side.day.split("-").map(Number);
12236
- const [hours, minutes] = (side.time || "12:00").split(":").map(Number);
12237
- return Date.UTC(year, month - 1, date, hours, minutes);
12247
+ willUpdate(changed) {
12248
+ super.willUpdate(changed);
12249
+ if (!this.hasUpdated) this.labelSlides();
12250
+ this.write(this, "role", "region");
12251
+ this.write(this, "aria-roledescription", this.words(void 0, "Carousel"));
12252
+ if (!this.hasAttribute("aria-labelledby")) this.write(this, "aria-label", this.words(this.label, "Carousel"));
12253
+ const width = squares(typeof this.slideWidth === "string" && this.slideWidth !== "auto" ? Number(this.slideWidth) : typeof this.slideWidth === "number" ? this.slideWidth : void 0, 1);
12254
+ const gap = squares(this.gap, 0);
12255
+ if (this.strip) this.style.setProperty("--ds-carousel-slide", "max-content");
12256
+ else if (width) this.style.setProperty("--ds-carousel-slide", `calc(var(--ds-unit) * ${width})`);
12257
+ else this.style.removeProperty("--ds-carousel-slide");
12258
+ if (gap !== void 0) this.style.setProperty("--ds-carousel-gap", `calc(var(--ds-unit) * ${gap})`);
12259
+ else this.style.removeProperty("--ds-carousel-gap");
12238
12260
  }
12239
- formatter(withTime) {
12240
- return dateFormatter({
12241
- dateStyle: this.length ?? "medium",
12242
- ...withTime ? { timeStyle: "short" } : {},
12243
- timeZone: "UTC"
12261
+ firstUpdated() {
12262
+ const track = this.track;
12263
+ const bleed = bleedOf(this.bleed);
12264
+ if (track && (bleed !== "none" || this.loop || this.every > 0)) this.drive = driveCarousel(this, track, this, {
12265
+ bleed,
12266
+ loop: this.loop,
12267
+ autoplay: this.every
12244
12268
  });
12245
12269
  }
12246
- fieldText(side) {
12247
- return side ? this.formatter(false).format(this.moment(side)) : "";
12270
+ updated(changed) {
12271
+ super.updated(changed);
12272
+ if (this.loop) return;
12273
+ if (changed.has("current")) {
12274
+ if (this.moving) {
12275
+ this.moving = false;
12276
+ return;
12277
+ }
12278
+ this.travel(this.index, changed.get("current") !== void 0);
12279
+ } else if (changed.has("count") && !changed.get("count") && this.index > 0) this.travel(this.index, false);
12248
12280
  }
12249
- /** The face's words: one day, or a range written as the language writes one ("Oct 21 – 24, 2026"). */
12250
- faceText() {
12251
- const { start, end } = this.ends;
12252
- if (!start) return this.words(this.placeholder, "Choose a date");
12253
- const formatter = this.formatter(Boolean(start.time || end?.time));
12254
- if (!end) return formatter.format(this.moment(start));
12255
- return formatter.formatRange ? formatter.formatRange(this.moment(start), this.moment(end)) : `${formatter.format(this.moment(start))} – ${formatter.format(this.moment(end))}`;
12281
+ /** Goes to a slide: smoothly, unless the reader asks for reduced motion. Fires no `change`. */
12282
+ goTo(index) {
12283
+ this.current = index;
12256
12284
  }
12257
- renderEnd(side, value, name) {
12258
- const label = side === "start" ? name : `${name}, ${this.words(void 0, "End date")}`;
12259
- return b$1`<div class="field-row" part="field">
12260
- <input
12261
- class="ds-input field"
12262
- type="text"
12263
- autocomplete="off"
12264
- aria-label=${label}
12265
- placeholder=${side === "start" ? this.words(this.placeholder, "Choose a date") : this.words(void 0, "End date")}
12266
- .value=${this.fieldText(value)}
12267
- @change=${(event) => this.typed(side, event.target)}
12268
- @keydown=${(event) => {
12269
- if (event.key === "Enter") {
12270
- event.preventDefault();
12271
- this.typed(side, event.target);
12285
+ /** Goes to the next slide, as the next button does. */
12286
+ next() {
12287
+ if (this.drive) this.drive.step(1);
12288
+ else this.move(this.index + 1);
12289
+ }
12290
+ /** Goes to the previous slide, as the previous button does. */
12291
+ previous() {
12292
+ if (this.drive) this.drive.step(-1);
12293
+ else this.move(this.index - 1);
12294
+ }
12295
+ togglePause() {
12296
+ this.paused = !this.paused;
12297
+ this.drive?.pause(this.paused);
12298
+ }
12299
+ travel(index, smooth) {
12300
+ const track = this.track;
12301
+ const slide = this.slides[index];
12302
+ if (!track || !slide || !scrollToSlide(track, slide, smooth)) return;
12303
+ this.pending = index;
12304
+ clearTimeout(this.settle);
12305
+ this.settle = setTimeout(() => {
12306
+ this.pending = void 0;
12307
+ }, 600);
12308
+ }
12309
+ /** A person moved to `index`: show it, say it, and fire `change`. */
12310
+ show(index) {
12311
+ const target = Math.max(0, Math.min(this.count - 1, index));
12312
+ const previous = this.index;
12313
+ if (target === previous || this.count === 0) return;
12314
+ this.moving = true;
12315
+ this.current = target;
12316
+ this.announcement = this.words(void 0, "{index} of {count}", {
12317
+ index: target + 1,
12318
+ count: this.count
12319
+ });
12320
+ this.dispatchEvent(new CustomEvent("change", {
12321
+ bubbles: true,
12322
+ composed: true,
12323
+ detail: {
12324
+ index: target,
12325
+ previous
12272
12326
  }
12273
- }}
12274
- />
12275
- </div>
12276
- ${this.time ? b$1`<div class="field-row" part="field">
12277
- <input
12278
- class="ds-input clock"
12279
- type="time"
12280
- aria-label=${`${label}, ${this.words(void 0, "Time")}`}
12281
- .value=${value?.time ?? ""}
12282
- ?disabled=${!this.ends.start}
12283
- @change=${(event) => this.timed(side, event.target)}
12284
- />
12285
- </div>` : A}`;
12327
+ }));
12328
+ }
12329
+ move(index) {
12330
+ const target = Math.max(0, Math.min(this.count - 1, index));
12331
+ this.travel(target, true);
12332
+ this.show(target);
12286
12333
  }
12287
12334
  render() {
12288
- const { start, end } = this.ends;
12289
- const text = this.faceText();
12290
- const name = this.label ? this.words(this.label, this.label) : this.words(void 0, "Date");
12291
- const days = start ? this.awaitingEnd ? start.day : `${start.day}/${(end ?? start).day}` : "";
12292
- return b$1`<button
12293
- class="ds-select trigger"
12294
- part="trigger"
12295
- type="button"
12296
- aria-haspopup="dialog"
12297
- aria-expanded=${String(this.open)}
12298
- aria-label=${start ? `${name}, ${text}` : name}
12299
- ?disabled=${this.disabled}
12300
- ?data-empty=${!start}
12301
- @click=${this.toggle}
12302
- @keydown=${this.onTriggerKey}
12303
- >
12304
- <ds-icon class="icon" type="calendar" aria-hidden="true"></ds-icon>
12305
- <span class="text">${text}</span>
12306
- <ds-icon class="arrow" type="down" aria-hidden="true"></ds-icon>
12307
- </button>
12308
- <div
12309
- class="popover"
12310
- part="popover"
12311
- popover="manual"
12312
- role="dialog"
12313
- aria-label=${name}
12314
- @keydown=${this.onPanelKey}
12315
- @focusout=${this.onFocusOut}
12316
- >
12317
- <div class="fields">
12318
- ${this.renderEnd("start", start, name)}
12319
- ${this.renderEnd("end", start ? end ?? start : void 0, name)}
12320
- </div>
12321
- <ds-calendar
12322
- compact
12323
- selectable
12324
- range
12325
- fixed
12326
- outside-days
12327
- .value=${days}
12328
- .min=${this.min}
12329
- .max=${this.max}
12330
- .weekStart=${this.weekStart}
12331
- .today=${this.today}
12332
- @change=${this.picked}
12333
- ></ds-calendar>
12334
- <div class="options" part="options">
12335
- ${this.noOptions ? A : b$1`<div class="separator" aria-hidden="true"></div>
12336
- <div class="option" @click=${this.passPress}>
12337
- <span>${this.words(void 0, "Include time")}</span>
12338
- <button class="ds-switch" type="button" role="switch" aria-label=${this.words(void 0, "Include time")} aria-checked=${String(this.time)} @click=${this.setTime}></button>
12339
- </div>`}
12340
- ${this.required ? A : b$1`<div class="separator" aria-hidden="true"></div>
12341
- <button class="option clear" part="clear" type="button" ?disabled=${!start} @click=${this.clear}>${this.words(void 0, "Clear")}</button>`}
12342
- </div>
12343
- </div>`;
12335
+ const index = this.index;
12336
+ const driven = this.drive !== null || bleedOf(this.bleed) !== "none" || this.loop || this.every > 0;
12337
+ const button = (which) => b$1`<button
12338
+ class="ds-button ds-button--icon ds-carousel__${which}"
12339
+ part=${which}
12340
+ type="button"
12341
+ aria-label=${this.words(void 0, which === "previous" ? "Previous slide" : "Next slide")}
12342
+ ?disabled=${!driven && (which === "previous" ? index <= 0 : index >= this.count - 1)}
12343
+ @click=${() => which === "previous" ? this.previous() : this.next()}
12344
+ ><ds-icon type=${which === "previous" ? "left" : "right"} aria-hidden="true"></ds-icon></button>`;
12345
+ const moving = this.every > 0 && !prefersReducedMotion();
12346
+ const dots = !this.noDots && !this.loop;
12347
+ const controls = this.buttons || dots || moving;
12348
+ const bleed = bleedOf(this.bleed);
12349
+ return b$1`<div class="ds-carousel" part="carousel" data-slides=${this.strip ? "auto" : A} data-bleed=${bleed === "none" ? A : bleed} data-snap=${this.free ? "none" : A}>
12350
+ <div class="ds-carousel__track" part="track" @scroll=${this.scrolled} @scrollend=${this.ended}>
12351
+ <slot @slotchange=${() => this.labelSlides()}></slot>
12352
+ </div>
12353
+ ${controls && this.count > 1 ? b$1`<div class="ds-carousel__controls" part="controls">
12354
+ ${moving ? b$1`<button
12355
+ class="ds-button ds-button--icon ds-carousel__pause"
12356
+ part="pause"
12357
+ type="button"
12358
+ aria-pressed=${String(this.paused)}
12359
+ aria-label=${this.words(void 0, this.paused ? "Play" : "Pause")}
12360
+ @click=${this.togglePause}
12361
+ ><ds-icon type=${this.paused ? "play-alt" : "pause"} aria-hidden="true"></ds-icon></button>` : A}
12362
+ ${this.buttons ? button("previous") : A}
12363
+ ${dots ? b$1`<ds-progress part="dots" .dots=${this.count} .active=${index} ?end=${this.end} @select=${this.picked}></ds-progress>` : A}
12364
+ ${this.buttons ? button("next") : A}
12365
+ </div>` : A}
12366
+ <span class="ds-carousel__status" part="status" aria-live="polite" aria-atomic="true">${this.announcement}</span>
12367
+ </div>`;
12344
12368
  }
12345
12369
  };
12346
- _DatePicker = DatePicker;
12347
- _DatePicker.formAssociated = true;
12348
- _DatePicker.properties = {
12349
- value: {
12350
- type: String,
12370
+ _Carousel = Carousel;
12371
+ _Carousel.properties = {
12372
+ label: { type: String },
12373
+ literal: {
12374
+ type: Boolean,
12351
12375
  reflect: true
12352
12376
  },
12353
- name: {
12354
- type: String,
12377
+ current: {
12378
+ type: Number,
12355
12379
  reflect: true
12356
12380
  },
12357
- label: { type: String },
12358
- literal: {
12381
+ buttons: {
12359
12382
  type: Boolean,
12360
12383
  reflect: true
12361
12384
  },
12362
- placeholder: { type: String },
12363
- length: { type: String },
12364
- time: {
12385
+ noDots: {
12365
12386
  type: Boolean,
12366
- reflect: true
12387
+ reflect: true,
12388
+ attribute: "no-dots"
12367
12389
  },
12368
- noOptions: {
12390
+ end: {
12369
12391
  type: Boolean,
12370
- attribute: "no-options",
12371
12392
  reflect: true
12372
12393
  },
12373
- min: { type: String },
12374
- max: { type: String },
12375
- weekStart: {
12394
+ slideWidth: {
12395
+ attribute: "slide-width",
12396
+ reflect: true,
12397
+ converter: {
12398
+ fromAttribute: (value) => value === null ? void 0 : value === "auto" ? "auto" : Number(value),
12399
+ toAttribute: (value) => value === void 0 ? null : String(value)
12400
+ }
12401
+ },
12402
+ gap: { type: Number },
12403
+ bleed: {
12376
12404
  type: String,
12377
- attribute: "week-start"
12405
+ reflect: true
12378
12406
  },
12379
- today: { type: String },
12380
- required: {
12407
+ loop: {
12381
12408
  type: Boolean,
12382
12409
  reflect: true
12383
12410
  },
12384
- disabled: {
12385
- type: Boolean,
12411
+ autoplay: {
12412
+ type: String,
12386
12413
  reflect: true
12387
12414
  },
12388
- open: {
12415
+ free: {
12389
12416
  type: Boolean,
12390
12417
  reflect: true
12391
12418
  },
12392
- awaitingEnd: { state: true }
12419
+ count: { state: true },
12420
+ announcement: { state: true },
12421
+ paused: { state: true }
12393
12422
  };
12394
- _DatePicker.styles = [
12423
+ _Carousel.styles = [
12395
12424
  r$3(component_metrics_default),
12396
12425
  r$3(controls_default),
12397
- r$3(ds_date_picker_default)
12426
+ r$3(carousel_default),
12427
+ r$3(ds_carousel_default)
12398
12428
  ];
12399
- define("ds-date-picker", DatePicker);
12429
+ define("ds-carousel", Carousel);
12400
12430
  //#endregion
12401
12431
  //#region DS1/3-unit/ds-form.ts
12402
12432
  var _Form;
@@ -16539,6 +16569,6 @@ _Image.properties = {
16539
16569
  _Image.styles = r$3(ds_image_default);
16540
16570
  define("ds-image", Image);
16541
16571
  //#endregion
16542
- export { Accordion, Avatar, AvatarGroup, Badge, Button, COMPONENT_VISIBILITY, CONTENTS_LABEL, Calendar, CalendarBase, CalendarLarge, Card, Carousel, ChartElement, Checkbox, ColorInput, Composer, Container, Context, Cycle, DEFAULT_LANGUAGE, DatePicker, DocumentPage, DsTable, EMPTY_HEADING, Empty, FALLBACK_LANGUAGE, FilePicker, Footer, Form, Gap, Grid, GridBars, GridChart, GridLineChart, GridRange, GridRatio, GridSpark, Header, Icon, Image, Input, LANGUAGES, LANGUAGE_METADATA, LOCALE_TAGS, LONG_PRESS_DELAY, List, MENU_LABEL, MESSAGE_ICONS, Matrix, Menu, Message, NOTICE_ICONS, Notice, Overlay, PAGE_LAYOUTS, Page, Panel, Progress, Radio, Row, SUPPORTED_LANGUAGES, Select, Series, Sidebar, Stat, Swatch, Switch, THEME_MODES, TOUCH_TOOLTIP_DURATION, Tabs, Tag, Text, Toaster, Token, TokenField, Tooltip, applike, applyScaling, ariaKeyShortcuts, avatarInk, calculateScalingFactor, columnLetter, componentVisibility, configureFormat, configureI18n, configureLanguageRouting, connectSidebar, createChart, currencyDigits, currencySymbol, currentLanguage, currentTheme, currentThemeMode, dateFormatter, detectMobileDevice, documentLevels, drawSparkline, englishText, fallbackLanguages, fillPlaceholders, followSystemTheme, footerPreferences, formatCompact, formatDate, formatDateTime, formatList, formatMoney, formatMoneyToParts, formatMonthDay, formatMonthDayTime, formatMonthYear, formatNumber, formatPercent, formatPrice, formatRelativeTime, formatTime, fromMinorUnits, getAvailableLanguages, getAvailableLanguagesSync, getBrowserLanguage, getDeviceInfo, getLanguageDisplayName, getLanguageRoutingOptions, getPriceCurrency, getPriceLabel, getScalingFactor, getText, getThemeMode, hasTranslation, headingId, hideTooltip, initDeviceDetection, initGridShortcut, initTooltips, isApplePlatform, isLanguage, isLanguageSlug, isModKey, isPublicComponent, isTranslationsFile, isTyping, keyWord, languageFromSlug, languageOrder, languageSlug, listFormatter, loadLanguage, loadSharedWords, loadTranslations, localeTag, matchesShortcut, measureStyle, toast as message, toast, modKey, nativeLanguageName, normalizeLanguage, numberFormatter, onShortcut, orderColumns, parseLanguagePath, parseShortcut, parseTypedDay, pickLanguages, placeholdersIn, preferenceStorageKey, provideIcons, readDateValue, readLanguageFile, readMonth, readRange, relativeTimeFormatter, relativeTimeUnit, resolveText, savePreferences, scale, scalingConfig, scalingFactor, setKeyWords, setLanguage, setScalingConfig, setTheme, setThemeMode, shortcutLabel, showTooltip, startScaling, stepStates, stripLanguagePrefix, textInLanguage, themeIconSvg, toDevicePixel, toLayoutStep, toMinorUnits, translate, treeStep, unscale, updateScalingFactor, validateTranslations, withLanguagePrefix, writeDateValue };
16572
+ export { Accordion, Avatar, AvatarGroup, Badge, Bento, Button, COMPONENT_VISIBILITY, CONTENTS_LABEL, Calendar, CalendarBase, CalendarLarge, Card, Carousel, ChartElement, Checkbox, ColorInput, Composer, Container, Context, Cycle, DEFAULT_LANGUAGE, DocumentPage, DsTable, EMPTY_HEADING, Empty, FALLBACK_LANGUAGE, FilePicker, Footer, Form, Gap, Grid, GridBars, GridChart, GridLineChart, GridRange, GridRatio, GridSpark, Header, Icon, Image, Input, LANGUAGES, LANGUAGE_METADATA, LOCALE_TAGS, LONG_PRESS_DELAY, List, MENU_LABEL, MESSAGE_ICONS, Matrix, Menu, Message, NOTICE_ICONS, Notice, Overlay, PAGE_LAYOUTS, Page, Panel, Progress, Radio, Row, SUPPORTED_LANGUAGES, Select, Series, Sidebar, Stat, Swatch, Switch, THEME_MODES, TOUCH_TOOLTIP_DURATION, Tabs, Tag, Text, Toaster, Token, TokenField, Tooltip, applike, applyScaling, ariaKeyShortcuts, avatarInk, calculateScalingFactor, columnLetter, componentVisibility, configureFormat, configureI18n, configureLanguageRouting, connectSidebar, createChart, currencyDigits, currencySymbol, currentLanguage, currentTheme, currentThemeMode, dateFormatter, detectMobileDevice, documentLevels, drawSparkline, englishText, fallbackLanguages, fillPlaceholders, followSystemTheme, footerPreferences, formatCompact, formatDate, formatDateTime, formatList, formatMoney, formatMoneyToParts, formatMonthDay, formatMonthDayTime, formatMonthYear, formatNumber, formatPercent, formatPrice, formatRelativeTime, formatTime, fromMinorUnits, getAvailableLanguages, getAvailableLanguagesSync, getBrowserLanguage, getDeviceInfo, getLanguageDisplayName, getLanguageRoutingOptions, getPriceCurrency, getPriceLabel, getScalingFactor, getText, getThemeMode, hasTranslation, headingId, hideTooltip, initDeviceDetection, initGridShortcut, initTooltips, isApplePlatform, isLanguage, isLanguageSlug, isModKey, isPublicComponent, isTranslationsFile, isTyping, keyWord, languageFromSlug, languageOrder, languageSlug, listFormatter, loadLanguage, loadSharedWords, loadTranslations, localeTag, matchesShortcut, measureStyle, toast as message, toast, modKey, nativeLanguageName, normalizeLanguage, numberFormatter, onShortcut, orderColumns, parseLanguagePath, parseShortcut, parseTypedDay, pickLanguages, placeholdersIn, preferenceStorageKey, provideIcons, readDateValue, readLanguageFile, readMonth, readRange, relativeTimeFormatter, relativeTimeUnit, resolveText, savePreferences, scale, scalingConfig, scalingFactor, setKeyWords, setLanguage, setScalingConfig, setTheme, setThemeMode, shortcutLabel, showTooltip, startScaling, stepStates, stripLanguagePrefix, textInLanguage, themeIconSvg, toDevicePixel, toLayoutStep, toMinorUnits, translate, treeStep, unscale, updateScalingFactor, validateTranslations, withLanguagePrefix, writeDateValue };
16543
16573
 
16544
16574
  //# sourceMappingURL=design-system-1.bundle.js.map