@krizaka/ui 3.51.0 → 3.53.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (251) hide show
  1. package/dist/action-bar.js +1 -1
  2. package/dist/argument-card.js +4 -4
  3. package/dist/blocks.css +293 -0
  4. package/dist/chapter.d.ts +89 -0
  5. package/dist/chapter.js +123 -0
  6. package/dist/chapter.js.map +1 -0
  7. package/dist/choice-grid.js +1 -1
  8. package/dist/choice-grid.js.map +1 -1
  9. package/dist/{chunk-IBDTAMPF.js → chunk-7JG3VR4C.js} +2 -2
  10. package/dist/{chunk-C7LTF5CC.js → chunk-ENOS6ETI.js} +2 -2
  11. package/dist/{chunk-C7LTF5CC.js.map → chunk-ENOS6ETI.js.map} +1 -1
  12. package/dist/{chunk-2AGSO3WH.js → chunk-FVGFPB73.js} +2 -2
  13. package/dist/{chunk-2AGSO3WH.js.map → chunk-FVGFPB73.js.map} +1 -1
  14. package/dist/chunk-YATTFMDV.js +45 -0
  15. package/dist/chunk-YATTFMDV.js.map +1 -0
  16. package/dist/cover-hero.js +1 -1
  17. package/dist/glossary-term.d.ts +12 -0
  18. package/dist/glossary-term.js +16 -0
  19. package/dist/glossary-term.js.map +1 -0
  20. package/dist/glossary.d.ts +20 -0
  21. package/dist/glossary.js +17 -0
  22. package/dist/glossary.js.map +1 -0
  23. package/dist/index.js +7 -7
  24. package/dist/kpi-explained.d.ts +66 -0
  25. package/dist/kpi-explained.js +84 -0
  26. package/dist/kpi-explained.js.map +1 -0
  27. package/dist/link-card.js +2 -2
  28. package/dist/media-grid.js +1 -1
  29. package/dist/media-grid.js.map +1 -1
  30. package/dist/next-actions.d.ts +27 -0
  31. package/dist/next-actions.js +27 -0
  32. package/dist/next-actions.js.map +1 -0
  33. package/dist/page-hero.js +1 -1
  34. package/dist/page-index.js +1 -1
  35. package/dist/service-card.js +1 -1
  36. package/dist/slot-picker.js +1 -1
  37. package/dist/text-style/server.d.ts +63 -0
  38. package/dist/text-style/server.js +109 -0
  39. package/dist/text-style/server.js.map +1 -0
  40. package/dist/text-style.d.ts +149 -0
  41. package/dist/text-style.js +399 -0
  42. package/dist/text-style.js.map +1 -0
  43. package/dist/tooltip.js +5 -37
  44. package/dist/tooltip.js.map +1 -1
  45. package/package.json +86 -8
  46. package/registry/account-menu.json +1 -1
  47. package/registry/account-settings.json +1 -1
  48. package/registry/achievement.json +1 -1
  49. package/registry/action-bar.json +2 -2
  50. package/registry/activity-feed.json +1 -1
  51. package/registry/agenda.json +1 -1
  52. package/registry/alert.json +1 -1
  53. package/registry/anatomy-plate.json +1 -1
  54. package/registry/app-header.json +1 -1
  55. package/registry/app-shell.json +1 -1
  56. package/registry/argument-card.json +2 -2
  57. package/registry/audio-player.json +1 -1
  58. package/registry/auth-layout.json +1 -1
  59. package/registry/avatar.json +1 -1
  60. package/registry/badge.json +1 -1
  61. package/registry/bird-plate.json +1 -1
  62. package/registry/button.json +1 -1
  63. package/registry/captcha.json +1 -1
  64. package/registry/card-grid.json +1 -1
  65. package/registry/card.json +1 -1
  66. package/registry/catalogue-page.json +1 -1
  67. package/registry/changelog-page.json +1 -1
  68. package/registry/chapter.json +478 -0
  69. package/registry/chat.json +1 -1
  70. package/registry/checkbox.json +1 -1
  71. package/registry/chip-link.json +1 -1
  72. package/registry/chip-row.json +1 -1
  73. package/registry/chip.json +1 -1
  74. package/registry/choice-grid.json +2 -2
  75. package/registry/code-block.json +1 -1
  76. package/registry/combobox.json +1 -1
  77. package/registry/command.json +1 -1
  78. package/registry/comparison-table.json +1 -1
  79. package/registry/component-page.json +1 -1
  80. package/registry/confirm-button.json +1 -1
  81. package/registry/contact-page.json +1 -1
  82. package/registry/continue-prompt.json +1 -1
  83. package/registry/countdown.json +1 -1
  84. package/registry/cover-hero.json +2 -2
  85. package/registry/cta-panel.json +1 -1
  86. package/registry/dashboard-layout.json +1 -1
  87. package/registry/data-table.json +1 -1
  88. package/registry/date-picker.json +2 -2
  89. package/registry/decision-list.json +1 -1
  90. package/registry/description-list.json +1 -1
  91. package/registry/detail-layout.json +1 -1
  92. package/registry/dialog.json +1 -1
  93. package/registry/docs-article-page.json +1 -1
  94. package/registry/docs-hub-page.json +1 -1
  95. package/registry/docs-layout.json +1 -1
  96. package/registry/docs-nav.json +1 -1
  97. package/registry/document-viewer.json +1 -1
  98. package/registry/drop-overlay.json +1 -1
  99. package/registry/dropdown-menu.json +1 -1
  100. package/registry/elephant.json +2 -2
  101. package/registry/empty-state.json +1 -1
  102. package/registry/engine-flow.json +1 -1
  103. package/registry/error-page.json +1 -1
  104. package/registry/examples/chapter/detail.d.ts +4 -0
  105. package/registry/examples/chapter/detail.js +21 -0
  106. package/registry/examples/chapter/detail.tsx +16 -0
  107. package/registry/examples/chapter/foot.d.ts +4 -0
  108. package/registry/examples/chapter/foot.js +20 -0
  109. package/registry/examples/chapter/foot.tsx +15 -0
  110. package/registry/examples/chapter/head.d.ts +4 -0
  111. package/registry/examples/chapter/head.js +22 -0
  112. package/registry/examples/chapter/head.tsx +17 -0
  113. package/registry/examples/chapter/index.d.ts +4 -0
  114. package/registry/examples/chapter/index.js +34 -0
  115. package/registry/examples/chapter/index.tsx +30 -0
  116. package/registry/examples/chapter/reading-page.d.ts +4 -0
  117. package/registry/examples/chapter/reading-page.js +34 -0
  118. package/registry/examples/chapter/reading-page.tsx +25 -0
  119. package/registry/examples/glossary/shop-terms.d.ts +4 -0
  120. package/registry/examples/glossary/shop-terms.js +11 -0
  121. package/registry/examples/glossary/shop-terms.tsx +5 -0
  122. package/registry/examples/glossary-term/inline.d.ts +4 -0
  123. package/registry/examples/glossary-term/inline.js +15 -0
  124. package/registry/examples/glossary-term/inline.tsx +5 -0
  125. package/registry/examples/kpi-explained/founder-cockpit.d.ts +4 -0
  126. package/registry/examples/kpi-explained/founder-cockpit.js +11 -0
  127. package/registry/examples/kpi-explained/founder-cockpit.tsx +5 -0
  128. package/registry/examples/kpi-explained/salon-month.d.ts +4 -0
  129. package/registry/examples/kpi-explained/salon-month.js +11 -0
  130. package/registry/examples/kpi-explained/salon-month.tsx +5 -0
  131. package/registry/examples/next-actions/shop-owner.d.ts +4 -0
  132. package/registry/examples/next-actions/shop-owner.js +11 -0
  133. package/registry/examples/next-actions/shop-owner.tsx +5 -0
  134. package/registry/examples/text-style/compact-toolbar.d.ts +4 -0
  135. package/registry/examples/text-style/compact-toolbar.js +28 -0
  136. package/registry/examples/text-style/compact-toolbar.tsx +21 -0
  137. package/registry/examples/text-style/product-price.d.ts +4 -0
  138. package/registry/examples/text-style/product-price.js +26 -0
  139. package/registry/examples/text-style/product-price.tsx +17 -0
  140. package/registry/examples/text-style/storefront-title.d.ts +4 -0
  141. package/registry/examples/text-style/storefront-title.js +36 -0
  142. package/registry/examples/text-style/storefront-title.tsx +22 -0
  143. package/registry/field.json +1 -1
  144. package/registry/file-upload.json +2 -2
  145. package/registry/filter-bar.json +1 -1
  146. package/registry/flock.json +1 -1
  147. package/registry/footnote.json +1 -1
  148. package/registry/force-update-screen.json +1 -1
  149. package/registry/form.json +2 -2
  150. package/registry/gallery-page.json +1 -1
  151. package/registry/genui.json +2 -2
  152. package/registry/glossary-term.json +144 -0
  153. package/registry/glossary.json +137 -0
  154. package/registry/glow-card.json +1 -1
  155. package/registry/goal-gauge.json +1 -1
  156. package/registry/how-it-works-page.json +1 -1
  157. package/registry/image-cropper.json +1 -1
  158. package/registry/index.json +199 -11
  159. package/registry/iso.json +1 -1
  160. package/registry/kanban.json +1 -1
  161. package/registry/kbd.json +1 -1
  162. package/registry/kpi-explained.json +364 -0
  163. package/registry/landing-page.json +1 -1
  164. package/registry/landscape.json +1 -1
  165. package/registry/language-switch.json +1 -1
  166. package/registry/legal-page.json +1 -1
  167. package/registry/link-card.json +2 -2
  168. package/registry/list.json +1 -1
  169. package/registry/loader.json +1 -1
  170. package/registry/longform-page.json +1 -1
  171. package/registry/maintenance-screen.json +1 -1
  172. package/registry/map.json +2 -2
  173. package/registry/markdown.json +1 -1
  174. package/registry/marketing-layout.json +1 -1
  175. package/registry/marquee.json +1 -1
  176. package/registry/mascot-quote.json +1 -1
  177. package/registry/mascot-state.json +1 -1
  178. package/registry/mascots.json +1 -1
  179. package/registry/match-dialog.json +1 -1
  180. package/registry/media-capture.json +1 -1
  181. package/registry/media-gallery.json +1 -1
  182. package/registry/media-grid.json +2 -2
  183. package/registry/meta-row.json +1 -1
  184. package/registry/meter.json +1 -1
  185. package/registry/nav-bar.json +2 -2
  186. package/registry/next-actions.json +142 -0
  187. package/registry/number-ticker.json +1 -1
  188. package/registry/opening-hours.json +1 -1
  189. package/registry/page-header.json +1 -1
  190. package/registry/page-hero.json +2 -2
  191. package/registry/page-index.json +2 -2
  192. package/registry/page.json +1 -1
  193. package/registry/pagination.json +1 -1
  194. package/registry/photo-grid.json +1 -1
  195. package/registry/pointer-light.json +1 -1
  196. package/registry/popover.json +1 -1
  197. package/registry/pricing-page.json +1 -1
  198. package/registry/product-page.json +1 -1
  199. package/registry/profile-card.json +1 -1
  200. package/registry/progress.json +1 -1
  201. package/registry/promise-card.json +1 -1
  202. package/registry/radio-group.json +1 -1
  203. package/registry/rail-nav.json +2 -2
  204. package/registry/recipes.json +1 -1
  205. package/registry/router.json +1 -1
  206. package/registry/scroll-stage.json +1 -1
  207. package/registry/search-field.json +1 -1
  208. package/registry/section-backdrop.json +1 -1
  209. package/registry/separator.json +1 -1
  210. package/registry/service-card.json +2 -2
  211. package/registry/settings-layout.json +1 -1
  212. package/registry/showcase.json +1 -1
  213. package/registry/site-footer.json +2 -2
  214. package/registry/sites.json +1 -1
  215. package/registry/skeleton.json +1 -1
  216. package/registry/skip-link.json +1 -1
  217. package/registry/slider.json +1 -1
  218. package/registry/slot-picker.json +2 -2
  219. package/registry/slot.json +1 -1
  220. package/registry/specimen-tag.json +1 -1
  221. package/registry/spinner.json +1 -1
  222. package/registry/split-view.json +1 -1
  223. package/registry/stage-banner.json +1 -1
  224. package/registry/stat.json +1 -1
  225. package/registry/status-page.json +1 -1
  226. package/registry/step-progress.json +1 -1
  227. package/registry/step-showcase.json +1 -1
  228. package/registry/stepper.json +1 -1
  229. package/registry/sticky-action.json +1 -1
  230. package/registry/swipe-deck.json +1 -1
  231. package/registry/switch.json +1 -1
  232. package/registry/tabs.json +1 -1
  233. package/registry/tag-input.json +1 -1
  234. package/registry/tag-selector.json +1 -1
  235. package/registry/taped-photo.json +1 -1
  236. package/registry/terminal-replay.json +1 -1
  237. package/registry/text-style.json +378 -0
  238. package/registry/theme.json +1 -1
  239. package/registry/toast.json +1 -1
  240. package/registry/toc.json +1 -1
  241. package/registry/tooltip.json +1 -1
  242. package/registry/tree-view.json +1 -1
  243. package/registry/update-prompt.json +1 -1
  244. package/registry/upload-queue.json +1 -1
  245. package/registry/verified-badge.json +1 -1
  246. package/registry/vertical-video.json +2 -2
  247. package/registry/video-player.json +1 -1
  248. package/registry/video-trimmer.json +1 -1
  249. package/registry/virtual.json +1 -1
  250. package/registry/waveform.json +1 -1
  251. /package/dist/{chunk-IBDTAMPF.js.map → chunk-7JG3VR4C.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/media-grid/load-more.tsx","../src/spinner/spinner.tsx","../src/media-grid/media-grid.tsx","../src/media-grid/section-hint.tsx","../src/block-link.tsx","../src/media-grid/shorts-shelf.tsx","../src/media-grid/video-card.tsx","../src/media-grid/preview-focus.ts","../src/media-grid/video-card-skeleton.tsx"],"sourcesContent":["// Client (IntersectionObserver). The end of a long list: the next page is requested while the person is still a\n// screenful away (`rootMargin`), so it is there before they reach it; the button stays as the way that never fails\n// (no observer, a failed page, a keyboard).\nimport * as React from \"react\";\n\nimport { Button } from \"../button\";\nimport { cn } from \"../cn\";\nimport { Spinner } from \"../spinner\";\n\nexport interface LoadMoreProps extends Omit<React.ComponentProps<\"div\">, \"children\" | \"onError\"> {\n /** Asks for the next page. Not called while `loading`, nor once `hasMore` is false. */\n onLoadMore: () => void;\n /** There is a next page. */\n hasMore: boolean;\n /** A page is on its way. */\n loading?: boolean;\n /** The last page failed: the button reads `retryLabel`, and nothing loads by itself until it is pressed. */\n failed?: boolean;\n /** The button, passed translated (\"Load more\"). */\n label: string;\n /** The wait, passed translated (\"Loading more videos\"). */\n loadingLabel: string;\n /** The button after a failure, passed translated (\"Try again\"). Defaults to `label`. */\n retryLabel?: string;\n /** Shown instead of the button when the list is over, passed translated (\"You have seen everything\"). Nothing if absent. */\n endLabel?: string;\n /**\n * How far below the viewport the page is already requested (an IntersectionObserver `rootMargin`).\n * @default \"900px 0px\"\n */\n rootMargin?: string;\n}\n\n/** Infinite scroll with a visible \"Load more\" under it. */\nexport function LoadMore({ onLoadMore, hasMore, loading = false, failed = false, label, loadingLabel, retryLabel, endLabel, rootMargin = \"900px 0px\", className, ...props }: LoadMoreProps) {\n const sentinel = React.useRef<HTMLDivElement>(null);\n const ask = React.useRef(onLoadMore);\n React.useEffect(() => {\n ask.current = onLoadMore;\n });\n // Observing again after each page makes the observer report the current state: a short page that leaves the sentinel in view asks for the next.\n React.useEffect(() => {\n const el = sentinel.current;\n if (!el || !hasMore || loading || failed || typeof IntersectionObserver === \"undefined\") return;\n const io = new IntersectionObserver((entries) => entries.some((e) => e.isIntersecting) && ask.current(), { rootMargin });\n io.observe(el);\n return () => io.disconnect();\n }, [hasMore, loading, failed, rootMargin]);\n return (\n <div data-load-more=\"\" data-state={loading ? \"loading\" : failed ? \"failed\" : hasMore ? \"idle\" : \"end\"} className={cn(\"relative flex min-h-14 items-center justify-center py-6\", className)} {...props}>\n <div ref={sentinel} aria-hidden className=\"pointer-events-none absolute inset-x-0 top-0 h-px\" />\n {loading ? (\n <Spinner label={loadingLabel} size=\"md\" />\n ) : hasMore ? (\n <Button type=\"button\" variant=\"secondary\" size=\"md\" onClick={() => onLoadMore()}>\n {failed ? (retryLabel ?? label) : label}\n </Button>\n ) : endLabel ? (\n <p className=\"m-0 text-sm text-fg-secondary\">{endLabel}</p>\n ) : null}\n </div>\n );\n}\n","// Server-safe. An indeterminate wait: the Krizaka loader glyph (a hexagon traced by light around a node), announced as\n// a status with its `label`. For a wait with a delay, a value, an ETA or an ending, see `@krizaka/ui/loader`.\nimport type * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nimport { LoaderGlyph } from \"./glyph\";\n\nexport const spinnerVariants = tv({\n base: \"shrink-0 text-accent\",\n variants: { size: { sm: \"h-4 w-4\", md: \"h-6 w-6\", lg: \"h-10 w-10\" } },\n defaultVariants: { size: \"md\" },\n});\n\nexport type SpinnerProps = Omit<React.ComponentProps<\"svg\">, \"children\"> &\n VariantProps<typeof spinnerVariants> & {\n /**\n * sm · md · lg.\n * @default \"md\"\n */\n size?: VariantProps<typeof spinnerVariants>[\"size\"];\n /** The accessible name of the wait, e.g. \"Loading\" — passed translated. */\n label: string;\n /** Between 0 and 1: the trace grows to the value (a determinate wait). Absent: it travels around. */\n value?: number | null;\n };\n\n/**\n * The Krizaka spinner: a hexagon traced by a segment of light around a breathing node, in `currentColor` (the accent by\n * default). `role=\"status\"`, named by `label`. Under prefers-reduced-motion it rests — a segment and the node lit.\n */\nexport function Spinner({ label, size, value, className, ...props }: SpinnerProps) {\n return (\n <LoaderGlyph\n role=\"status\"\n aria-label={label}\n aria-hidden={undefined}\n value={value}\n data-size={size ?? \"md\"}\n className={spinnerVariants({ size, className })}\n {...props}\n />\n );\n}\n","// Client (it renders the shelf). Mixed orientations without a broken grid. `split` (the feed default) sends the 9:16\n// clips to a `ShortsShelf` and lays the rest in a uniform 16:9 grid; `uniform` puts everything in one grid of one frame\n// (a source of the other orientation is told to `contain`, never cropped to a face). `layout=\"list\"` is one column.\nimport * as React from \"react\";\n\nimport { cn } from \"../cn\";\nimport { SectionHint, type SectionHintProps } from \"./section-hint\";\nimport { ShortsShelf, type ShortsShelfProps } from \"./shorts-shelf\";\nimport type { VideoCardAspect } from \"./video-card\";\n\nexport interface MediaGridState {\n /** The frame the card is drawn in (`aspect` of `VideoCard`). */\n aspect: VideoCardAspect;\n /** The card's `layout`. */\n layout: \"stack\" | \"row\";\n /** The source's orientation differs from the frame: pass `fit=\"contain\"`. */\n mismatch: boolean;\n}\n\nexport interface MediaGridProps<T> extends Omit<React.ComponentProps<\"div\">, \"children\"> {\n /** The videos, in order. */\n items: readonly T[];\n /** A stable key for an item. */\n getId: (item: T) => string;\n /** The source's orientation (from its width and height). */\n orientationOf: (item: T) => VideoCardAspect;\n /** One card, drawn in the frame the grid gives (`state`): use `VideoCard`. */\n renderItem: (item: T, state: MediaGridState) => React.ReactNode;\n /**\n * `split`: verticals on the shelf, the others in the grid · `uniform`: all in one frame (`frame`).\n * @default \"split\"\n */\n mode?: \"split\" | \"uniform\";\n /** The frame of `uniform`. @default \"landscape\" */\n frame?: VideoCardAspect;\n /** `grid` or `list` (one row per video). @default \"grid\" */\n layout?: \"grid\" | \"list\";\n /** The shelf of `split`: its name, \"See all\" and arrows, passed translated. */\n shelf: Pick<ShortsShelfProps<unknown>, \"label\" | \"title\" | \"hint\" | \"seeAll\" | \"previousLabel\" | \"nextLabel\">;\n /** A heading for the grid of the others (“Videos”), in the style of the shelf's, with an optional definition. */\n grid?: { label: string; hint?: SectionHintProps };\n}\n\nconst GRID = {\n landscape: \"grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4\",\n portrait: \"grid-cols-2 sm:grid-cols-3 lg:grid-cols-5 xl:grid-cols-6\",\n} as const;\n\n/** A media grid that never mixes heights badly: a shelf for the 9:16, a uniform grid for the rest. */\nexport function MediaGrid<T>({ items, getId, orientationOf, renderItem, mode = \"split\", frame = \"landscape\", layout = \"grid\", shelf, grid, className, ...props }: MediaGridProps<T>) {\n const gridHeading = React.useId();\n const row = layout === \"list\";\n const split = mode === \"split\";\n const verticals = split ? items.filter((i) => orientationOf(i) === \"portrait\") : [];\n const rest = split ? items.filter((i) => orientationOf(i) !== \"portrait\") : items;\n const gridFrame: VideoCardAspect = split ? \"landscape\" : frame;\n const state = (item: T, aspect: VideoCardAspect): MediaGridState => ({ aspect, layout: row ? \"row\" : \"stack\", mismatch: orientationOf(item) !== aspect });\n return (\n <div data-media-grid={mode} className={cn(\"flex min-w-0 flex-col gap-8\", className)} {...props}>\n {verticals.length > 0 && <ShortsShelf {...shelf} items={verticals} getId={getId} renderItem={(item) => renderItem(item, { ...state(item, \"portrait\"), layout: \"stack\" })} />}\n {rest.length > 0 && (\n <section aria-labelledby={grid ? gridHeading : undefined} data-media-grid-section className=\"min-w-0\">\n {grid && (\n <div className=\"mb-3 flex items-center gap-2\">\n <h2 id={gridHeading} className=\"m-0 text-base font-bold text-fg\">\n {grid.label}\n </h2>\n {grid.hint && <SectionHint {...grid.hint} />}\n </div>\n )}\n <ul data-grid-frame={gridFrame} className={cn(\"m-0 grid list-none auto-rows-fr gap-4 p-0\", row ? \"grid-cols-1\" : GRID[gridFrame])}>\n {rest.map((item) => (\n <li key={getId(item)} className=\"min-w-0\">\n {renderItem(item, state(item, gridFrame))}\n </li>\n ))}\n </ul>\n </section>\n )}\n </div>\n );\n}\n","// Client (open state). A tiny \"i\" button beside a section heading: it opens a one-line definition and, when given, a link to\n// the glossary. Opens on a tap or a press, on hover with a mouse, closes on Escape or when the focus leaves.\nimport * as React from \"react\";\n\nimport { BlockLink, type LinkComponent } from \"../block-link\";\nimport { cn } from \"../cn\";\n\nexport interface SectionHintProps {\n /** The button's accessible name, passed translated (“What are Shorts?”). */\n label: string;\n /** The definition, in one line. */\n text: string;\n /** A link to the full definition (the glossary). */\n link?: { href: string; label: string; linkAs?: LinkComponent };\n}\n\n/** The definition of a section, one tap away from its heading. */\nexport function SectionHint({ label, text, link }: SectionHintProps) {\n const [open, setOpen] = React.useState(false);\n const id = React.useId();\n return (\n <span\n className=\"relative inline-flex\"\n onMouseEnter={() => setOpen(true)}\n onMouseLeave={() => setOpen(false)}\n onBlur={(e) => {\n if (!e.currentTarget.contains(e.relatedTarget)) setOpen(false);\n }}\n onKeyDown={(e) => {\n if (e.key === \"Escape\") setOpen(false);\n }}\n >\n <button\n type=\"button\"\n aria-label={label}\n aria-expanded={open}\n aria-controls={id}\n onClick={() => setOpen((o) => !o)}\n className=\"inline-flex h-6 w-6 items-center justify-center rounded-full text-fg-muted transition-colors hover:text-fg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" aria-hidden>\n <circle cx=\"12\" cy=\"12\" r=\"9\" />\n <path d=\"M12 11v5M12 8h.01\" />\n </svg>\n </button>\n <span\n id={id}\n role=\"tooltip\"\n hidden={!open}\n className={cn(\"absolute left-0 top-full z-20 mt-1 w-64 max-w-[80vw] rounded-xl border border-border-default bg-surface-1 p-3 text-sm font-normal leading-snug text-fg-secondary shadow-lg\")}\n >\n {text}\n {link && (\n <>\n {\" \"}\n <BlockLink href={link.href} linkAs={link.linkAs} className=\"font-semibold text-fg-accent hover:underline\">\n {link.label}\n </BlockLink>\n </>\n )}\n </span>\n </span>\n );\n}\n","// The links of the page blocks (PageHero's back link, LinkCard, ArgumentCard, ActionBar): an internal path renders\n// through the app's router link (`linkAs`, e.g. Next's `Link`), anything else is an `<a>` that opens in a new tab.\n// Shared by the block entries; never exported on its own.\nimport type * as React from \"react\";\n\n/** The app's link component (Next's `Link`, React Router's `Link`…): receives `href`, `className` and the children. */\nexport type LinkComponent = React.ElementType<{ href: string; className?: string; children?: React.ReactNode; \"aria-label\"?: string; \"aria-describedby\"?: string }>;\n\n/** An address that leaves the app: an absolute URL or a protocol (`mailto:`). */\nexport const isExternal = (href: string) => /^[a-z][a-z0-9+.-]*:/i.test(href);\n\nexport function BlockLink({\n href,\n linkAs: Link = \"a\",\n external = isExternal(href),\n rel,\n ...props\n}: Omit<React.ComponentProps<\"a\">, \"href\"> & { href: string; linkAs?: LinkComponent; external?: boolean }) {\n if (external) return <a href={href} target=\"_blank\" rel={rel ?? \"noopener noreferrer\"} {...props} />;\n const Comp = Link as React.ElementType;\n return <Comp href={href} {...props} />;\n}\n","// Client (the arrows scroll the row). A horizontal row of 9:16 cards of one height: scroll-snap, arrows with a mouse,\n// a swipe on touch, a \"See all\" link. The row is a labelled list that scrolls with the keyboard (it takes focus).\nimport * as React from \"react\";\n\nimport { BlockLink, type LinkComponent } from \"../block-link\";\nimport { cn } from \"../cn\";\nimport { SectionHint, type SectionHintProps } from \"./section-hint\";\n\nexport interface ShortsShelfProps<T> extends Omit<React.ComponentProps<\"section\">, \"children\" | \"title\"> {\n /** The shelf's accessible name (\"Shorts\"), passed translated; shown as its heading unless `title` is set. */\n label: string;\n /** A heading different from the name. */\n title?: React.ReactNode;\n /** A small “i” beside the heading: the one-line definition of the section and a link to the glossary. */\n hint?: SectionHintProps;\n /** The clips, in order. */\n items: readonly T[];\n /** A stable key for an item. */\n getId: (item: T) => string;\n /** One card: use `VideoCard aspect=\"portrait\"`. */\n renderItem: (item: T, index: number) => React.ReactNode;\n /** Opens the vertical feed. */\n seeAll?: { href: string; label: string; linkAs?: LinkComponent };\n /** The arrows' names, passed translated. */\n previousLabel: string;\n /** The next arrow's name. */\n nextLabel: string;\n}\n\nconst Chevron = ({ flip }: { flip?: boolean }) => (\n <svg viewBox=\"0 0 24 24\" width=\"18\" height=\"18\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden className={flip ? \"rotate-180\" : undefined}>\n <path d=\"m9 6 6 6-6 6\" />\n </svg>\n);\n\nconst ARROW =\n \"hidden h-9 w-9 items-center justify-center rounded-full border border-border-default bg-surface-1 text-fg shadow-sm transition-colors hover:bg-surface-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-40 [@media(hover:hover)]:inline-flex\";\n\n/** A Shorts shelf: equal-height 9:16 cards in a snap-scrolling row. */\nexport function ShortsShelf<T>({ label, title, hint, items, getId, renderItem, seeAll, previousLabel, nextLabel, className, ...props }: ShortsShelfProps<T>) {\n const row = React.useRef<HTMLUListElement>(null);\n const [edge, setEdge] = React.useState({ start: true, end: false });\n const measure = React.useCallback(() => {\n const el = row.current;\n if (!el) return;\n setEdge({ start: el.scrollLeft <= 2, end: el.scrollLeft + el.clientWidth >= el.scrollWidth - 2 });\n }, []);\n React.useEffect(measure, [measure, items.length]);\n const page = (dir: 1 | -1) => {\n const el = row.current;\n if (el) el.scrollBy({ left: dir * el.clientWidth * 0.85, behavior: window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches ? \"auto\" : \"smooth\" });\n };\n const headingId = React.useId();\n if (items.length === 0) return null;\n return (\n <section aria-labelledby={headingId} data-shorts-shelf className={cn(\"min-w-0\", className)} {...props}>\n <div className=\"mb-3 flex items-center gap-2\">\n <h2 id={headingId} className=\"m-0 mr-auto text-base font-bold text-fg\">\n {title ?? label}\n </h2>\n {hint && <SectionHint {...hint} />}\n {seeAll && (\n <BlockLink href={seeAll.href} linkAs={seeAll.linkAs} className=\"text-sm font-semibold text-fg-accent hover:underline\">\n {seeAll.label}\n </BlockLink>\n )}\n <button type=\"button\" aria-label={previousLabel} disabled={edge.start} onClick={() => page(-1)} className={ARROW}>\n <Chevron flip />\n </button>\n <button type=\"button\" aria-label={nextLabel} disabled={edge.end} onClick={() => page(1)} className={ARROW}>\n <Chevron />\n </button>\n </div>\n <ul\n ref={row}\n onScroll={measure}\n // biome-ignore: a scroll container must take the keyboard focus to scroll with the arrow keys\n tabIndex={0}\n aria-labelledby={headingId}\n className=\"m-0 flex list-none snap-x snap-mandatory items-stretch gap-3 overflow-x-auto overflow-y-hidden overscroll-x-contain p-0 pt-1 -mt-1 pb-2 [scrollbar-width:none] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring [&::-webkit-scrollbar]:hidden\"\n >\n {items.map((item, i) => (\n <li key={getId(item)} className=\"w-[9.5rem] shrink-0 snap-start sm:w-44\">\n {renderItem(item, i)}\n </li>\n ))}\n </ul>\n </section>\n );\n}\n","// Client (hover / focus swaps the picture for its animated preview). A video in a grid or a shelf: the picture in a 16:9\n// or 9:16 frame (a source of the other shape is shown inside, never stretched), badges, duration, the title on two lines\n// reserved, the creator, a count and one quick action — the same height of body in every card so a row aligns.\nimport * as React from \"react\";\n\nimport { Avatar } from \"../avatar\";\nimport type { DefaultAvatar } from \"../avatar/default-avatar\";\nimport { BlockLink, type LinkComponent } from \"../block-link\";\nimport { Card } from \"../card\";\nimport { cn } from \"../cn\";\nimport { isLeader, subscribeLeader, trackVisibility } from \"./preview-focus\";\n\nexport type VideoCardAspect = \"landscape\" | \"portrait\";\n\nexport interface VideoCardCreator {\n /** The creator's name. */\n name: string;\n /** Their photo. */\n avatarSrc?: string | null;\n /** Their profile. */\n href?: string;\n /** The Krizaka default picture when there is no photo (a brand and a seed). */\n defaultAvatar?: DefaultAvatar;\n}\n\nexport interface VideoCardProps extends Omit<React.ComponentProps<\"article\">, \"title\"> {\n /** The title; it names the card and is its link. */\n title: string;\n /** Where the card opens. */\n href: string;\n /** The app's router link (Next's `Link`). */\n linkAs?: LinkComponent;\n /**\n * The frame: `landscape` (16:9) or `portrait` (9:16).\n * @default \"landscape\"\n */\n aspect?: VideoCardAspect;\n /**\n * `stack` (picture over text) or `row` (picture beside the text: the list mode of a section).\n * @default \"stack\"\n */\n layout?: \"stack\" | \"row\";\n /** The picture. */\n poster?: string | null;\n /**\n * An animated picture (WebP, a few hundred kB) that fades in over the poster once the pointer has rested on the card\n * (or it is focused) for `previewDelay`, and is removed again on leave. It is never requested before. On a phone (no\n * hover) only the card most inside the viewport plays it, unless the person asked for less data or less motion.\n */\n previewSrc?: string | null;\n /**\n * How long the pointer must rest before the preview is requested, in ms: a pointer crossing the grid loads nothing.\n * @default 200\n */\n previewDelay?: number;\n /**\n * `fetchPriority` of the poster: `high` for the first row (the largest contentful paint).\n * @default \"auto\"\n */\n posterPriority?: \"high\" | \"low\" | \"auto\";\n /**\n * `cover` fills the frame; `contain` draws the whole picture over a blurred copy of itself (a source of the other\n * orientation: no cropped face, no black bar).\n * @default \"cover\"\n */\n fit?: \"cover\" | \"contain\";\n /** The length, already formatted (`3:42`). */\n duration?: string;\n /** Badges at the top-left of the picture (Free, a price, 18+). Put them on `scrim` so they read in both themes. */\n badges?: React.ReactNode;\n /** Who made it: name, photo, profile. */\n creator?: VideoCardCreator;\n /** What is counted (`12.4K views`), already formatted. */\n meta?: React.ReactNode;\n /** One quick action at the end of the footer (a tip, a save). Its clicks must not follow the card's link. */\n action?: React.ReactNode;\n /** A sensitive picture: blurred and scaled so nothing shows (put the reveal button in `overlay`). */\n blurred?: boolean;\n /** Over the whole picture, above the card's link (a veil, a reveal button). */\n overlay?: React.ReactNode;\n /** Shown instead of the picture while there is none. */\n fallback?: React.ReactNode;\n /** `eager` for the first cards of a page. */\n loading?: \"lazy\" | \"eager\";\n}\n\nconst FRAME = { landscape: \"aspect-video\", portrait: \"aspect-[9/16]\" } as const;\n\n/** A video card whose frame, body and footer have one height per aspect, so a grid row or a shelf never staggers. */\nexport function VideoCard({\n title,\n href,\n linkAs,\n aspect = \"landscape\",\n layout = \"stack\",\n poster,\n previewSrc,\n previewDelay = 200,\n posterPriority = \"auto\",\n fit = \"cover\",\n duration,\n badges,\n creator,\n meta,\n action,\n fallback,\n blurred = false,\n overlay,\n loading = \"lazy\",\n className,\n onMouseEnter,\n onMouseLeave,\n onFocus,\n onBlur,\n ref,\n ...props\n}: VideoCardProps) {\n const [node, setNode] = React.useState<HTMLElement | null>(null);\n const setRef = React.useCallback(\n (el: HTMLElement | null) => {\n setNode(el);\n if (typeof ref === \"function\") ref(el);\n else if (ref) (ref as React.RefObject<HTMLElement | null>).current = el;\n },\n [ref],\n );\n const canPreview = Boolean(previewSrc) && !blurred;\n const { live, arm, disarm } = usePreviewIntent(node, canPreview, previewDelay);\n const row = layout === \"row\";\n const tall = aspect === \"portrait\" && !row;\n return (\n <article\n ref={setRef}\n data-aspect={aspect}\n data-layout={layout}\n className={cn(\"group relative h-full min-w-0\", className)}\n data-preview={live ? \"on\" : undefined}\n onMouseEnter={(e) => (arm(\"pointer\"), onMouseEnter?.(e))}\n onMouseLeave={(e) => (disarm(\"pointer\"), onMouseLeave?.(e))}\n onFocus={(e) => (arm(\"focus\"), onFocus?.(e))}\n onBlur={(e) => (disarm(\"focus\"), onBlur?.(e))}\n {...props}\n >\n <Card.Root interactive tone=\"glass\" className={cn(\"h-full w-full max-w-full\", row ? \"flex-row\" : \"flex-col\")}>\n <Card.Media aspect=\"auto\" className={cn(\"shrink-0\", FRAME[aspect], row && (aspect === \"portrait\" ? \"w-24 sm:w-28\" : \"w-40 sm:w-56\"))}>\n {fit === \"contain\" && poster && <img src={poster} alt=\"\" aria-hidden loading=\"lazy\" className=\"absolute inset-0 h-full w-full scale-125 object-cover opacity-70 blur-2xl\" />}\n <Card.Image\n src={poster}\n loading={loading}\n fetchPriority={posterPriority === \"auto\" ? undefined : posterPriority}\n fallback={fallback}\n className={cn(fit === \"contain\" && \"relative object-contain\", blurred && \"scale-110 blur-xl\")}\n />\n {live && previewSrc && <PreviewLayer src={previewSrc} contain={fit === \"contain\"} />}\n {overlay && <div className=\"absolute inset-0 z-20 flex\">{overlay}</div>}\n {badges && (\n <Card.Overlay corner=\"top-left\" className=\"theme-dark pointer-events-none flex-wrap\">\n {badges}\n </Card.Overlay>\n )}\n {duration && (\n <Card.Overlay corner=\"bottom-right\" className=\"pointer-events-none\">\n <span className=\"rounded-sm bg-scrim px-1.5 py-0.5 font-mono text-[11px] text-fg-on-media\">{duration}</span>\n </Card.Overlay>\n )}\n </Card.Media>\n {/* `static`: the body must not be the containing block of the title link's ::after, or the link would cover the text only and the picture would not open the video. */}\n <Card.Body className=\"static min-w-0 flex-1 justify-between gap-2 p-3\">\n <h3 className=\"m-0 min-h-[2.5em] text-sm font-semibold leading-tight text-fg\">\n <BlockLink href={href} linkAs={linkAs} className=\"line-clamp-2 outline-none after:absolute after:inset-0 after:z-0 after:content-[''] focus-visible:after:ring-2 focus-visible:after:ring-ring\">\n {title}\n </BlockLink>\n </h3>\n {/* A narrow card (9:16 on a shelf) puts the count and the action on a second line so the name is never cut to an initial. */}\n <div className={cn(\"relative z-10 text-xs text-fg-secondary\", tall ? \"flex flex-col gap-1\" : \"flex h-8 items-center gap-2\")}>\n {creator && (\n <div className=\"flex h-6 min-w-0 items-center gap-2\">\n <Avatar src={creator.avatarSrc} defaultAvatar={creator.defaultAvatar} alt=\"\" fallback={creator.name.charAt(0)} size=\"xs\" className=\"shrink-0\" />\n {creator.href ? (\n <BlockLink href={creator.href} linkAs={linkAs} className=\"min-w-0 truncate hover:text-fg\">\n {creator.name}\n </BlockLink>\n ) : (\n <span className=\"min-w-0 truncate\">{creator.name}</span>\n )}\n </div>\n )}\n {(meta || action) && (\n <div className={cn(\"flex items-center gap-2\", tall ? \"h-7 justify-between\" : \"ml-auto shrink-0\")}>\n {meta && <span className=\"shrink-0 font-mono text-[11px] text-fg-muted\">{meta}</span>}\n {action && <span className=\"shrink-0\">{action}</span>}\n </div>\n )}\n </div>\n </Card.Body>\n </Card.Root>\n </article>\n );\n}\n\n/** The animated preview, fading in over the poster once it has loaded; unmounted (so unloaded) on leave. */\nfunction PreviewLayer({ src, contain }: { src: string; contain: boolean }) {\n const [ready, setReady] = React.useState(false);\n return (\n <img\n src={src}\n alt=\"\"\n aria-hidden\n decoding=\"async\"\n data-preview-layer=\"\"\n data-ready={ready ? \"\" : undefined}\n onLoad={() => setReady(true)}\n className={cn(\"pointer-events-none absolute inset-0 h-full w-full opacity-0 transition-opacity duration-300 data-[ready]:opacity-100 motion-reduce:transition-none\", contain ? \"object-contain\" : \"object-cover\")}\n />\n );\n}\n\ntype Cause = \"pointer\" | \"focus\";\n\nconst matches = (query: string) => typeof window !== \"undefined\" && typeof window.matchMedia === \"function\" && window.matchMedia(query).matches;\nconst hoverCapable = () => matches(\"(hover: hover)\");\nconst saveData = () => typeof navigator !== \"undefined\" && (navigator as Navigator & { connection?: { saveData?: boolean } }).connection?.saveData === true;\n\n/**\n * When the preview is wanted. A desktop pointer or the keyboard focus must rest `delay` ms first; on a phone the card\n * the page shows most (and only one) wants it, never under Save-Data or reduced motion. Leaving clears everything.\n */\nfunction usePreviewIntent(node: HTMLElement | null, enabled: boolean, delay: number) {\n const [causes, setCauses] = React.useState<ReadonlySet<Cause>>(new Set());\n const timers = React.useRef(new Map<Cause, ReturnType<typeof setTimeout>>());\n const arm = React.useCallback(\n (cause: Cause) => {\n if (!enabled || saveData()) return;\n if (cause === \"pointer\" && !hoverCapable()) return;\n clearTimeout(timers.current.get(cause));\n timers.current.set(\n cause,\n setTimeout(() => setCauses((c) => new Set(c).add(cause)), delay),\n );\n },\n [enabled, delay],\n );\n const disarm = React.useCallback((cause: Cause) => {\n clearTimeout(timers.current.get(cause));\n timers.current.delete(cause);\n setCauses((c) => {\n if (!c.has(cause)) return c;\n const next = new Set(c);\n next.delete(cause);\n return next;\n });\n }, []);\n React.useEffect(() => {\n const pending = timers.current;\n return () => {\n for (const t of pending.values()) clearTimeout(t);\n pending.clear();\n };\n }, []);\n\n // A phone: the single most visible card, after the same rest.\n const touch = enabled && !hoverCapable();\n const leading = React.useSyncExternalStore(\n subscribeLeader,\n () => isLeader(node),\n () => false,\n );\n React.useEffect(() => {\n if (!node || !touch) return;\n return trackVisibility(node);\n }, [node, touch]);\n const [ready, setReady] = React.useState(false);\n const [wasLeading, setWasLeading] = React.useState(false);\n if (leading !== wasLeading) {\n setWasLeading(leading);\n if (!leading) setReady(false);\n }\n const wanted = touch && leading && !saveData() && !matches(\"(prefers-reduced-motion: reduce)\");\n React.useEffect(() => {\n if (!wanted) return;\n const t = setTimeout(() => setReady(true), delay);\n return () => clearTimeout(t);\n }, [wanted, delay]);\n\n return { live: enabled && (causes.size > 0 || (wanted && ready)), arm, disarm };\n}\n","// Client only. Which card of the page is the most visible: on a phone there is no hover, so exactly one card at a time\n// (the one most inside the viewport) may play its preview. One IntersectionObserver for the whole page, no per-card\n// observer; the answer is a store read with `useSyncExternalStore`.\nconst MIN_RATIO = 0.6;\nconst ratios = new Map<Element, number>();\nconst listeners = new Set<() => void>();\nlet observer: IntersectionObserver | null = null;\nlet leader: Element | null = null;\n\nfunction elect() {\n let best: Element | null = null;\n let bestRatio = MIN_RATIO - Number.EPSILON;\n for (const [el, ratio] of ratios) {\n if (ratio > bestRatio) {\n best = el;\n bestRatio = ratio;\n }\n }\n if (best === leader) return;\n leader = best;\n for (const l of listeners) l();\n}\n\n/** Starts counting an element's visibility; returns the function that stops it. */\nexport function trackVisibility(el: Element): () => void {\n if (typeof IntersectionObserver === \"undefined\") return () => {};\n observer ??= new IntersectionObserver(\n (entries) => {\n for (const e of entries) ratios.set(e.target, e.isIntersecting ? e.intersectionRatio : 0);\n elect();\n },\n { threshold: [0, 0.25, 0.5, 0.6, 0.75, 1] },\n );\n ratios.set(el, 0);\n observer.observe(el);\n return () => {\n observer?.unobserve(el);\n ratios.delete(el);\n elect();\n };\n}\n\nexport const subscribeLeader = (l: () => void) => (listeners.add(l), () => void listeners.delete(l));\n\n/** Whether this element is the card the page is showing most. */\nexport const isLeader = (el: Element | null) => el !== null && leader === el;\n\n/** Test seam: forget everything. */\nexport function resetPreviewFocus() {\n observer?.disconnect();\n observer = null;\n ratios.clear();\n leader = null;\n}\n","// A video card on its way: the same frame, body and footer heights as `VideoCard`, so nothing moves when it arrives.\nimport * as React from \"react\";\n\nimport { Card } from \"../card\";\nimport { cn } from \"../cn\";\nimport { Skeleton } from \"../skeleton\";\nimport type { VideoCardAspect } from \"./video-card\";\n\nexport interface VideoCardSkeletonProps extends React.ComponentProps<\"div\"> {\n /** `landscape` (16:9) or `portrait` (9:16), as the card it stands for. @default \"landscape\" */\n aspect?: VideoCardAspect;\n /** `stack` or `row`, as the card it stands for. @default \"stack\" */\n layout?: \"stack\" | \"row\";\n}\n\nconst FRAME = { landscape: \"aspect-video\", portrait: \"aspect-[9/16]\" } as const;\n\n/** The placeholder of a `VideoCard` (decorative: announce the wait on the list, with `MediaGridSkeleton`). */\nexport function VideoCardSkeleton({ aspect = \"landscape\", layout = \"stack\", className, ...props }: VideoCardSkeletonProps) {\n const row = layout === \"row\";\n const tall = aspect === \"portrait\" && !row;\n return (\n <div aria-hidden data-skeleton=\"video-card\" data-aspect={aspect} data-layout={layout} className={cn(\"h-full min-w-0\", className)} {...props}>\n <Card.Root tone=\"glass\" className={cn(\"h-full w-full max-w-full\", row ? \"flex-row\" : \"flex-col\")}>\n <Card.Media aspect=\"auto\" className={cn(\"shrink-0\", FRAME[aspect], row && (aspect === \"portrait\" ? \"w-24 sm:w-28\" : \"w-40 sm:w-56\"))}>\n <Skeleton shape=\"rect\" className=\"h-full w-full rounded-none\" />\n </Card.Media>\n <Card.Body className=\"static min-w-0 flex-1 justify-between gap-2 p-3\">\n <div className=\"flex min-h-[2.5em] flex-col justify-center gap-1.5\">\n <Skeleton className=\"h-3\" />\n <Skeleton className=\"h-3 w-3/5\" />\n </div>\n <div className={cn(\"flex text-xs\", tall ? \"flex-col gap-1\" : \"h-8 items-center gap-2\")}>\n <div className=\"flex h-6 min-w-0 items-center gap-2\">\n <Skeleton shape=\"circle\" className=\"h-6 w-6 shrink-0\" />\n <Skeleton className=\"h-2.5 w-20\" />\n </div>\n <div className={cn(\"flex items-center\", tall ? \"h-7\" : \"ml-auto\")}>\n <Skeleton className=\"h-2.5 w-14\" />\n </div>\n </div>\n </Card.Body>\n </Card.Root>\n </div>\n );\n}\n\nexport interface MediaGridSkeletonProps extends Omit<React.ComponentProps<\"div\">, \"children\"> {\n /** What is loading, passed translated (\"Loading videos\"): announced once, the cards are hidden from assistive technology. */\n label: string;\n /** Cards in the grid. @default 8 */\n count?: number;\n /** Cards on the Shorts shelf above the grid (`split` mode); 0 for none. @default 0 */\n shelfCount?: number;\n /** `grid` or `list`. @default \"grid\" */\n layout?: \"grid\" | \"list\";\n /** The frame of the grid. @default \"landscape\" */\n frame?: VideoCardAspect;\n}\n\nconst GRID = {\n landscape: \"grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4\",\n portrait: \"grid-cols-2 sm:grid-cols-3 lg:grid-cols-5 xl:grid-cols-6\",\n} as const;\n\n/** The placeholder of a `MediaGrid`: the same shelf and grid, skeleton cards, one polite announcement. */\nexport function MediaGridSkeleton({ label, count = 8, shelfCount = 0, layout = \"grid\", frame = \"landscape\", className, ...props }: MediaGridSkeletonProps) {\n const row = layout === \"list\";\n return (\n <div role=\"status\" aria-busy=\"true\" data-media-grid-skeleton=\"\" className={cn(\"flex min-w-0 flex-col gap-8\", className)} {...props}>\n <span className=\"sr-only\">{label}</span>\n {shelfCount > 0 && (\n <div aria-hidden className=\"min-w-0\">\n <Skeleton className=\"mb-3 h-4 w-24\" />\n <ul className=\"m-0 flex list-none items-stretch gap-3 overflow-hidden p-0\">\n {Array.from({ length: shelfCount }, (_, i) => (\n <li key={i} className=\"w-[9.5rem] shrink-0 sm:w-44\">\n <VideoCardSkeleton aspect=\"portrait\" />\n </li>\n ))}\n </ul>\n </div>\n )}\n <ul aria-hidden className={cn(\"m-0 grid list-none auto-rows-fr gap-4 p-0\", row ? \"grid-cols-1\" : GRID[frame])}>\n {Array.from({ length: count }, (_, i) => (\n <li key={i} className=\"min-w-0\">\n <VideoCardSkeleton aspect={frame} layout={row ? \"row\" : \"stack\"} />\n </li>\n ))}\n </ul>\n </div>\n );\n}\n"],"mappings":"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","names":["jsx","React","React","jsx","jsx","jsxs","React","jsx","jsxs","jsx","jsxs","React","jsx","jsxs","jsx","jsxs","FRAME","GRID"]}
1
+ {"version":3,"sources":["../src/media-grid/load-more.tsx","../src/spinner/spinner.tsx","../src/media-grid/media-grid.tsx","../src/media-grid/section-hint.tsx","../src/block-link.tsx","../src/media-grid/shorts-shelf.tsx","../src/media-grid/video-card.tsx","../src/media-grid/preview-focus.ts","../src/media-grid/video-card-skeleton.tsx"],"sourcesContent":["// Client (IntersectionObserver). The end of a long list: the next page is requested while the person is still a\n// screenful away (`rootMargin`), so it is there before they reach it; the button stays as the way that never fails\n// (no observer, a failed page, a keyboard).\nimport * as React from \"react\";\n\nimport { Button } from \"../button\";\nimport { cn } from \"../cn\";\nimport { Spinner } from \"../spinner\";\n\nexport interface LoadMoreProps extends Omit<React.ComponentProps<\"div\">, \"children\" | \"onError\"> {\n /** Asks for the next page. Not called while `loading`, nor once `hasMore` is false. */\n onLoadMore: () => void;\n /** There is a next page. */\n hasMore: boolean;\n /** A page is on its way. */\n loading?: boolean;\n /** The last page failed: the button reads `retryLabel`, and nothing loads by itself until it is pressed. */\n failed?: boolean;\n /** The button, passed translated (\"Load more\"). */\n label: string;\n /** The wait, passed translated (\"Loading more videos\"). */\n loadingLabel: string;\n /** The button after a failure, passed translated (\"Try again\"). Defaults to `label`. */\n retryLabel?: string;\n /** Shown instead of the button when the list is over, passed translated (\"You have seen everything\"). Nothing if absent. */\n endLabel?: string;\n /**\n * How far below the viewport the page is already requested (an IntersectionObserver `rootMargin`).\n * @default \"900px 0px\"\n */\n rootMargin?: string;\n}\n\n/** Infinite scroll with a visible \"Load more\" under it. */\nexport function LoadMore({ onLoadMore, hasMore, loading = false, failed = false, label, loadingLabel, retryLabel, endLabel, rootMargin = \"900px 0px\", className, ...props }: LoadMoreProps) {\n const sentinel = React.useRef<HTMLDivElement>(null);\n const ask = React.useRef(onLoadMore);\n React.useEffect(() => {\n ask.current = onLoadMore;\n });\n // Observing again after each page makes the observer report the current state: a short page that leaves the sentinel in view asks for the next.\n React.useEffect(() => {\n const el = sentinel.current;\n if (!el || !hasMore || loading || failed || typeof IntersectionObserver === \"undefined\") return;\n const io = new IntersectionObserver((entries) => entries.some((e) => e.isIntersecting) && ask.current(), { rootMargin });\n io.observe(el);\n return () => io.disconnect();\n }, [hasMore, loading, failed, rootMargin]);\n return (\n <div data-load-more=\"\" data-state={loading ? \"loading\" : failed ? \"failed\" : hasMore ? \"idle\" : \"end\"} className={cn(\"relative flex min-h-14 items-center justify-center py-6\", className)} {...props}>\n <div ref={sentinel} aria-hidden className=\"pointer-events-none absolute inset-x-0 top-0 h-px\" />\n {loading ? (\n <Spinner label={loadingLabel} size=\"md\" />\n ) : hasMore ? (\n <Button type=\"button\" variant=\"secondary\" size=\"md\" onClick={() => onLoadMore()}>\n {failed ? (retryLabel ?? label) : label}\n </Button>\n ) : endLabel ? (\n <p className=\"m-0 text-sm text-fg-secondary\">{endLabel}</p>\n ) : null}\n </div>\n );\n}\n","// Server-safe. An indeterminate wait: the Krizaka loader glyph (a hexagon traced by light around a node), announced as\n// a status with its `label`. For a wait with a delay, a value, an ETA or an ending, see `@krizaka/ui/loader`.\nimport type * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nimport { LoaderGlyph } from \"./glyph\";\n\nexport const spinnerVariants = tv({\n base: \"shrink-0 text-accent\",\n variants: { size: { sm: \"h-4 w-4\", md: \"h-6 w-6\", lg: \"h-10 w-10\" } },\n defaultVariants: { size: \"md\" },\n});\n\nexport type SpinnerProps = Omit<React.ComponentProps<\"svg\">, \"children\"> &\n VariantProps<typeof spinnerVariants> & {\n /**\n * sm · md · lg.\n * @default \"md\"\n */\n size?: VariantProps<typeof spinnerVariants>[\"size\"];\n /** The accessible name of the wait, e.g. \"Loading\" — passed translated. */\n label: string;\n /** Between 0 and 1: the trace grows to the value (a determinate wait). Absent: it travels around. */\n value?: number | null;\n };\n\n/**\n * The Krizaka spinner: a hexagon traced by a segment of light around a breathing node, in `currentColor` (the accent by\n * default). `role=\"status\"`, named by `label`. Under prefers-reduced-motion it rests — a segment and the node lit.\n */\nexport function Spinner({ label, size, value, className, ...props }: SpinnerProps) {\n return (\n <LoaderGlyph\n role=\"status\"\n aria-label={label}\n aria-hidden={undefined}\n value={value}\n data-size={size ?? \"md\"}\n className={spinnerVariants({ size, className })}\n {...props}\n />\n );\n}\n","// Client (it renders the shelf). Mixed orientations without a broken grid. `split` (the feed default) sends the 9:16\n// clips to a `ShortsShelf` and lays the rest in a uniform 16:9 grid; `uniform` puts everything in one grid of one frame\n// (a source of the other orientation is told to `contain`, never cropped to a face). `layout=\"list\"` is one column.\nimport * as React from \"react\";\n\nimport { cn } from \"../cn\";\nimport { SectionHint, type SectionHintProps } from \"./section-hint\";\nimport { ShortsShelf, type ShortsShelfProps } from \"./shorts-shelf\";\nimport type { VideoCardAspect } from \"./video-card\";\n\nexport interface MediaGridState {\n /** The frame the card is drawn in (`aspect` of `VideoCard`). */\n aspect: VideoCardAspect;\n /** The card's `layout`. */\n layout: \"stack\" | \"row\";\n /** The source's orientation differs from the frame: pass `fit=\"contain\"`. */\n mismatch: boolean;\n}\n\nexport interface MediaGridProps<T> extends Omit<React.ComponentProps<\"div\">, \"children\"> {\n /** The videos, in order. */\n items: readonly T[];\n /** A stable key for an item. */\n getId: (item: T) => string;\n /** The source's orientation (from its width and height). */\n orientationOf: (item: T) => VideoCardAspect;\n /** One card, drawn in the frame the grid gives (`state`): use `VideoCard`. */\n renderItem: (item: T, state: MediaGridState) => React.ReactNode;\n /**\n * `split`: verticals on the shelf, the others in the grid · `uniform`: all in one frame (`frame`).\n * @default \"split\"\n */\n mode?: \"split\" | \"uniform\";\n /** The frame of `uniform`. @default \"landscape\" */\n frame?: VideoCardAspect;\n /** `grid` or `list` (one row per video). @default \"grid\" */\n layout?: \"grid\" | \"list\";\n /** The shelf of `split`: its name, \"See all\" and arrows, passed translated. */\n shelf: Pick<ShortsShelfProps<unknown>, \"label\" | \"title\" | \"hint\" | \"seeAll\" | \"previousLabel\" | \"nextLabel\">;\n /** A heading for the grid of the others (“Videos”), in the style of the shelf's, with an optional definition. */\n grid?: { label: string; hint?: SectionHintProps };\n}\n\nconst GRID = {\n landscape: \"grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4\",\n portrait: \"grid-cols-2 sm:grid-cols-3 lg:grid-cols-5 xl:grid-cols-6\",\n} as const;\n\n/** A media grid that never mixes heights badly: a shelf for the 9:16, a uniform grid for the rest. */\nexport function MediaGrid<T>({ items, getId, orientationOf, renderItem, mode = \"split\", frame = \"landscape\", layout = \"grid\", shelf, grid, className, ...props }: MediaGridProps<T>) {\n const gridHeading = React.useId();\n const row = layout === \"list\";\n const split = mode === \"split\";\n const verticals = split ? items.filter((i) => orientationOf(i) === \"portrait\") : [];\n const rest = split ? items.filter((i) => orientationOf(i) !== \"portrait\") : items;\n const gridFrame: VideoCardAspect = split ? \"landscape\" : frame;\n const state = (item: T, aspect: VideoCardAspect): MediaGridState => ({ aspect, layout: row ? \"row\" : \"stack\", mismatch: orientationOf(item) !== aspect });\n return (\n <div data-media-grid={mode} className={cn(\"flex min-w-0 flex-col gap-8\", className)} {...props}>\n {verticals.length > 0 && <ShortsShelf {...shelf} items={verticals} getId={getId} renderItem={(item) => renderItem(item, { ...state(item, \"portrait\"), layout: \"stack\" })} />}\n {rest.length > 0 && (\n <section aria-labelledby={grid ? gridHeading : undefined} data-media-grid-section className=\"min-w-0\">\n {grid && (\n <div className=\"mb-3 flex items-center gap-2\">\n <h2 id={gridHeading} className=\"m-0 text-base font-bold text-fg\">\n {grid.label}\n </h2>\n {grid.hint && <SectionHint {...grid.hint} />}\n </div>\n )}\n <ul data-grid-frame={gridFrame} className={cn(\"m-0 grid list-none auto-rows-fr gap-4 p-0\", row ? \"grid-cols-1\" : GRID[gridFrame])}>\n {rest.map((item) => (\n <li key={getId(item)} className=\"min-w-0\">\n {renderItem(item, state(item, gridFrame))}\n </li>\n ))}\n </ul>\n </section>\n )}\n </div>\n );\n}\n","// Client (open state). A tiny \"i\" button beside a section heading: it opens a one-line definition and, when given, a link to\n// the glossary. Opens on a tap or a press, on hover with a mouse, closes on Escape or when the focus leaves.\nimport * as React from \"react\";\n\nimport { BlockLink, type LinkComponent } from \"../block-link\";\nimport { cn } from \"../cn\";\n\nexport interface SectionHintProps {\n /** The button's accessible name, passed translated (“What are Shorts?”). */\n label: string;\n /** The definition, in one line. */\n text: string;\n /** A link to the full definition (the glossary). */\n link?: { href: string; label: string; linkAs?: LinkComponent };\n}\n\n/** The definition of a section, one tap away from its heading. */\nexport function SectionHint({ label, text, link }: SectionHintProps) {\n const [open, setOpen] = React.useState(false);\n const id = React.useId();\n return (\n <span\n className=\"relative inline-flex\"\n onMouseEnter={() => setOpen(true)}\n onMouseLeave={() => setOpen(false)}\n onBlur={(e) => {\n if (!e.currentTarget.contains(e.relatedTarget)) setOpen(false);\n }}\n onKeyDown={(e) => {\n if (e.key === \"Escape\") setOpen(false);\n }}\n >\n <button\n type=\"button\"\n aria-label={label}\n aria-expanded={open}\n aria-controls={id}\n onClick={() => setOpen((o) => !o)}\n className=\"inline-flex h-6 w-6 items-center justify-center rounded-full text-fg-muted transition-colors hover:text-fg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" aria-hidden>\n <circle cx=\"12\" cy=\"12\" r=\"9\" />\n <path d=\"M12 11v5M12 8h.01\" />\n </svg>\n </button>\n <span\n id={id}\n role=\"tooltip\"\n hidden={!open}\n className={cn(\"absolute left-0 top-full z-20 mt-1 w-64 max-w-[80vw] rounded-xl border border-border-default bg-surface-1 p-3 text-sm font-normal leading-snug text-fg-secondary shadow-lg\")}\n >\n {text}\n {link && (\n <>\n {\" \"}\n <BlockLink href={link.href} linkAs={link.linkAs} className=\"font-semibold text-fg-accent hover:underline\">\n {link.label}\n </BlockLink>\n </>\n )}\n </span>\n </span>\n );\n}\n","// The links of the page blocks (PageHero's back link, LinkCard, ArgumentCard, ActionBar): an internal path renders\n// through the app's router link (`linkAs`, e.g. Next's `Link`), anything else is an `<a>` that opens in a new tab.\n// Shared by the block entries; never exported on its own.\nimport type * as React from \"react\";\n\n/** The app's link component (Next's `Link`, React Router's `Link`…): receives `href`, `className` and the children. */\nexport type LinkComponent = React.ElementType<{ href: string; className?: string; children?: React.ReactNode; \"aria-label\"?: string; \"aria-describedby\"?: string }>;\n\n/** An address that leaves the app: an absolute URL or a protocol (`mailto:`). */\nexport const isExternal = (href: string) => /^[a-z][a-z0-9+.-]*:/i.test(href);\n\nexport function BlockLink({\n href,\n linkAs: Link = \"a\",\n external = isExternal(href),\n rel,\n ...props\n}: Omit<React.ComponentProps<\"a\">, \"href\"> & { href: string; linkAs?: LinkComponent; external?: boolean }) {\n if (external) return <a href={href} target=\"_blank\" rel={rel ?? \"noopener noreferrer\"} {...props} />;\n const Comp = Link as React.ElementType;\n return <Comp href={href} rel={rel} {...props} />;\n}\n","// Client (the arrows scroll the row). A horizontal row of 9:16 cards of one height: scroll-snap, arrows with a mouse,\n// a swipe on touch, a \"See all\" link. The row is a labelled list that scrolls with the keyboard (it takes focus).\nimport * as React from \"react\";\n\nimport { BlockLink, type LinkComponent } from \"../block-link\";\nimport { cn } from \"../cn\";\nimport { SectionHint, type SectionHintProps } from \"./section-hint\";\n\nexport interface ShortsShelfProps<T> extends Omit<React.ComponentProps<\"section\">, \"children\" | \"title\"> {\n /** The shelf's accessible name (\"Shorts\"), passed translated; shown as its heading unless `title` is set. */\n label: string;\n /** A heading different from the name. */\n title?: React.ReactNode;\n /** A small “i” beside the heading: the one-line definition of the section and a link to the glossary. */\n hint?: SectionHintProps;\n /** The clips, in order. */\n items: readonly T[];\n /** A stable key for an item. */\n getId: (item: T) => string;\n /** One card: use `VideoCard aspect=\"portrait\"`. */\n renderItem: (item: T, index: number) => React.ReactNode;\n /** Opens the vertical feed. */\n seeAll?: { href: string; label: string; linkAs?: LinkComponent };\n /** The arrows' names, passed translated. */\n previousLabel: string;\n /** The next arrow's name. */\n nextLabel: string;\n}\n\nconst Chevron = ({ flip }: { flip?: boolean }) => (\n <svg viewBox=\"0 0 24 24\" width=\"18\" height=\"18\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden className={flip ? \"rotate-180\" : undefined}>\n <path d=\"m9 6 6 6-6 6\" />\n </svg>\n);\n\nconst ARROW =\n \"hidden h-9 w-9 items-center justify-center rounded-full border border-border-default bg-surface-1 text-fg shadow-sm transition-colors hover:bg-surface-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-40 [@media(hover:hover)]:inline-flex\";\n\n/** A Shorts shelf: equal-height 9:16 cards in a snap-scrolling row. */\nexport function ShortsShelf<T>({ label, title, hint, items, getId, renderItem, seeAll, previousLabel, nextLabel, className, ...props }: ShortsShelfProps<T>) {\n const row = React.useRef<HTMLUListElement>(null);\n const [edge, setEdge] = React.useState({ start: true, end: false });\n const measure = React.useCallback(() => {\n const el = row.current;\n if (!el) return;\n setEdge({ start: el.scrollLeft <= 2, end: el.scrollLeft + el.clientWidth >= el.scrollWidth - 2 });\n }, []);\n React.useEffect(measure, [measure, items.length]);\n const page = (dir: 1 | -1) => {\n const el = row.current;\n if (el) el.scrollBy({ left: dir * el.clientWidth * 0.85, behavior: window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches ? \"auto\" : \"smooth\" });\n };\n const headingId = React.useId();\n if (items.length === 0) return null;\n return (\n <section aria-labelledby={headingId} data-shorts-shelf className={cn(\"min-w-0\", className)} {...props}>\n <div className=\"mb-3 flex items-center gap-2\">\n <h2 id={headingId} className=\"m-0 mr-auto text-base font-bold text-fg\">\n {title ?? label}\n </h2>\n {hint && <SectionHint {...hint} />}\n {seeAll && (\n <BlockLink href={seeAll.href} linkAs={seeAll.linkAs} className=\"text-sm font-semibold text-fg-accent hover:underline\">\n {seeAll.label}\n </BlockLink>\n )}\n <button type=\"button\" aria-label={previousLabel} disabled={edge.start} onClick={() => page(-1)} className={ARROW}>\n <Chevron flip />\n </button>\n <button type=\"button\" aria-label={nextLabel} disabled={edge.end} onClick={() => page(1)} className={ARROW}>\n <Chevron />\n </button>\n </div>\n <ul\n ref={row}\n onScroll={measure}\n // biome-ignore: a scroll container must take the keyboard focus to scroll with the arrow keys\n tabIndex={0}\n aria-labelledby={headingId}\n className=\"m-0 flex list-none snap-x snap-mandatory items-stretch gap-3 overflow-x-auto overflow-y-hidden overscroll-x-contain p-0 pt-1 -mt-1 pb-2 [scrollbar-width:none] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring [&::-webkit-scrollbar]:hidden\"\n >\n {items.map((item, i) => (\n <li key={getId(item)} className=\"w-[9.5rem] shrink-0 snap-start sm:w-44\">\n {renderItem(item, i)}\n </li>\n ))}\n </ul>\n </section>\n );\n}\n","// Client (hover / focus swaps the picture for its animated preview). A video in a grid or a shelf: the picture in a 16:9\n// or 9:16 frame (a source of the other shape is shown inside, never stretched), badges, duration, the title on two lines\n// reserved, the creator, a count and one quick action — the same height of body in every card so a row aligns.\nimport * as React from \"react\";\n\nimport { Avatar } from \"../avatar\";\nimport type { DefaultAvatar } from \"../avatar/default-avatar\";\nimport { BlockLink, type LinkComponent } from \"../block-link\";\nimport { Card } from \"../card\";\nimport { cn } from \"../cn\";\nimport { isLeader, subscribeLeader, trackVisibility } from \"./preview-focus\";\n\nexport type VideoCardAspect = \"landscape\" | \"portrait\";\n\nexport interface VideoCardCreator {\n /** The creator's name. */\n name: string;\n /** Their photo. */\n avatarSrc?: string | null;\n /** Their profile. */\n href?: string;\n /** The Krizaka default picture when there is no photo (a brand and a seed). */\n defaultAvatar?: DefaultAvatar;\n}\n\nexport interface VideoCardProps extends Omit<React.ComponentProps<\"article\">, \"title\"> {\n /** The title; it names the card and is its link. */\n title: string;\n /** Where the card opens. */\n href: string;\n /** The app's router link (Next's `Link`). */\n linkAs?: LinkComponent;\n /**\n * The frame: `landscape` (16:9) or `portrait` (9:16).\n * @default \"landscape\"\n */\n aspect?: VideoCardAspect;\n /**\n * `stack` (picture over text) or `row` (picture beside the text: the list mode of a section).\n * @default \"stack\"\n */\n layout?: \"stack\" | \"row\";\n /** The picture. */\n poster?: string | null;\n /**\n * An animated picture (WebP, a few hundred kB) that fades in over the poster once the pointer has rested on the card\n * (or it is focused) for `previewDelay`, and is removed again on leave. It is never requested before. On a phone (no\n * hover) only the card most inside the viewport plays it, unless the person asked for less data or less motion.\n */\n previewSrc?: string | null;\n /**\n * How long the pointer must rest before the preview is requested, in ms: a pointer crossing the grid loads nothing.\n * @default 200\n */\n previewDelay?: number;\n /**\n * `fetchPriority` of the poster: `high` for the first row (the largest contentful paint).\n * @default \"auto\"\n */\n posterPriority?: \"high\" | \"low\" | \"auto\";\n /**\n * `cover` fills the frame; `contain` draws the whole picture over a blurred copy of itself (a source of the other\n * orientation: no cropped face, no black bar).\n * @default \"cover\"\n */\n fit?: \"cover\" | \"contain\";\n /** The length, already formatted (`3:42`). */\n duration?: string;\n /** Badges at the top-left of the picture (Free, a price, 18+). Put them on `scrim` so they read in both themes. */\n badges?: React.ReactNode;\n /** Who made it: name, photo, profile. */\n creator?: VideoCardCreator;\n /** What is counted (`12.4K views`), already formatted. */\n meta?: React.ReactNode;\n /** One quick action at the end of the footer (a tip, a save). Its clicks must not follow the card's link. */\n action?: React.ReactNode;\n /** A sensitive picture: blurred and scaled so nothing shows (put the reveal button in `overlay`). */\n blurred?: boolean;\n /** Over the whole picture, above the card's link (a veil, a reveal button). */\n overlay?: React.ReactNode;\n /** Shown instead of the picture while there is none. */\n fallback?: React.ReactNode;\n /** `eager` for the first cards of a page. */\n loading?: \"lazy\" | \"eager\";\n}\n\nconst FRAME = { landscape: \"aspect-video\", portrait: \"aspect-[9/16]\" } as const;\n\n/** A video card whose frame, body and footer have one height per aspect, so a grid row or a shelf never staggers. */\nexport function VideoCard({\n title,\n href,\n linkAs,\n aspect = \"landscape\",\n layout = \"stack\",\n poster,\n previewSrc,\n previewDelay = 200,\n posterPriority = \"auto\",\n fit = \"cover\",\n duration,\n badges,\n creator,\n meta,\n action,\n fallback,\n blurred = false,\n overlay,\n loading = \"lazy\",\n className,\n onMouseEnter,\n onMouseLeave,\n onFocus,\n onBlur,\n ref,\n ...props\n}: VideoCardProps) {\n const [node, setNode] = React.useState<HTMLElement | null>(null);\n const setRef = React.useCallback(\n (el: HTMLElement | null) => {\n setNode(el);\n if (typeof ref === \"function\") ref(el);\n else if (ref) (ref as React.RefObject<HTMLElement | null>).current = el;\n },\n [ref],\n );\n const canPreview = Boolean(previewSrc) && !blurred;\n const { live, arm, disarm } = usePreviewIntent(node, canPreview, previewDelay);\n const row = layout === \"row\";\n const tall = aspect === \"portrait\" && !row;\n return (\n <article\n ref={setRef}\n data-aspect={aspect}\n data-layout={layout}\n className={cn(\"group relative h-full min-w-0\", className)}\n data-preview={live ? \"on\" : undefined}\n onMouseEnter={(e) => (arm(\"pointer\"), onMouseEnter?.(e))}\n onMouseLeave={(e) => (disarm(\"pointer\"), onMouseLeave?.(e))}\n onFocus={(e) => (arm(\"focus\"), onFocus?.(e))}\n onBlur={(e) => (disarm(\"focus\"), onBlur?.(e))}\n {...props}\n >\n <Card.Root interactive tone=\"glass\" className={cn(\"h-full w-full max-w-full\", row ? \"flex-row\" : \"flex-col\")}>\n <Card.Media aspect=\"auto\" className={cn(\"shrink-0\", FRAME[aspect], row && (aspect === \"portrait\" ? \"w-24 sm:w-28\" : \"w-40 sm:w-56\"))}>\n {fit === \"contain\" && poster && <img src={poster} alt=\"\" aria-hidden loading=\"lazy\" className=\"absolute inset-0 h-full w-full scale-125 object-cover opacity-70 blur-2xl\" />}\n <Card.Image\n src={poster}\n loading={loading}\n fetchPriority={posterPriority === \"auto\" ? undefined : posterPriority}\n fallback={fallback}\n className={cn(fit === \"contain\" && \"relative object-contain\", blurred && \"scale-110 blur-xl\")}\n />\n {live && previewSrc && <PreviewLayer src={previewSrc} contain={fit === \"contain\"} />}\n {overlay && <div className=\"absolute inset-0 z-20 flex\">{overlay}</div>}\n {badges && (\n <Card.Overlay corner=\"top-left\" className=\"theme-dark pointer-events-none flex-wrap\">\n {badges}\n </Card.Overlay>\n )}\n {duration && (\n <Card.Overlay corner=\"bottom-right\" className=\"pointer-events-none\">\n <span className=\"rounded-sm bg-scrim px-1.5 py-0.5 font-mono text-[11px] text-fg-on-media\">{duration}</span>\n </Card.Overlay>\n )}\n </Card.Media>\n {/* `static`: the body must not be the containing block of the title link's ::after, or the link would cover the text only and the picture would not open the video. */}\n <Card.Body className=\"static min-w-0 flex-1 justify-between gap-2 p-3\">\n <h3 className=\"m-0 min-h-[2.5em] text-sm font-semibold leading-tight text-fg\">\n <BlockLink href={href} linkAs={linkAs} className=\"line-clamp-2 outline-none after:absolute after:inset-0 after:z-0 after:content-[''] focus-visible:after:ring-2 focus-visible:after:ring-ring\">\n {title}\n </BlockLink>\n </h3>\n {/* A narrow card (9:16 on a shelf) puts the count and the action on a second line so the name is never cut to an initial. */}\n <div className={cn(\"relative z-10 text-xs text-fg-secondary\", tall ? \"flex flex-col gap-1\" : \"flex h-8 items-center gap-2\")}>\n {creator && (\n <div className=\"flex h-6 min-w-0 items-center gap-2\">\n <Avatar src={creator.avatarSrc} defaultAvatar={creator.defaultAvatar} alt=\"\" fallback={creator.name.charAt(0)} size=\"xs\" className=\"shrink-0\" />\n {creator.href ? (\n <BlockLink href={creator.href} linkAs={linkAs} className=\"min-w-0 truncate hover:text-fg\">\n {creator.name}\n </BlockLink>\n ) : (\n <span className=\"min-w-0 truncate\">{creator.name}</span>\n )}\n </div>\n )}\n {(meta || action) && (\n <div className={cn(\"flex items-center gap-2\", tall ? \"h-7 justify-between\" : \"ml-auto shrink-0\")}>\n {meta && <span className=\"shrink-0 font-mono text-[11px] text-fg-muted\">{meta}</span>}\n {action && <span className=\"shrink-0\">{action}</span>}\n </div>\n )}\n </div>\n </Card.Body>\n </Card.Root>\n </article>\n );\n}\n\n/** The animated preview, fading in over the poster once it has loaded; unmounted (so unloaded) on leave. */\nfunction PreviewLayer({ src, contain }: { src: string; contain: boolean }) {\n const [ready, setReady] = React.useState(false);\n return (\n <img\n src={src}\n alt=\"\"\n aria-hidden\n decoding=\"async\"\n data-preview-layer=\"\"\n data-ready={ready ? \"\" : undefined}\n onLoad={() => setReady(true)}\n className={cn(\"pointer-events-none absolute inset-0 h-full w-full opacity-0 transition-opacity duration-300 data-[ready]:opacity-100 motion-reduce:transition-none\", contain ? \"object-contain\" : \"object-cover\")}\n />\n );\n}\n\ntype Cause = \"pointer\" | \"focus\";\n\nconst matches = (query: string) => typeof window !== \"undefined\" && typeof window.matchMedia === \"function\" && window.matchMedia(query).matches;\nconst hoverCapable = () => matches(\"(hover: hover)\");\nconst saveData = () => typeof navigator !== \"undefined\" && (navigator as Navigator & { connection?: { saveData?: boolean } }).connection?.saveData === true;\n\n/**\n * When the preview is wanted. A desktop pointer or the keyboard focus must rest `delay` ms first; on a phone the card\n * the page shows most (and only one) wants it, never under Save-Data or reduced motion. Leaving clears everything.\n */\nfunction usePreviewIntent(node: HTMLElement | null, enabled: boolean, delay: number) {\n const [causes, setCauses] = React.useState<ReadonlySet<Cause>>(new Set());\n const timers = React.useRef(new Map<Cause, ReturnType<typeof setTimeout>>());\n const arm = React.useCallback(\n (cause: Cause) => {\n if (!enabled || saveData()) return;\n if (cause === \"pointer\" && !hoverCapable()) return;\n clearTimeout(timers.current.get(cause));\n timers.current.set(\n cause,\n setTimeout(() => setCauses((c) => new Set(c).add(cause)), delay),\n );\n },\n [enabled, delay],\n );\n const disarm = React.useCallback((cause: Cause) => {\n clearTimeout(timers.current.get(cause));\n timers.current.delete(cause);\n setCauses((c) => {\n if (!c.has(cause)) return c;\n const next = new Set(c);\n next.delete(cause);\n return next;\n });\n }, []);\n React.useEffect(() => {\n const pending = timers.current;\n return () => {\n for (const t of pending.values()) clearTimeout(t);\n pending.clear();\n };\n }, []);\n\n // A phone: the single most visible card, after the same rest.\n const touch = enabled && !hoverCapable();\n const leading = React.useSyncExternalStore(\n subscribeLeader,\n () => isLeader(node),\n () => false,\n );\n React.useEffect(() => {\n if (!node || !touch) return;\n return trackVisibility(node);\n }, [node, touch]);\n const [ready, setReady] = React.useState(false);\n const [wasLeading, setWasLeading] = React.useState(false);\n if (leading !== wasLeading) {\n setWasLeading(leading);\n if (!leading) setReady(false);\n }\n const wanted = touch && leading && !saveData() && !matches(\"(prefers-reduced-motion: reduce)\");\n React.useEffect(() => {\n if (!wanted) return;\n const t = setTimeout(() => setReady(true), delay);\n return () => clearTimeout(t);\n }, [wanted, delay]);\n\n return { live: enabled && (causes.size > 0 || (wanted && ready)), arm, disarm };\n}\n","// Client only. Which card of the page is the most visible: on a phone there is no hover, so exactly one card at a time\n// (the one most inside the viewport) may play its preview. One IntersectionObserver for the whole page, no per-card\n// observer; the answer is a store read with `useSyncExternalStore`.\nconst MIN_RATIO = 0.6;\nconst ratios = new Map<Element, number>();\nconst listeners = new Set<() => void>();\nlet observer: IntersectionObserver | null = null;\nlet leader: Element | null = null;\n\nfunction elect() {\n let best: Element | null = null;\n let bestRatio = MIN_RATIO - Number.EPSILON;\n for (const [el, ratio] of ratios) {\n if (ratio > bestRatio) {\n best = el;\n bestRatio = ratio;\n }\n }\n if (best === leader) return;\n leader = best;\n for (const l of listeners) l();\n}\n\n/** Starts counting an element's visibility; returns the function that stops it. */\nexport function trackVisibility(el: Element): () => void {\n if (typeof IntersectionObserver === \"undefined\") return () => {};\n observer ??= new IntersectionObserver(\n (entries) => {\n for (const e of entries) ratios.set(e.target, e.isIntersecting ? e.intersectionRatio : 0);\n elect();\n },\n { threshold: [0, 0.25, 0.5, 0.6, 0.75, 1] },\n );\n ratios.set(el, 0);\n observer.observe(el);\n return () => {\n observer?.unobserve(el);\n ratios.delete(el);\n elect();\n };\n}\n\nexport const subscribeLeader = (l: () => void) => (listeners.add(l), () => void listeners.delete(l));\n\n/** Whether this element is the card the page is showing most. */\nexport const isLeader = (el: Element | null) => el !== null && leader === el;\n\n/** Test seam: forget everything. */\nexport function resetPreviewFocus() {\n observer?.disconnect();\n observer = null;\n ratios.clear();\n leader = null;\n}\n","// A video card on its way: the same frame, body and footer heights as `VideoCard`, so nothing moves when it arrives.\nimport * as React from \"react\";\n\nimport { Card } from \"../card\";\nimport { cn } from \"../cn\";\nimport { Skeleton } from \"../skeleton\";\nimport type { VideoCardAspect } from \"./video-card\";\n\nexport interface VideoCardSkeletonProps extends React.ComponentProps<\"div\"> {\n /** `landscape` (16:9) or `portrait` (9:16), as the card it stands for. @default \"landscape\" */\n aspect?: VideoCardAspect;\n /** `stack` or `row`, as the card it stands for. @default \"stack\" */\n layout?: \"stack\" | \"row\";\n}\n\nconst FRAME = { landscape: \"aspect-video\", portrait: \"aspect-[9/16]\" } as const;\n\n/** The placeholder of a `VideoCard` (decorative: announce the wait on the list, with `MediaGridSkeleton`). */\nexport function VideoCardSkeleton({ aspect = \"landscape\", layout = \"stack\", className, ...props }: VideoCardSkeletonProps) {\n const row = layout === \"row\";\n const tall = aspect === \"portrait\" && !row;\n return (\n <div aria-hidden data-skeleton=\"video-card\" data-aspect={aspect} data-layout={layout} className={cn(\"h-full min-w-0\", className)} {...props}>\n <Card.Root tone=\"glass\" className={cn(\"h-full w-full max-w-full\", row ? \"flex-row\" : \"flex-col\")}>\n <Card.Media aspect=\"auto\" className={cn(\"shrink-0\", FRAME[aspect], row && (aspect === \"portrait\" ? \"w-24 sm:w-28\" : \"w-40 sm:w-56\"))}>\n <Skeleton shape=\"rect\" className=\"h-full w-full rounded-none\" />\n </Card.Media>\n <Card.Body className=\"static min-w-0 flex-1 justify-between gap-2 p-3\">\n <div className=\"flex min-h-[2.5em] flex-col justify-center gap-1.5\">\n <Skeleton className=\"h-3\" />\n <Skeleton className=\"h-3 w-3/5\" />\n </div>\n <div className={cn(\"flex text-xs\", tall ? \"flex-col gap-1\" : \"h-8 items-center gap-2\")}>\n <div className=\"flex h-6 min-w-0 items-center gap-2\">\n <Skeleton shape=\"circle\" className=\"h-6 w-6 shrink-0\" />\n <Skeleton className=\"h-2.5 w-20\" />\n </div>\n <div className={cn(\"flex items-center\", tall ? \"h-7\" : \"ml-auto\")}>\n <Skeleton className=\"h-2.5 w-14\" />\n </div>\n </div>\n </Card.Body>\n </Card.Root>\n </div>\n );\n}\n\nexport interface MediaGridSkeletonProps extends Omit<React.ComponentProps<\"div\">, \"children\"> {\n /** What is loading, passed translated (\"Loading videos\"): announced once, the cards are hidden from assistive technology. */\n label: string;\n /** Cards in the grid. @default 8 */\n count?: number;\n /** Cards on the Shorts shelf above the grid (`split` mode); 0 for none. @default 0 */\n shelfCount?: number;\n /** `grid` or `list`. @default \"grid\" */\n layout?: \"grid\" | \"list\";\n /** The frame of the grid. @default \"landscape\" */\n frame?: VideoCardAspect;\n}\n\nconst GRID = {\n landscape: \"grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4\",\n portrait: \"grid-cols-2 sm:grid-cols-3 lg:grid-cols-5 xl:grid-cols-6\",\n} as const;\n\n/** The placeholder of a `MediaGrid`: the same shelf and grid, skeleton cards, one polite announcement. */\nexport function MediaGridSkeleton({ label, count = 8, shelfCount = 0, layout = \"grid\", frame = \"landscape\", className, ...props }: MediaGridSkeletonProps) {\n const row = layout === \"list\";\n return (\n <div role=\"status\" aria-busy=\"true\" data-media-grid-skeleton=\"\" className={cn(\"flex min-w-0 flex-col gap-8\", className)} {...props}>\n <span className=\"sr-only\">{label}</span>\n {shelfCount > 0 && (\n <div aria-hidden className=\"min-w-0\">\n <Skeleton className=\"mb-3 h-4 w-24\" />\n <ul className=\"m-0 flex list-none items-stretch gap-3 overflow-hidden p-0\">\n {Array.from({ length: shelfCount }, (_, i) => (\n <li key={i} className=\"w-[9.5rem] shrink-0 sm:w-44\">\n <VideoCardSkeleton aspect=\"portrait\" />\n </li>\n ))}\n </ul>\n </div>\n )}\n <ul aria-hidden className={cn(\"m-0 grid list-none auto-rows-fr gap-4 p-0\", row ? \"grid-cols-1\" : GRID[frame])}>\n {Array.from({ length: count }, (_, i) => (\n <li key={i} className=\"min-w-0\">\n <VideoCardSkeleton aspect={frame} layout={row ? \"row\" : \"stack\"} />\n </li>\n ))}\n </ul>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAGA,YAAY,WAAW;;;ACAvB,SAAS,UAA6B;AA6BlC;AAzBG,IAAM,kBAAkB,GAAG;AAAA,EAChC,MAAM;AAAA,EACN,UAAU,EAAE,MAAM,EAAE,IAAI,WAAW,IAAI,WAAW,IAAI,YAAY,EAAE;AAAA,EACpE,iBAAiB,EAAE,MAAM,KAAK;AAChC,CAAC;AAmBM,SAAS,QAAQ,EAAE,OAAO,MAAM,OAAO,WAAW,GAAG,MAAM,GAAiB;AACjF,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,cAAY;AAAA,MACZ,eAAa;AAAA,MACb;AAAA,MACA,aAAW,QAAQ;AAAA,MACnB,WAAW,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAAA,MAC7C,GAAG;AAAA;AAAA,EACN;AAEJ;;;ADOI,SACE,OAAAA,MADF;AAfG,SAAS,SAAS,EAAE,YAAY,SAAS,UAAU,OAAO,SAAS,OAAO,OAAO,cAAc,YAAY,UAAU,aAAa,aAAa,WAAW,GAAG,MAAM,GAAkB;AAC1L,QAAM,WAAiB,aAAuB,IAAI;AAClD,QAAM,MAAY,aAAO,UAAU;AACnC,EAAM,gBAAU,MAAM;AACpB,QAAI,UAAU;AAAA,EAChB,CAAC;AAED,EAAM,gBAAU,MAAM;AACpB,UAAM,KAAK,SAAS;AACpB,QAAI,CAAC,MAAM,CAAC,WAAW,WAAW,UAAU,OAAO,yBAAyB,YAAa;AACzF,UAAM,KAAK,IAAI,qBAAqB,CAAC,YAAY,QAAQ,KAAK,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,QAAQ,GAAG,EAAE,WAAW,CAAC;AACvH,OAAG,QAAQ,EAAE;AACb,WAAO,MAAM,GAAG,WAAW;AAAA,EAC7B,GAAG,CAAC,SAAS,SAAS,QAAQ,UAAU,CAAC;AACzC,SACE,qBAAC,SAAI,kBAAe,IAAG,cAAY,UAAU,YAAY,SAAS,WAAW,UAAU,SAAS,OAAO,WAAW,GAAG,2DAA2D,SAAS,GAAI,GAAG,OAC9L;AAAA,oBAAAA,KAAC,SAAI,KAAK,UAAU,eAAW,MAAC,WAAU,qDAAoD;AAAA,IAC7F,UACC,gBAAAA,KAAC,WAAQ,OAAO,cAAc,MAAK,MAAK,IACtC,UACF,gBAAAA,KAAC,UAAO,MAAK,UAAS,SAAQ,aAAY,MAAK,MAAK,SAAS,MAAM,WAAW,GAC3E,mBAAU,cAAc,QAAS,OACpC,IACE,WACF,gBAAAA,KAAC,OAAE,WAAU,iCAAiC,oBAAS,IACrD;AAAA,KACN;AAEJ;;;AE3DA,YAAYC,YAAW;;;ACDvB,YAAYC,YAAW;;;ACgBA,gBAAAC,YAAA;AAThB,IAAM,aAAa,CAAC,SAAiB,uBAAuB,KAAK,IAAI;AAErE,SAAS,UAAU;AAAA,EACxB;AAAA,EACA,QAAQ,OAAO;AAAA,EACf,WAAW,WAAW,IAAI;AAAA,EAC1B;AAAA,EACA,GAAG;AACL,GAA2G;AACzG,MAAI,SAAU,QAAO,gBAAAA,KAAC,OAAE,MAAY,QAAO,UAAS,KAAK,OAAO,uBAAwB,GAAG,OAAO;AAClG,QAAM,OAAO;AACb,SAAO,gBAAAA,KAAC,QAAK,MAAY,KAAW,GAAG,OAAO;AAChD;;;ADmBQ,SAaE,UAZA,OAAAC,MADF,QAAAC,aAAA;AAvBD,SAAS,YAAY,EAAE,OAAO,MAAM,KAAK,GAAqB;AACnE,QAAM,CAAC,MAAM,OAAO,IAAU,gBAAS,KAAK;AAC5C,QAAM,KAAW,aAAM;AACvB,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,cAAc,MAAM,QAAQ,IAAI;AAAA,MAChC,cAAc,MAAM,QAAQ,KAAK;AAAA,MACjC,QAAQ,CAAC,MAAM;AACb,YAAI,CAAC,EAAE,cAAc,SAAS,EAAE,aAAa,EAAG,SAAQ,KAAK;AAAA,MAC/D;AAAA,MACA,WAAW,CAAC,MAAM;AAChB,YAAI,EAAE,QAAQ,SAAU,SAAQ,KAAK;AAAA,MACvC;AAAA,MAEA;AAAA,wBAAAD;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,cAAY;AAAA,YACZ,iBAAe;AAAA,YACf,iBAAe;AAAA,YACf,SAAS,MAAM,QAAQ,CAAC,MAAM,CAAC,CAAC;AAAA,YAChC,WAAU;AAAA,YAEV,0BAAAC,MAAC,SAAI,SAAQ,aAAY,OAAM,MAAK,QAAO,MAAK,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,eAAW,MACjI;AAAA,8BAAAD,KAAC,YAAO,IAAG,MAAK,IAAG,MAAK,GAAE,KAAI;AAAA,cAC9B,gBAAAA,KAAC,UAAK,GAAE,qBAAoB;AAAA,eAC9B;AAAA;AAAA,QACF;AAAA,QACA,gBAAAC;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA,MAAK;AAAA,YACL,QAAQ,CAAC;AAAA,YACT,WAAW,GAAG,4KAA4K;AAAA,YAEzL;AAAA;AAAA,cACA,QACC,gBAAAA,MAAA,YACG;AAAA;AAAA,gBACD,gBAAAD,KAAC,aAAU,MAAM,KAAK,MAAM,QAAQ,KAAK,QAAQ,WAAU,gDACxD,eAAK,OACR;AAAA,iBACF;AAAA;AAAA;AAAA,QAEJ;AAAA;AAAA;AAAA,EACF;AAEJ;;;AE7DA,YAAYE,YAAW;AA6BnB,gBAAAC,MAyBE,QAAAC,aAzBF;AAFJ,IAAM,UAAU,CAAC,EAAE,KAAK,MACtB,gBAAAD,KAAC,SAAI,SAAQ,aAAY,OAAM,MAAK,QAAO,MAAK,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,eAAW,MAAC,WAAW,OAAO,eAAe,QAC1L,0BAAAA,KAAC,UAAK,GAAE,gBAAe,GACzB;AAGF,IAAM,QACJ;AAGK,SAAS,YAAe,EAAE,OAAO,OAAO,MAAM,OAAO,OAAO,YAAY,QAAQ,eAAe,WAAW,WAAW,GAAG,MAAM,GAAwB;AAC3J,QAAM,MAAY,cAAyB,IAAI;AAC/C,QAAM,CAAC,MAAM,OAAO,IAAU,gBAAS,EAAE,OAAO,MAAM,KAAK,MAAM,CAAC;AAClE,QAAM,UAAgB,mBAAY,MAAM;AACtC,UAAM,KAAK,IAAI;AACf,QAAI,CAAC,GAAI;AACT,YAAQ,EAAE,OAAO,GAAG,cAAc,GAAG,KAAK,GAAG,aAAa,GAAG,eAAe,GAAG,cAAc,EAAE,CAAC;AAAA,EAClG,GAAG,CAAC,CAAC;AACL,EAAM,iBAAU,SAAS,CAAC,SAAS,MAAM,MAAM,CAAC;AAChD,QAAM,OAAO,CAAC,QAAgB;AAC5B,UAAM,KAAK,IAAI;AACf,QAAI,GAAI,IAAG,SAAS,EAAE,MAAM,MAAM,GAAG,cAAc,MAAM,UAAU,OAAO,WAAW,kCAAkC,EAAE,UAAU,SAAS,SAAS,CAAC;AAAA,EACxJ;AACA,QAAM,YAAkB,aAAM;AAC9B,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,SACE,gBAAAC,MAAC,aAAQ,mBAAiB,WAAW,qBAAiB,MAAC,WAAW,GAAG,WAAW,SAAS,GAAI,GAAG,OAC9F;AAAA,oBAAAA,MAAC,SAAI,WAAU,gCACb;AAAA,sBAAAD,KAAC,QAAG,IAAI,WAAW,WAAU,2CAC1B,mBAAS,OACZ;AAAA,MACC,QAAQ,gBAAAA,KAAC,eAAa,GAAG,MAAM;AAAA,MAC/B,UACC,gBAAAA,KAAC,aAAU,MAAM,OAAO,MAAM,QAAQ,OAAO,QAAQ,WAAU,wDAC5D,iBAAO,OACV;AAAA,MAEF,gBAAAA,KAAC,YAAO,MAAK,UAAS,cAAY,eAAe,UAAU,KAAK,OAAO,SAAS,MAAM,KAAK,EAAE,GAAG,WAAW,OACzG,0BAAAA,KAAC,WAAQ,MAAI,MAAC,GAChB;AAAA,MACA,gBAAAA,KAAC,YAAO,MAAK,UAAS,cAAY,WAAW,UAAU,KAAK,KAAK,SAAS,MAAM,KAAK,CAAC,GAAG,WAAW,OAClG,0BAAAA,KAAC,WAAQ,GACX;AAAA,OACF;AAAA,IACA,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,KAAK;AAAA,QACL,UAAU;AAAA,QAEV,UAAU;AAAA,QACV,mBAAiB;AAAA,QACjB,WAAU;AAAA,QAET,gBAAM,IAAI,CAAC,MAAM,MAChB,gBAAAA,KAAC,QAAqB,WAAU,0CAC7B,qBAAW,MAAM,CAAC,KADZ,MAAM,IAAI,CAEnB,CACD;AAAA;AAAA,IACH;AAAA,KACF;AAEJ;;;AH9B+B,gBAAAE,MAInB,QAAAC,aAJmB;AAhB/B,IAAM,OAAO;AAAA,EACX,WAAW;AAAA,EACX,UAAU;AACZ;AAGO,SAAS,UAAa,EAAE,OAAO,OAAO,eAAe,YAAY,OAAO,SAAS,QAAQ,aAAa,SAAS,QAAQ,OAAO,MAAM,WAAW,GAAG,MAAM,GAAsB;AACnL,QAAM,cAAoB,aAAM;AAChC,QAAM,MAAM,WAAW;AACvB,QAAM,QAAQ,SAAS;AACvB,QAAM,YAAY,QAAQ,MAAM,OAAO,CAAC,MAAM,cAAc,CAAC,MAAM,UAAU,IAAI,CAAC;AAClF,QAAM,OAAO,QAAQ,MAAM,OAAO,CAAC,MAAM,cAAc,CAAC,MAAM,UAAU,IAAI;AAC5E,QAAM,YAA6B,QAAQ,cAAc;AACzD,QAAM,QAAQ,CAAC,MAAS,YAA6C,EAAE,QAAQ,QAAQ,MAAM,QAAQ,SAAS,UAAU,cAAc,IAAI,MAAM,OAAO;AACvJ,SACE,gBAAAA,MAAC,SAAI,mBAAiB,MAAM,WAAW,GAAG,+BAA+B,SAAS,GAAI,GAAG,OACtF;AAAA,cAAU,SAAS,KAAK,gBAAAD,KAAC,eAAa,GAAG,OAAO,OAAO,WAAW,OAAc,YAAY,CAAC,SAAS,WAAW,MAAM,EAAE,GAAG,MAAM,MAAM,UAAU,GAAG,QAAQ,QAAQ,CAAC,GAAG;AAAA,IACzK,KAAK,SAAS,KACb,gBAAAC,MAAC,aAAQ,mBAAiB,OAAO,cAAc,QAAW,2BAAuB,MAAC,WAAU,WACzF;AAAA,cACC,gBAAAA,MAAC,SAAI,WAAU,gCACb;AAAA,wBAAAD,KAAC,QAAG,IAAI,aAAa,WAAU,mCAC5B,eAAK,OACR;AAAA,QACC,KAAK,QAAQ,gBAAAA,KAAC,eAAa,GAAG,KAAK,MAAM;AAAA,SAC5C;AAAA,MAEF,gBAAAA,KAAC,QAAG,mBAAiB,WAAW,WAAW,GAAG,6CAA6C,MAAM,gBAAgB,KAAK,SAAS,CAAC,GAC7H,eAAK,IAAI,CAAC,SACT,gBAAAA,KAAC,QAAqB,WAAU,WAC7B,qBAAW,MAAM,MAAM,MAAM,SAAS,CAAC,KADjC,MAAM,IAAI,CAEnB,CACD,GACH;AAAA,OACF;AAAA,KAEJ;AAEJ;;;AI9EA,YAAYE,YAAW;;;ACAvB,IAAM,YAAY;AAClB,IAAM,SAAS,oBAAI,IAAqB;AACxC,IAAM,YAAY,oBAAI,IAAgB;AACtC,IAAI,WAAwC;AAC5C,IAAI,SAAyB;AAE7B,SAAS,QAAQ;AACf,MAAI,OAAuB;AAC3B,MAAI,YAAY,YAAY,OAAO;AACnC,aAAW,CAAC,IAAI,KAAK,KAAK,QAAQ;AAChC,QAAI,QAAQ,WAAW;AACrB,aAAO;AACP,kBAAY;AAAA,IACd;AAAA,EACF;AACA,MAAI,SAAS,OAAQ;AACrB,WAAS;AACT,aAAW,KAAK,UAAW,GAAE;AAC/B;AAGO,SAAS,gBAAgB,IAAyB;AACvD,MAAI,OAAO,yBAAyB,YAAa,QAAO,MAAM;AAAA,EAAC;AAC/D,eAAa,IAAI;AAAA,IACf,CAAC,YAAY;AACX,iBAAW,KAAK,QAAS,QAAO,IAAI,EAAE,QAAQ,EAAE,iBAAiB,EAAE,oBAAoB,CAAC;AACxF,YAAM;AAAA,IACR;AAAA,IACA,EAAE,WAAW,CAAC,GAAG,MAAM,KAAK,KAAK,MAAM,CAAC,EAAE;AAAA,EAC5C;AACA,SAAO,IAAI,IAAI,CAAC;AAChB,WAAS,QAAQ,EAAE;AACnB,SAAO,MAAM;AACX,cAAU,UAAU,EAAE;AACtB,WAAO,OAAO,EAAE;AAChB,UAAM;AAAA,EACR;AACF;AAEO,IAAM,kBAAkB,CAAC,OAAmB,UAAU,IAAI,CAAC,GAAG,MAAM,KAAK,UAAU,OAAO,CAAC;AAG3F,IAAM,WAAW,CAAC,OAAuB,OAAO,QAAQ,WAAW;;;ADmGlE,SACkC,OAAAC,MADlC,QAAAC,aAAA;AA1DR,IAAM,QAAQ,EAAE,WAAW,gBAAgB,UAAU,gBAAgB;AAG9D,SAAS,UAAU;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS;AAAA,EACT,SAAS;AAAA,EACT;AAAA,EACA;AAAA,EACA,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,MAAM;AAAA,EACN;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV;AAAA,EACA,UAAU;AAAA,EACV;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAmB;AACjB,QAAM,CAAC,MAAM,OAAO,IAAU,gBAA6B,IAAI;AAC/D,QAAM,SAAe;AAAA,IACnB,CAAC,OAA2B;AAC1B,cAAQ,EAAE;AACV,UAAI,OAAO,QAAQ,WAAY,KAAI,EAAE;AAAA,eAC5B,IAAK,CAAC,IAA4C,UAAU;AAAA,IACvE;AAAA,IACA,CAAC,GAAG;AAAA,EACN;AACA,QAAM,aAAa,QAAQ,UAAU,KAAK,CAAC;AAC3C,QAAM,EAAE,MAAM,KAAK,OAAO,IAAI,iBAAiB,MAAM,YAAY,YAAY;AAC7E,QAAM,MAAM,WAAW;AACvB,QAAM,OAAO,WAAW,cAAc,CAAC;AACvC,SACE,gBAAAD;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,eAAa;AAAA,MACb,eAAa;AAAA,MACb,WAAW,GAAG,iCAAiC,SAAS;AAAA,MACxD,gBAAc,OAAO,OAAO;AAAA,MAC5B,cAAc,CAAC,OAAO,IAAI,SAAS,GAAG,eAAe,CAAC;AAAA,MACtD,cAAc,CAAC,OAAO,OAAO,SAAS,GAAG,eAAe,CAAC;AAAA,MACzD,SAAS,CAAC,OAAO,IAAI,OAAO,GAAG,UAAU,CAAC;AAAA,MAC1C,QAAQ,CAAC,OAAO,OAAO,OAAO,GAAG,SAAS,CAAC;AAAA,MAC1C,GAAG;AAAA,MAEJ,0BAAAC,MAAC,KAAK,MAAL,EAAU,aAAW,MAAC,MAAK,SAAQ,WAAW,GAAG,4BAA4B,MAAM,aAAa,UAAU,GACzG;AAAA,wBAAAA,MAAC,KAAK,OAAL,EAAW,QAAO,QAAO,WAAW,GAAG,YAAY,MAAM,MAAM,GAAG,QAAQ,WAAW,aAAa,iBAAiB,eAAe,GAChI;AAAA,kBAAQ,aAAa,UAAU,gBAAAD,KAAC,SAAI,KAAK,QAAQ,KAAI,IAAG,eAAW,MAAC,SAAQ,QAAO,WAAU,6EAA4E;AAAA,UAC1K,gBAAAA;AAAA,YAAC,KAAK;AAAA,YAAL;AAAA,cACC,KAAK;AAAA,cACL;AAAA,cACA,eAAe,mBAAmB,SAAS,SAAY;AAAA,cACvD;AAAA,cACA,WAAW,GAAG,QAAQ,aAAa,2BAA2B,WAAW,mBAAmB;AAAA;AAAA,UAC9F;AAAA,UACC,QAAQ,cAAc,gBAAAA,KAAC,gBAAa,KAAK,YAAY,SAAS,QAAQ,WAAW;AAAA,UACjF,WAAW,gBAAAA,KAAC,SAAI,WAAU,8BAA8B,mBAAQ;AAAA,UAChE,UACC,gBAAAA,KAAC,KAAK,SAAL,EAAa,QAAO,YAAW,WAAU,4CACvC,kBACH;AAAA,UAED,YACC,gBAAAA,KAAC,KAAK,SAAL,EAAa,QAAO,gBAAe,WAAU,uBAC5C,0BAAAA,KAAC,UAAK,WAAU,4EAA4E,oBAAS,GACvG;AAAA,WAEJ;AAAA,QAEA,gBAAAC,MAAC,KAAK,MAAL,EAAU,WAAU,mDACnB;AAAA,0BAAAD,KAAC,QAAG,WAAU,iEACZ,0BAAAA,KAAC,aAAU,MAAY,QAAgB,WAAU,gJAC9C,iBACH,GACF;AAAA,UAEA,gBAAAC,MAAC,SAAI,WAAW,GAAG,2CAA2C,OAAO,wBAAwB,6BAA6B,GACvH;AAAA,uBACC,gBAAAA,MAAC,SAAI,WAAU,uCACb;AAAA,8BAAAD,KAAC,UAAO,KAAK,QAAQ,WAAW,eAAe,QAAQ,eAAe,KAAI,IAAG,UAAU,QAAQ,KAAK,OAAO,CAAC,GAAG,MAAK,MAAK,WAAU,YAAW;AAAA,cAC7I,QAAQ,OACP,gBAAAA,KAAC,aAAU,MAAM,QAAQ,MAAM,QAAgB,WAAU,kCACtD,kBAAQ,MACX,IAEA,gBAAAA,KAAC,UAAK,WAAU,oBAAoB,kBAAQ,MAAK;AAAA,eAErD;AAAA,aAEA,QAAQ,WACR,gBAAAC,MAAC,SAAI,WAAW,GAAG,2BAA2B,OAAO,wBAAwB,kBAAkB,GAC5F;AAAA,sBAAQ,gBAAAD,KAAC,UAAK,WAAU,gDAAgD,gBAAK;AAAA,cAC7E,UAAU,gBAAAA,KAAC,UAAK,WAAU,YAAY,kBAAO;AAAA,eAChD;AAAA,aAEJ;AAAA,WACF;AAAA,SACF;AAAA;AAAA,EACF;AAEJ;AAGA,SAAS,aAAa,EAAE,KAAK,QAAQ,GAAsC;AACzE,QAAM,CAAC,OAAO,QAAQ,IAAU,gBAAS,KAAK;AAC9C,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,KAAI;AAAA,MACJ,eAAW;AAAA,MACX,UAAS;AAAA,MACT,sBAAmB;AAAA,MACnB,cAAY,QAAQ,KAAK;AAAA,MACzB,QAAQ,MAAM,SAAS,IAAI;AAAA,MAC3B,WAAW,GAAG,uJAAuJ,UAAU,mBAAmB,cAAc;AAAA;AAAA,EAClN;AAEJ;AAIA,IAAM,UAAU,CAAC,UAAkB,OAAO,WAAW,eAAe,OAAO,OAAO,eAAe,cAAc,OAAO,WAAW,KAAK,EAAE;AACxI,IAAM,eAAe,MAAM,QAAQ,gBAAgB;AACnD,IAAM,WAAW,MAAM,OAAO,cAAc,eAAgB,UAAkE,YAAY,aAAa;AAMvJ,SAAS,iBAAiB,MAA0B,SAAkB,OAAe;AACnF,QAAM,CAAC,QAAQ,SAAS,IAAU,gBAA6B,oBAAI,IAAI,CAAC;AACxE,QAAM,SAAe,cAAO,oBAAI,IAA0C,CAAC;AAC3E,QAAM,MAAY;AAAA,IAChB,CAAC,UAAiB;AAChB,UAAI,CAAC,WAAW,SAAS,EAAG;AAC5B,UAAI,UAAU,aAAa,CAAC,aAAa,EAAG;AAC5C,mBAAa,OAAO,QAAQ,IAAI,KAAK,CAAC;AACtC,aAAO,QAAQ;AAAA,QACb;AAAA,QACA,WAAW,MAAM,UAAU,CAAC,MAAM,IAAI,IAAI,CAAC,EAAE,IAAI,KAAK,CAAC,GAAG,KAAK;AAAA,MACjE;AAAA,IACF;AAAA,IACA,CAAC,SAAS,KAAK;AAAA,EACjB;AACA,QAAM,SAAe,mBAAY,CAAC,UAAiB;AACjD,iBAAa,OAAO,QAAQ,IAAI,KAAK,CAAC;AACtC,WAAO,QAAQ,OAAO,KAAK;AAC3B,cAAU,CAAC,MAAM;AACf,UAAI,CAAC,EAAE,IAAI,KAAK,EAAG,QAAO;AAC1B,YAAM,OAAO,IAAI,IAAI,CAAC;AACtB,WAAK,OAAO,KAAK;AACjB,aAAO;AAAA,IACT,CAAC;AAAA,EACH,GAAG,CAAC,CAAC;AACL,EAAM,iBAAU,MAAM;AACpB,UAAM,UAAU,OAAO;AACvB,WAAO,MAAM;AACX,iBAAW,KAAK,QAAQ,OAAO,EAAG,cAAa,CAAC;AAChD,cAAQ,MAAM;AAAA,IAChB;AAAA,EACF,GAAG,CAAC,CAAC;AAGL,QAAM,QAAQ,WAAW,CAAC,aAAa;AACvC,QAAM,UAAgB;AAAA,IACpB;AAAA,IACA,MAAM,SAAS,IAAI;AAAA,IACnB,MAAM;AAAA,EACR;AACA,EAAM,iBAAU,MAAM;AACpB,QAAI,CAAC,QAAQ,CAAC,MAAO;AACrB,WAAO,gBAAgB,IAAI;AAAA,EAC7B,GAAG,CAAC,MAAM,KAAK,CAAC;AAChB,QAAM,CAAC,OAAO,QAAQ,IAAU,gBAAS,KAAK;AAC9C,QAAM,CAAC,YAAY,aAAa,IAAU,gBAAS,KAAK;AACxD,MAAI,YAAY,YAAY;AAC1B,kBAAc,OAAO;AACrB,QAAI,CAAC,QAAS,UAAS,KAAK;AAAA,EAC9B;AACA,QAAM,SAAS,SAAS,WAAW,CAAC,SAAS,KAAK,CAAC,QAAQ,kCAAkC;AAC7F,EAAM,iBAAU,MAAM;AACpB,QAAI,CAAC,OAAQ;AACb,UAAM,IAAI,WAAW,MAAM,SAAS,IAAI,GAAG,KAAK;AAChD,WAAO,MAAM,aAAa,CAAC;AAAA,EAC7B,GAAG,CAAC,QAAQ,KAAK,CAAC;AAElB,SAAO,EAAE,MAAM,YAAY,OAAO,OAAO,KAAM,UAAU,QAAS,KAAK,OAAO;AAChF;;;AEpQU,gBAAAE,MAGA,QAAAC,aAHA;AAVV,IAAMC,SAAQ,EAAE,WAAW,gBAAgB,UAAU,gBAAgB;AAG9D,SAAS,kBAAkB,EAAE,SAAS,aAAa,SAAS,SAAS,WAAW,GAAG,MAAM,GAA2B;AACzH,QAAM,MAAM,WAAW;AACvB,QAAM,OAAO,WAAW,cAAc,CAAC;AACvC,SACE,gBAAAF,KAAC,SAAI,eAAW,MAAC,iBAAc,cAAa,eAAa,QAAQ,eAAa,QAAQ,WAAW,GAAG,kBAAkB,SAAS,GAAI,GAAG,OACpI,0BAAAC,MAAC,KAAK,MAAL,EAAU,MAAK,SAAQ,WAAW,GAAG,4BAA4B,MAAM,aAAa,UAAU,GAC7F;AAAA,oBAAAD,KAAC,KAAK,OAAL,EAAW,QAAO,QAAO,WAAW,GAAG,YAAYE,OAAM,MAAM,GAAG,QAAQ,WAAW,aAAa,iBAAiB,eAAe,GACjI,0BAAAF,KAAC,YAAS,OAAM,QAAO,WAAU,8BAA6B,GAChE;AAAA,IACA,gBAAAC,MAAC,KAAK,MAAL,EAAU,WAAU,mDACnB;AAAA,sBAAAA,MAAC,SAAI,WAAU,sDACb;AAAA,wBAAAD,KAAC,YAAS,WAAU,OAAM;AAAA,QAC1B,gBAAAA,KAAC,YAAS,WAAU,aAAY;AAAA,SAClC;AAAA,MACA,gBAAAC,MAAC,SAAI,WAAW,GAAG,gBAAgB,OAAO,mBAAmB,wBAAwB,GACnF;AAAA,wBAAAA,MAAC,SAAI,WAAU,uCACb;AAAA,0BAAAD,KAAC,YAAS,OAAM,UAAS,WAAU,oBAAmB;AAAA,UACtD,gBAAAA,KAAC,YAAS,WAAU,cAAa;AAAA,WACnC;AAAA,QACA,gBAAAA,KAAC,SAAI,WAAW,GAAG,qBAAqB,OAAO,QAAQ,SAAS,GAC9D,0BAAAA,KAAC,YAAS,WAAU,cAAa,GACnC;AAAA,SACF;AAAA,OACF;AAAA,KACF,GACF;AAEJ;AAeA,IAAMG,QAAO;AAAA,EACX,WAAW;AAAA,EACX,UAAU;AACZ;AAGO,SAAS,kBAAkB,EAAE,OAAO,QAAQ,GAAG,aAAa,GAAG,SAAS,QAAQ,QAAQ,aAAa,WAAW,GAAG,MAAM,GAA2B;AACzJ,QAAM,MAAM,WAAW;AACvB,SACE,gBAAAF,MAAC,SAAI,MAAK,UAAS,aAAU,QAAO,4BAAyB,IAAG,WAAW,GAAG,+BAA+B,SAAS,GAAI,GAAG,OAC3H;AAAA,oBAAAD,KAAC,UAAK,WAAU,WAAW,iBAAM;AAAA,IAChC,aAAa,KACZ,gBAAAC,MAAC,SAAI,eAAW,MAAC,WAAU,WACzB;AAAA,sBAAAD,KAAC,YAAS,WAAU,iBAAgB;AAAA,MACpC,gBAAAA,KAAC,QAAG,WAAU,8DACX,gBAAM,KAAK,EAAE,QAAQ,WAAW,GAAG,CAAC,GAAG,MACtC,gBAAAA,KAAC,QAAW,WAAU,+BACpB,0BAAAA,KAAC,qBAAkB,QAAO,YAAW,KAD9B,CAET,CACD,GACH;AAAA,OACF;AAAA,IAEF,gBAAAA,KAAC,QAAG,eAAW,MAAC,WAAW,GAAG,6CAA6C,MAAM,gBAAgBG,MAAK,KAAK,CAAC,GACzG,gBAAM,KAAK,EAAE,QAAQ,MAAM,GAAG,CAAC,GAAG,MACjC,gBAAAH,KAAC,QAAW,WAAU,WACpB,0BAAAA,KAAC,qBAAkB,QAAQ,OAAO,QAAQ,MAAM,QAAQ,SAAS,KAD1D,CAET,CACD,GACH;AAAA,KACF;AAEJ;","names":["jsx","React","React","jsx","jsx","jsxs","React","jsx","jsxs","jsx","jsxs","React","jsx","jsxs","jsx","jsxs","FRAME","GRID"]}
@@ -0,0 +1,27 @@
1
+ import * as React from 'react';
2
+
3
+ type ActionUrgency = "low" | "medium" | "high";
4
+ interface NextAction {
5
+ /** A stable key supplied by the host. */
6
+ id: string;
7
+ /** The action's short name. */
8
+ title: React.ReactNode;
9
+ /** Why it matters, in one line. */
10
+ why: string;
11
+ /** How soon it matters. */
12
+ urgency: ActionUrgency;
13
+ /** The host's translated urgency word. */
14
+ urgencyLabel: React.ReactNode;
15
+ /** A host-rendered link, button or command for a developer tool. */
16
+ action: React.ReactNode;
17
+ }
18
+ interface NextActionsProps extends Omit<React.ComponentProps<"section">, "children" | "title"> {
19
+ /** A heading such as “Que faire maintenant”. */
20
+ title: React.ReactNode;
21
+ /** Two or three actions, already ordered by the host from most to least important. */
22
+ actions: readonly [NextAction, NextAction] | readonly [NextAction, NextAction, NextAction];
23
+ }
24
+ /** “What to do now”: two or three ranked actions, with their reason and urgency. */
25
+ declare function NextActions({ title, actions, className, ...props }: NextActionsProps): React.JSX.Element;
26
+
27
+ export { type ActionUrgency, type NextAction, NextActions, type NextActionsProps };
@@ -0,0 +1,27 @@
1
+ // src/next-actions/next-actions.tsx
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ function NextActions({ title, actions, className, ...props }) {
4
+ return /* @__PURE__ */ jsxs("section", { className: ["kz-next-actions", className].filter(Boolean).join(" "), ...props, children: [
5
+ /* @__PURE__ */ jsx("h2", { className: "kz-next-actions-title", children: title }),
6
+ /* @__PURE__ */ jsx("ol", { className: "kz-next-actions-list", children: actions.map((action, index) => /* @__PURE__ */ jsxs("li", { className: "kz-next-action", "data-urgency": action.urgency, children: [
7
+ /* @__PURE__ */ jsxs("span", { className: "kz-next-action-rank", "aria-hidden": "true", children: [
8
+ index + 1,
9
+ "."
10
+ ] }),
11
+ /* @__PURE__ */ jsxs("div", { className: "kz-next-action-content", children: [
12
+ /* @__PURE__ */ jsxs("div", { className: "kz-next-action-copy", children: [
13
+ /* @__PURE__ */ jsxs("div", { className: "kz-next-action-heading", children: [
14
+ /* @__PURE__ */ jsx("h3", { children: action.title }),
15
+ /* @__PURE__ */ jsx("span", { className: "kz-next-action-urgency", "data-urgency": action.urgency, children: action.urgencyLabel })
16
+ ] }),
17
+ /* @__PURE__ */ jsx("p", { children: action.why })
18
+ ] }),
19
+ /* @__PURE__ */ jsx("div", { className: "kz-next-action-control", children: action.action })
20
+ ] })
21
+ ] }, action.id)) })
22
+ ] });
23
+ }
24
+ export {
25
+ NextActions
26
+ };
27
+ //# sourceMappingURL=next-actions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/next-actions/next-actions.tsx"],"sourcesContent":["// Server-safe. A ranked list from the host: this block never creates an action of its own.\nimport type * as React from \"react\";\n\nexport type ActionUrgency = \"low\" | \"medium\" | \"high\";\n\nexport interface NextAction {\n /** A stable key supplied by the host. */\n id: string;\n /** The action's short name. */\n title: React.ReactNode;\n /** Why it matters, in one line. */\n why: string;\n /** How soon it matters. */\n urgency: ActionUrgency;\n /** The host's translated urgency word. */\n urgencyLabel: React.ReactNode;\n /** A host-rendered link, button or command for a developer tool. */\n action: React.ReactNode;\n}\n\nexport interface NextActionsProps extends Omit<React.ComponentProps<\"section\">, \"children\" | \"title\"> {\n /** A heading such as “Que faire maintenant”. */\n title: React.ReactNode;\n /** Two or three actions, already ordered by the host from most to least important. */\n actions: readonly [NextAction, NextAction] | readonly [NextAction, NextAction, NextAction];\n}\n\n/** “What to do now”: two or three ranked actions, with their reason and urgency. */\nexport function NextActions({ title, actions, className, ...props }: NextActionsProps) {\n return (\n <section className={[\"kz-next-actions\", className].filter(Boolean).join(\" \")} {...props}>\n <h2 className=\"kz-next-actions-title\">{title}</h2>\n <ol className=\"kz-next-actions-list\">\n {actions.map((action, index) => (\n <li className=\"kz-next-action\" data-urgency={action.urgency} key={action.id}>\n <span className=\"kz-next-action-rank\" aria-hidden=\"true\">{index + 1}.</span>\n <div className=\"kz-next-action-content\">\n <div className=\"kz-next-action-copy\">\n <div className=\"kz-next-action-heading\">\n <h3>{action.title}</h3>\n <span className=\"kz-next-action-urgency\" data-urgency={action.urgency}>{action.urgencyLabel}</span>\n </div>\n <p>{action.why}</p>\n </div>\n <div className=\"kz-next-action-control\">{action.action}</div>\n </div>\n </li>\n ))}\n </ol>\n </section>\n );\n}\n"],"mappings":";AA+BM,cAIM,YAJN;AAHC,SAAS,YAAY,EAAE,OAAO,SAAS,WAAW,GAAG,MAAM,GAAqB;AACrF,SACE,qBAAC,aAAQ,WAAW,CAAC,mBAAmB,SAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,GAAI,GAAG,OAChF;AAAA,wBAAC,QAAG,WAAU,yBAAyB,iBAAM;AAAA,IAC7C,oBAAC,QAAG,WAAU,wBACX,kBAAQ,IAAI,CAAC,QAAQ,UACpB,qBAAC,QAAG,WAAU,kBAAiB,gBAAc,OAAO,SAClD;AAAA,2BAAC,UAAK,WAAU,uBAAsB,eAAY,QAAQ;AAAA,gBAAQ;AAAA,QAAE;AAAA,SAAC;AAAA,MACrE,qBAAC,SAAI,WAAU,0BACb;AAAA,6BAAC,SAAI,WAAU,uBACb;AAAA,+BAAC,SAAI,WAAU,0BACb;AAAA,gCAAC,QAAI,iBAAO,OAAM;AAAA,YAClB,oBAAC,UAAK,WAAU,0BAAyB,gBAAc,OAAO,SAAU,iBAAO,cAAa;AAAA,aAC9F;AAAA,UACA,oBAAC,OAAG,iBAAO,KAAI;AAAA,WACjB;AAAA,QACA,oBAAC,SAAI,WAAU,0BAA0B,iBAAO,QAAO;AAAA,SACzD;AAAA,SAXgE,OAAO,EAYzE,CACD,GACH;AAAA,KACF;AAEJ;","names":[]}
package/dist/page-hero.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-YKEIOYJ5.js";
4
4
  import {
5
5
  BlockLink
6
- } from "./chunk-2AGSO3WH.js";
6
+ } from "./chunk-FVGFPB73.js";
7
7
  import {
8
8
  ArrowGlyph
9
9
  } from "./chunk-XCBVFMNV.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  BlockLink
3
- } from "./chunk-2AGSO3WH.js";
3
+ } from "./chunk-FVGFPB73.js";
4
4
  import {
5
5
  cn
6
6
  } from "./chunk-W675RFOA.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  BlockLink
3
- } from "./chunk-2AGSO3WH.js";
3
+ } from "./chunk-FVGFPB73.js";
4
4
  import {
5
5
  CheckGlyph
6
6
  } from "./chunk-XCBVFMNV.js";
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import {
3
3
  BlockLink
4
- } from "./chunk-C7LTF5CC.js";
4
+ } from "./chunk-ENOS6ETI.js";
5
5
 
6
6
  // src/slot-picker/slot-picker.tsx
7
7
  import * as React from "react";
@@ -0,0 +1,63 @@
1
+ import * as React from 'react';
2
+ import { CSSProperties } from 'react';
3
+
4
+ type StyledTextProps<T extends React.ElementType = "p"> = {
5
+ /** The element drawn: `h1`…`h6`, `p`, `span`, `div`. */
6
+ as?: T;
7
+ /** The stored text style — validated here: anything invalid draws the default. */
8
+ value?: unknown;
9
+ /** Merged after the style's own class. */
10
+ className?: string;
11
+ children?: React.ReactNode;
12
+ } & Omit<React.ComponentPropsWithoutRef<T>, "as" | "value" | "className" | "style" | "children">;
13
+ /** A text role drawn from a `TextStyle`: its font, size, weight, spacing, alignment, case and tone, all from CSS variables. */
14
+ declare function StyledText<T extends React.ElementType = "p">({ as, value, className, children, ...rest }: StyledTextProps<T>): React.JSX.Element;
15
+
16
+ /** The curated families of `@krizaka/fonts`, by role: `display` (the headlines), `text` (reading), `mono` (the typewriter), `cahier` (the pen). */
17
+ declare const fontIds: readonly ["display", "text", "mono", "cahier"];
18
+ type FontId = (typeof fontIds)[number];
19
+ /** The steps of the type scale, smallest to largest. Never pixels. */
20
+ declare const sizeSteps: readonly ["xs", "sm", "md", "lg", "xl", "2xl", "3xl", "4xl"];
21
+ type SizeStep = (typeof sizeSteps)[number];
22
+ declare const textWeights: readonly [400, 500, 600, 700];
23
+ type TextWeight = (typeof textWeights)[number];
24
+ declare const leadings: readonly ["tight", "normal", "relaxed"];
25
+ type Leading = (typeof leadings)[number];
26
+ declare const trackings: readonly ["tight", "normal", "wide"];
27
+ type Tracking = (typeof trackings)[number];
28
+ declare const aligns: readonly ["start", "center", "end"];
29
+ type Align = (typeof aligns)[number];
30
+ declare const cases: readonly ["none", "upper", "small-caps"];
31
+ type Case = (typeof cases)[number];
32
+ /** The text roles of the tokens, and the brand hue. */
33
+ declare const toneIds: readonly ["primary", "secondary", "muted", "accent"];
34
+ type ToneId = (typeof toneIds)[number];
35
+ /** A text style: what a shop owner chooses, stored as it is. */
36
+ type TextStyle = {
37
+ font: FontId;
38
+ size: SizeStep;
39
+ weight: TextWeight;
40
+ leading: Leading;
41
+ tracking: Tracking;
42
+ align: Align;
43
+ case: Case;
44
+ italic: boolean;
45
+ tone: ToneId;
46
+ };
47
+ /** What `parseTextStyle` answers for anything that is not a text style. */
48
+ declare const defaultTextStyle: Readonly<TextStyle>;
49
+ /** The weights each face is cut in: a picker offers these, never a synthesised bold. */
50
+ declare const fontWeights: Readonly<Record<FontId, readonly TextWeight[]>>;
51
+ /**
52
+ * Validates what was stored (or sent): the text style, or `defaultTextStyle` for anything invalid — a free font name, a
53
+ * hex colour, a pixel size, CSS text, a value that is not an object. Keys a text style does not have are dropped; keys it
54
+ * lacks (a value stored before one was added) take their default. Usable on a server.
55
+ */
56
+ declare function parseTextStyle(input: unknown): TextStyle;
57
+ /** The `className` and `style` that draw a text style. Built from fixed tables of CSS variables only. */
58
+ declare function textStyleProps(style: unknown): {
59
+ className: string;
60
+ style: CSSProperties;
61
+ };
62
+
63
+ export { type Align, type Case, type FontId, type Leading, type SizeStep, StyledText, type StyledTextProps, type TextStyle, type TextWeight, type ToneId, type Tracking, aligns, cases, defaultTextStyle, fontIds, fontWeights, leadings, parseTextStyle, sizeSteps, textStyleProps, textWeights, toneIds, trackings };
@@ -0,0 +1,109 @@
1
+ // src/cx.ts
2
+ function cx(...parts) {
3
+ return parts.filter(Boolean).join(" ");
4
+ }
5
+
6
+ // src/text-style/text-style.ts
7
+ import * as z from "zod/mini";
8
+ var fontIds = ["display", "text", "mono", "cahier"];
9
+ var sizeSteps = ["xs", "sm", "md", "lg", "xl", "2xl", "3xl", "4xl"];
10
+ var textWeights = [400, 500, 600, 700];
11
+ var leadings = ["tight", "normal", "relaxed"];
12
+ var trackings = ["tight", "normal", "wide"];
13
+ var aligns = ["start", "center", "end"];
14
+ var cases = ["none", "upper", "small-caps"];
15
+ var toneIds = ["primary", "secondary", "muted", "accent"];
16
+ var defaultTextStyle = Object.freeze({
17
+ font: "text",
18
+ size: "md",
19
+ weight: 400,
20
+ leading: "normal",
21
+ tracking: "normal",
22
+ align: "start",
23
+ case: "none",
24
+ italic: false,
25
+ tone: "primary"
26
+ });
27
+ var fontWeights = {
28
+ display: [400],
29
+ text: [400, 600, 700],
30
+ mono: [400, 700],
31
+ cahier: [500]
32
+ };
33
+ var schema = z.object({
34
+ font: z.enum(fontIds),
35
+ size: z.enum(sizeSteps),
36
+ weight: z.union([z.literal(400), z.literal(500), z.literal(600), z.literal(700)]),
37
+ leading: z.enum(leadings),
38
+ tracking: z.enum(trackings),
39
+ align: z.enum(aligns),
40
+ case: z.enum(cases),
41
+ italic: z.boolean(),
42
+ tone: z.enum(toneIds)
43
+ });
44
+ function parseTextStyle(input) {
45
+ if (typeof input !== "object" || input === null || Array.isArray(input)) return { ...defaultTextStyle };
46
+ const own = Object.fromEntries(Object.entries(input).filter(([, v]) => v !== void 0));
47
+ const parsed = schema.safeParse({ ...defaultTextStyle, ...own });
48
+ return parsed.success ? { ...parsed.data } : { ...defaultTextStyle };
49
+ }
50
+ var rem = { xs: "0.75rem", sm: "0.875rem", md: "1rem", lg: "1.125rem", xl: "1.25rem", "2xl": "1.5rem", "3xl": "1.875rem", "4xl": "2.25rem" };
51
+ var lineHeight = { tight: "1.15", normal: "1.4", relaxed: "1.7" };
52
+ var letterSpacing = { tight: "-0.02em", normal: "0em", wide: "0.08em" };
53
+ var textAlign = { start: "start", center: "center", end: "end" };
54
+ var tone = {
55
+ primary: "var(--kz-text-primary)",
56
+ secondary: "var(--kz-text-secondary)",
57
+ muted: "var(--kz-text-muted)",
58
+ accent: "var(--kz-accent-text)"
59
+ };
60
+ var fontVariable = {
61
+ display: "var(--kz-font-display)",
62
+ text: "var(--kz-font-text)",
63
+ mono: "var(--kz-font-mono)",
64
+ cahier: "var(--kz-font-cahier)"
65
+ };
66
+ var sizeVariable = (step) => `var(--kz-text-style-${step}, ${rem[step]})`;
67
+ var toneVariable = (id) => tone[id];
68
+ function textStyleProps(style) {
69
+ const v = parseTextStyle(style);
70
+ return {
71
+ className: "kz-text-style",
72
+ style: {
73
+ fontFamily: fontVariable[v.font],
74
+ fontSize: sizeVariable(v.size),
75
+ fontWeight: v.weight,
76
+ lineHeight: `var(--kz-text-style-leading-${v.leading}, ${lineHeight[v.leading]})`,
77
+ letterSpacing: `var(--kz-text-style-tracking-${v.tracking}, ${letterSpacing[v.tracking]})`,
78
+ textAlign: textAlign[v.align],
79
+ textTransform: v.case === "upper" ? "uppercase" : "none",
80
+ fontVariantCaps: v.case === "small-caps" ? "small-caps" : "normal",
81
+ fontStyle: v.italic ? "italic" : "normal",
82
+ color: toneVariable(v.tone)
83
+ }
84
+ };
85
+ }
86
+
87
+ // src/text-style/styled-text.tsx
88
+ import { jsx } from "react/jsx-runtime";
89
+ function StyledText({ as, value, className, children, ...rest }) {
90
+ const Tag = as ?? "p";
91
+ const props = textStyleProps(value);
92
+ return /* @__PURE__ */ jsx(Tag, { ...rest, className: cx(props.className, className), style: props.style, children });
93
+ }
94
+ export {
95
+ StyledText,
96
+ aligns,
97
+ cases,
98
+ defaultTextStyle,
99
+ fontIds,
100
+ fontWeights,
101
+ leadings,
102
+ parseTextStyle,
103
+ sizeSteps,
104
+ textStyleProps,
105
+ textWeights,
106
+ toneIds,
107
+ trackings
108
+ };
109
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/cx.ts","../../src/text-style/text-style.ts","../../src/text-style/styled-text.tsx"],"sourcesContent":["/** Joins class names, skipping the falsy ones. */\nexport function cx(...parts: Array<string | false | null | undefined>): string {\n return parts.filter(Boolean).join(\" \");\n}\n","// Server-safe (no hook, no React): the value of a text style, its validation and its CSS. A shop owner's choice is stored\n// by the app, so the value is plain data and its CSS is built from fixed tables of CSS variables — what comes from a\n// database never reaches a style sheet as text. The fonts are the curated roles of @krizaka/fonts (the product theme\n// says which family a role is), the sizes are the type scale's steps, the tones are the text roles and the brand hue.\nimport type { CSSProperties } from \"react\";\nimport * as z from \"zod/mini\";\n\n/** The curated families of `@krizaka/fonts`, by role: `display` (the headlines), `text` (reading), `mono` (the typewriter), `cahier` (the pen). */\nexport const fontIds = [\"display\", \"text\", \"mono\", \"cahier\"] as const;\nexport type FontId = (typeof fontIds)[number];\n\n/** The steps of the type scale, smallest to largest. Never pixels. */\nexport const sizeSteps = [\"xs\", \"sm\", \"md\", \"lg\", \"xl\", \"2xl\", \"3xl\", \"4xl\"] as const;\nexport type SizeStep = (typeof sizeSteps)[number];\n\nexport const textWeights = [400, 500, 600, 700] as const;\nexport type TextWeight = (typeof textWeights)[number];\n\nexport const leadings = [\"tight\", \"normal\", \"relaxed\"] as const;\nexport type Leading = (typeof leadings)[number];\n\nexport const trackings = [\"tight\", \"normal\", \"wide\"] as const;\nexport type Tracking = (typeof trackings)[number];\n\nexport const aligns = [\"start\", \"center\", \"end\"] as const;\nexport type Align = (typeof aligns)[number];\n\nexport const cases = [\"none\", \"upper\", \"small-caps\"] as const;\nexport type Case = (typeof cases)[number];\n\n/** The text roles of the tokens, and the brand hue. */\nexport const toneIds = [\"primary\", \"secondary\", \"muted\", \"accent\"] as const;\nexport type ToneId = (typeof toneIds)[number];\n\n/** A text style: what a shop owner chooses, stored as it is. */\nexport type TextStyle = {\n font: FontId;\n size: SizeStep;\n weight: TextWeight;\n leading: Leading;\n tracking: Tracking;\n align: Align;\n case: Case;\n italic: boolean;\n tone: ToneId;\n};\n\n/** What `parseTextStyle` answers for anything that is not a text style. */\nexport const defaultTextStyle: Readonly<TextStyle> = Object.freeze({\n font: \"text\",\n size: \"md\",\n weight: 400,\n leading: \"normal\",\n tracking: \"normal\",\n align: \"start\",\n case: \"none\",\n italic: false,\n tone: \"primary\",\n});\n\n/** The weights each face is cut in: a picker offers these, never a synthesised bold. */\nexport const fontWeights: Readonly<Record<FontId, readonly TextWeight[]>> = {\n display: [400],\n text: [400, 600, 700],\n mono: [400, 700],\n cahier: [500],\n};\n\nconst schema = z.object({\n font: z.enum(fontIds),\n size: z.enum(sizeSteps),\n weight: z.union([z.literal(400), z.literal(500), z.literal(600), z.literal(700)]),\n leading: z.enum(leadings),\n tracking: z.enum(trackings),\n align: z.enum(aligns),\n case: z.enum(cases),\n italic: z.boolean(),\n tone: z.enum(toneIds),\n});\n\n/**\n * Validates what was stored (or sent): the text style, or `defaultTextStyle` for anything invalid — a free font name, a\n * hex colour, a pixel size, CSS text, a value that is not an object. Keys a text style does not have are dropped; keys it\n * lacks (a value stored before one was added) take their default. Usable on a server.\n */\nexport function parseTextStyle(input: unknown): TextStyle {\n if (typeof input !== \"object\" || input === null || Array.isArray(input)) return { ...defaultTextStyle };\n const own = Object.fromEntries(Object.entries(input).filter(([, v]) => v !== undefined));\n const parsed = schema.safeParse({ ...defaultTextStyle, ...own });\n return parsed.success ? { ...parsed.data } : { ...defaultTextStyle };\n}\n\nconst rem: Record<SizeStep, string> = { xs: \"0.75rem\", sm: \"0.875rem\", md: \"1rem\", lg: \"1.125rem\", xl: \"1.25rem\", \"2xl\": \"1.5rem\", \"3xl\": \"1.875rem\", \"4xl\": \"2.25rem\" };\nconst lineHeight: Record<Leading, string> = { tight: \"1.15\", normal: \"1.4\", relaxed: \"1.7\" };\nconst letterSpacing: Record<Tracking, string> = { tight: \"-0.02em\", normal: \"0em\", wide: \"0.08em\" };\nconst textAlign: Record<Align, CSSProperties[\"textAlign\"]> = { start: \"start\", center: \"center\", end: \"end\" };\nconst tone: Record<ToneId, string> = {\n primary: \"var(--kz-text-primary)\",\n secondary: \"var(--kz-text-secondary)\",\n muted: \"var(--kz-text-muted)\",\n accent: \"var(--kz-accent-text)\",\n};\n\n/** The CSS variable of a text role: the product theme's face for `display`, the preset's for the others. */\nexport const fontVariable: Readonly<Record<FontId, string>> = {\n display: \"var(--kz-font-display)\",\n text: \"var(--kz-font-text)\",\n mono: \"var(--kz-font-mono)\",\n cahier: \"var(--kz-font-cahier)\",\n};\n\n/** A step's size as a CSS value: `var(--kz-text-style-<step>, <rem>)` — a product may re-cut the scale with the variable. */\nexport const sizeVariable = (step: SizeStep): string => `var(--kz-text-style-${step}, ${rem[step]})`;\n\n/** A tone's colour as a CSS value: a role of the tokens. */\nexport const toneVariable = (id: ToneId): string => tone[id];\n\n/** The `className` and `style` that draw a text style. Built from fixed tables of CSS variables only. */\nexport function textStyleProps(style: unknown): { className: string; style: CSSProperties } {\n const v = parseTextStyle(style);\n return {\n className: \"kz-text-style\",\n style: {\n fontFamily: fontVariable[v.font],\n fontSize: sizeVariable(v.size),\n fontWeight: v.weight,\n lineHeight: `var(--kz-text-style-leading-${v.leading}, ${lineHeight[v.leading]})`,\n letterSpacing: `var(--kz-text-style-tracking-${v.tracking}, ${letterSpacing[v.tracking]})`,\n textAlign: textAlign[v.align],\n textTransform: v.case === \"upper\" ? \"uppercase\" : \"none\",\n fontVariantCaps: v.case === \"small-caps\" ? \"small-caps\" : \"normal\",\n fontStyle: v.italic ? \"italic\" : \"normal\",\n color: toneVariable(v.tone),\n },\n };\n}\n\n/** A colour read from the page, as `[r, g, b]` in 0–255, or `null` when it cannot be read (the check then says nothing). */\nexport function parseColor(text: string): [number, number, number] | null {\n const value = text.trim().toLowerCase();\n const hex = /^#([0-9a-f]{3,8})$/.exec(value)?.[1];\n if (hex && [3, 4, 6, 8].includes(hex.length)) {\n const full = hex.length <= 4 ? [...hex].map((c) => c + c).join(\"\") : hex;\n return [0, 2, 4].map((i) => Number.parseInt(full.slice(i, i + 2), 16)) as [number, number, number];\n }\n const rgb = /^rgba?\\(([^)]+)\\)$/.exec(value)?.[1];\n if (rgb) {\n const parts = rgb.split(/[\\s,/]+/).filter(Boolean).slice(0, 3).map((p) => (p.endsWith(\"%\") ? (Number.parseFloat(p) / 100) * 255 : Number.parseFloat(p)));\n return parts.length === 3 && parts.every(Number.isFinite) ? (parts as [number, number, number]) : null;\n }\n const srgb = /^color\\(srgb\\s+([^)]+)\\)$/.exec(value)?.[1];\n if (srgb) {\n const parts = srgb.split(/[\\s/]+/).filter(Boolean).slice(0, 3).map((p) => Number.parseFloat(p) * 255);\n return parts.length === 3 && parts.every(Number.isFinite) ? (parts as [number, number, number]) : null;\n }\n return null;\n}\n\nconst channel = (c: number) => {\n const s = Math.min(Math.max(c, 0), 255) / 255;\n return s <= 0.03928 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;\n};\nconst luminance = ([r, g, b]: [number, number, number]) => 0.2126 * channel(r) + 0.7152 * channel(g) + 0.0722 * channel(b);\n\n/** The WCAG contrast ratio of two colours (1 – 21). */\nexport function contrastRatio(foreground: [number, number, number], background: [number, number, number]): number {\n const a = luminance(foreground);\n const b = luminance(background);\n return (Math.max(a, b) + 0.05) / (Math.min(a, b) + 0.05);\n}\n\n/** The ratio WCAG asks of large text. */\nexport const largeTextRatio = 3;\n/** The ratio WCAG asks of body text. */\nexport const bodyTextRatio = 4.5;\n\n/** Large text for WCAG: 24 px and over, or bold from 18.66 px (`xl`, 20 px, in bold). */\nexport function isLargeText(style: Pick<TextStyle, \"size\" | \"weight\">): boolean {\n const at = sizeSteps.indexOf(style.size);\n return at >= sizeSteps.indexOf(\"2xl\") || (at >= sizeSteps.indexOf(\"xl\") && style.weight >= 700);\n}\n\n/** The ratio a style's text must reach on its surface. */\nexport const requiredContrast = (style: Pick<TextStyle, \"size\" | \"weight\">): number => (isLargeText(style) ? largeTextRatio : bodyTextRatio);\n\n/** True when body text in this step is under the minimum: a warning to show, never a size to force. */\nexport const isUnderMinimumSize = (size: SizeStep, minimum: SizeStep): boolean => sizeSteps.indexOf(size) < sizeSteps.indexOf(minimum);\n","// Server-safe: a text drawn with a stored text style. Whatever is passed as `value` goes through `parseTextStyle`.\nimport * as React from \"react\";\n\nimport { cx } from \"../cx\";\nimport { textStyleProps } from \"./text-style\";\n\nexport type StyledTextProps<T extends React.ElementType = \"p\"> = {\n /** The element drawn: `h1`…`h6`, `p`, `span`, `div`. */\n as?: T;\n /** The stored text style — validated here: anything invalid draws the default. */\n value?: unknown;\n /** Merged after the style's own class. */\n className?: string;\n children?: React.ReactNode;\n} & Omit<React.ComponentPropsWithoutRef<T>, \"as\" | \"value\" | \"className\" | \"style\" | \"children\">;\n\n/** A text role drawn from a `TextStyle`: its font, size, weight, spacing, alignment, case and tone, all from CSS variables. */\nexport function StyledText<T extends React.ElementType = \"p\">({ as, value, className, children, ...rest }: StyledTextProps<T>) {\n const Tag: React.ElementType = as ?? \"p\";\n const props = textStyleProps(value);\n return (\n <Tag {...rest} className={cx(props.className, className)} style={props.style}>\n {children}\n </Tag>\n );\n}\n"],"mappings":"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","names":[]}