@raydenui/ui 0.9.7 → 0.10.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 (308) hide show
  1. package/dist/Table-DCNSxua1.d.cts +31 -0
  2. package/dist/Table-DCNSxua1.d.ts +31 -0
  3. package/dist/blocks/ApplicationShellBlock.cjs +157 -0
  4. package/dist/blocks/ApplicationShellBlock.js +154 -0
  5. package/dist/blocks/CheckoutReviewBlock.cjs +16 -0
  6. package/dist/blocks/CheckoutReviewBlock.js +13 -0
  7. package/dist/blocks/CommandPaletteBlock.cjs +334 -0
  8. package/dist/blocks/CommandPaletteBlock.js +331 -0
  9. package/dist/blocks/CreateAccountBlock.cjs +147 -0
  10. package/dist/blocks/CreateAccountBlock.js +143 -0
  11. package/dist/blocks/EmptyStateBlock.cjs +19 -0
  12. package/dist/blocks/EmptyStateBlock.js +16 -0
  13. package/dist/blocks/FeatureOverviewBlock.cjs +54 -0
  14. package/dist/blocks/FeatureOverviewBlock.js +51 -0
  15. package/dist/blocks/HeaderBlock.cjs +230 -0
  16. package/dist/blocks/HeaderBlock.js +227 -0
  17. package/dist/blocks/KpiOverviewBlock.cjs +63 -0
  18. package/dist/blocks/KpiOverviewBlock.js +60 -0
  19. package/dist/blocks/LoginBlock.cjs +66 -0
  20. package/dist/blocks/LoginBlock.js +63 -0
  21. package/dist/blocks/NotificationsBlock.cjs +17 -0
  22. package/dist/blocks/NotificationsBlock.js +14 -0
  23. package/dist/blocks/PageHeaderBlock.cjs +121 -0
  24. package/dist/blocks/PageHeaderBlock.js +118 -0
  25. package/dist/blocks/PricingPlansBlock.cjs +146 -0
  26. package/dist/blocks/PricingPlansBlock.js +143 -0
  27. package/dist/blocks/ProductCollectionBlock.cjs +37 -0
  28. package/dist/blocks/ProductCollectionBlock.js +34 -0
  29. package/dist/blocks/ProductDetailBlock.cjs +30 -0
  30. package/dist/blocks/ProductDetailBlock.js +27 -0
  31. package/dist/blocks/ProductHeroBlock.cjs +83 -0
  32. package/dist/blocks/ProductHeroBlock.js +80 -0
  33. package/dist/blocks/ProfileSettingsBlock.cjs +130 -0
  34. package/dist/blocks/ProfileSettingsBlock.js +126 -0
  35. package/dist/blocks/QuickSendBlock.cjs +11 -0
  36. package/dist/blocks/QuickSendBlock.js +8 -0
  37. package/dist/blocks/RecentTransactionsBlock.cjs +13 -0
  38. package/dist/blocks/RecentTransactionsBlock.js +10 -0
  39. package/dist/blocks/SearchableTableBlock.cjs +91 -0
  40. package/dist/blocks/SearchableTableBlock.js +88 -0
  41. package/dist/blocks/ShoppingCartBlock.cjs +16 -0
  42. package/dist/blocks/ShoppingCartBlock.js +13 -0
  43. package/dist/blocks/SiteFooterBlock.cjs +69 -0
  44. package/dist/blocks/SiteFooterBlock.js +66 -0
  45. package/dist/blocks/TableBlock.cjs +20 -0
  46. package/dist/blocks/TableBlock.js +17 -0
  47. package/dist/blocks/TaskListBlock.cjs +198 -0
  48. package/dist/blocks/TaskListBlock.js +195 -0
  49. package/dist/blocks/WorkspaceSwitcherBlock.cjs +291 -0
  50. package/dist/blocks/WorkspaceSwitcherBlock.js +288 -0
  51. package/dist/blocks/commerce.cjs +44 -0
  52. package/dist/blocks/commerce.js +35 -0
  53. package/dist/blocks/index.cjs +53 -0
  54. package/dist/blocks/index.js +24 -0
  55. package/dist/blocks.d.cts +1820 -0
  56. package/dist/blocks.d.ts +1820 -0
  57. package/dist/chart.cjs +13 -0
  58. package/dist/chart.d.cts +59 -0
  59. package/dist/chart.d.ts +59 -0
  60. package/dist/chart.js +5 -0
  61. package/dist/components/Accordion/Accordion.cjs +94 -0
  62. package/dist/components/Accordion/Accordion.js +91 -0
  63. package/dist/components/Accordion/index.cjs +8 -0
  64. package/dist/components/Accordion/index.js +1 -0
  65. package/dist/components/ActivityFeed/ActivityContent.cjs +40 -0
  66. package/dist/components/ActivityFeed/ActivityContent.js +37 -0
  67. package/dist/components/ActivityFeed/ActivityItem.cjs +19 -0
  68. package/dist/components/ActivityFeed/ActivityItem.js +16 -0
  69. package/dist/components/ActivityFeed/index.cjs +7 -0
  70. package/dist/components/ActivityFeed/index.js +2 -0
  71. package/dist/components/Alert/Alert.cjs +42 -0
  72. package/dist/components/Alert/Alert.js +39 -0
  73. package/dist/components/Alert/index.cjs +5 -0
  74. package/dist/components/Alert/index.js +1 -0
  75. package/dist/components/Avatar/Avatar.cjs +101 -0
  76. package/dist/components/Avatar/Avatar.js +98 -0
  77. package/dist/components/Avatar/index.cjs +6 -0
  78. package/dist/components/Avatar/index.js +1 -0
  79. package/dist/components/Badge/Badge.cjs +53 -0
  80. package/dist/components/Badge/Badge.js +50 -0
  81. package/dist/components/Badge/index.cjs +5 -0
  82. package/dist/components/Badge/index.js +1 -0
  83. package/dist/components/Banner/Banner.cjs +83 -0
  84. package/dist/components/Banner/Banner.js +80 -0
  85. package/dist/components/Banner/index.cjs +5 -0
  86. package/dist/components/Banner/index.js +1 -0
  87. package/dist/components/Breadcrumb/Breadcrumb.cjs +31 -0
  88. package/dist/components/Breadcrumb/Breadcrumb.js +28 -0
  89. package/dist/components/Breadcrumb/index.cjs +5 -0
  90. package/dist/components/Breadcrumb/index.js +1 -0
  91. package/dist/components/Button/Button.cjs +88 -0
  92. package/dist/components/Button/Button.js +85 -0
  93. package/dist/components/Button/index.cjs +5 -0
  94. package/dist/components/Button/index.js +1 -0
  95. package/dist/components/ButtonGroup/ButtonGroup.cjs +8 -0
  96. package/dist/components/ButtonGroup/ButtonGroup.js +5 -0
  97. package/dist/components/ButtonGroup/ButtonGroupItem.cjs +15 -0
  98. package/dist/components/ButtonGroup/ButtonGroupItem.js +12 -0
  99. package/dist/components/ButtonGroup/index.cjs +7 -0
  100. package/dist/components/ButtonGroup/index.js +2 -0
  101. package/dist/components/Card/Card.cjs +94 -0
  102. package/dist/components/Card/Card.js +91 -0
  103. package/dist/components/Card/index.cjs +9 -0
  104. package/dist/components/Card/index.js +1 -0
  105. package/dist/components/Chart/Chart.cjs +160 -0
  106. package/dist/components/Chart/Chart.js +153 -0
  107. package/dist/components/Chart/index.cjs +10 -0
  108. package/dist/components/Chart/index.js +2 -0
  109. package/dist/components/Chart/theme.cjs +67 -0
  110. package/dist/components/Chart/theme.js +62 -0
  111. package/dist/components/Chip/Chip.cjs +18 -0
  112. package/dist/components/Chip/Chip.js +15 -0
  113. package/dist/components/Chip/index.cjs +5 -0
  114. package/dist/components/Chip/index.js +1 -0
  115. package/dist/components/Counter/Counter.cjs +74 -0
  116. package/dist/components/Counter/Counter.js +71 -0
  117. package/dist/components/Counter/index.cjs +6 -0
  118. package/dist/components/Counter/index.js +1 -0
  119. package/dist/components/DatePicker/DatePicker.cjs +343 -0
  120. package/dist/components/DatePicker/DatePicker.js +340 -0
  121. package/dist/components/DatePicker/index.cjs +5 -0
  122. package/dist/components/DatePicker/index.js +1 -0
  123. package/dist/components/Divider/Divider.cjs +31 -0
  124. package/dist/components/Divider/Divider.js +28 -0
  125. package/dist/components/Divider/index.cjs +5 -0
  126. package/dist/components/Divider/index.js +1 -0
  127. package/dist/components/DropdownMenu/DropdownMenu.cjs +108 -0
  128. package/dist/components/DropdownMenu/DropdownMenu.js +105 -0
  129. package/dist/components/DropdownMenu/index.cjs +11 -0
  130. package/dist/components/DropdownMenu/index.js +1 -0
  131. package/dist/components/EmptyStateIllustration/EmptyStateIllustration.cjs +42 -0
  132. package/dist/components/EmptyStateIllustration/EmptyStateIllustration.js +39 -0
  133. package/dist/components/EmptyStateIllustration/illustrations.cjs +3322 -0
  134. package/dist/components/EmptyStateIllustration/illustrations.js +3319 -0
  135. package/dist/components/EmptyStateIllustration/index.cjs +7 -0
  136. package/dist/components/EmptyStateIllustration/index.js +2 -0
  137. package/dist/components/FileUpload/FileUpload.cjs +116 -0
  138. package/dist/components/FileUpload/FileUpload.js +113 -0
  139. package/dist/components/FileUpload/FileUploadDropZone.cjs +58 -0
  140. package/dist/components/FileUpload/FileUploadDropZone.js +55 -0
  141. package/dist/components/FileUpload/FileUploadIcons.cjs +55 -0
  142. package/dist/components/FileUpload/FileUploadIcons.js +50 -0
  143. package/dist/components/FileUpload/FileUploadItem.cjs +34 -0
  144. package/dist/components/FileUpload/FileUploadItem.js +31 -0
  145. package/dist/components/FileUpload/index.cjs +9 -0
  146. package/dist/components/FileUpload/index.js +3 -0
  147. package/dist/components/FormControl/Checkbox.cjs +27 -0
  148. package/dist/components/FormControl/Checkbox.js +24 -0
  149. package/dist/components/FormControl/Radio.cjs +15 -0
  150. package/dist/components/FormControl/Radio.js +12 -0
  151. package/dist/components/FormControl/Toggle.cjs +15 -0
  152. package/dist/components/FormControl/Toggle.js +12 -0
  153. package/dist/components/FormControl/index.cjs +9 -0
  154. package/dist/components/FormControl/index.js +3 -0
  155. package/dist/components/Icon/Icon.cjs +70 -0
  156. package/dist/components/Icon/Icon.js +34 -0
  157. package/dist/components/Icon/catalog.cjs +1672 -0
  158. package/dist/components/Icon/catalog.js +1669 -0
  159. package/dist/components/Icon/icons.cjs +5621 -0
  160. package/dist/components/Icon/icons.js +5610 -0
  161. package/dist/components/Icon/index.cjs +8 -0
  162. package/dist/components/Icon/index.js +2 -0
  163. package/dist/components/Input/Input.cjs +64 -0
  164. package/dist/components/Input/Input.js +61 -0
  165. package/dist/components/Input/index.cjs +5 -0
  166. package/dist/components/Input/index.js +1 -0
  167. package/dist/components/MetricsCard/MetricsCard.cjs +107 -0
  168. package/dist/components/MetricsCard/MetricsCard.js +104 -0
  169. package/dist/components/MetricsCard/index.cjs +5 -0
  170. package/dist/components/MetricsCard/index.js +1 -0
  171. package/dist/components/Modal/Modal.cjs +126 -0
  172. package/dist/components/Modal/Modal.js +123 -0
  173. package/dist/components/Modal/index.cjs +5 -0
  174. package/dist/components/Modal/index.js +1 -0
  175. package/dist/components/Pagination/Pagination.cjs +42 -0
  176. package/dist/components/Pagination/Pagination.js +39 -0
  177. package/dist/components/Pagination/index.cjs +5 -0
  178. package/dist/components/Pagination/index.js +1 -0
  179. package/dist/components/ProgressBar/ProgressBar.cjs +34 -0
  180. package/dist/components/ProgressBar/ProgressBar.js +31 -0
  181. package/dist/components/ProgressBar/index.cjs +5 -0
  182. package/dist/components/ProgressBar/index.js +1 -0
  183. package/dist/components/ProgressCircle/ProgressCircle.cjs +34 -0
  184. package/dist/components/ProgressCircle/ProgressCircle.js +31 -0
  185. package/dist/components/ProgressCircle/index.cjs +5 -0
  186. package/dist/components/ProgressCircle/index.js +1 -0
  187. package/dist/components/Select/Select.cjs +84 -0
  188. package/dist/components/Select/Select.js +80 -0
  189. package/dist/components/Select/SelectOption.cjs +26 -0
  190. package/dist/components/Select/SelectOption.js +23 -0
  191. package/dist/components/Select/index.cjs +7 -0
  192. package/dist/components/Select/index.js +2 -0
  193. package/dist/components/SidebarMenu/SidebarMenu.cjs +85 -0
  194. package/dist/components/SidebarMenu/SidebarMenu.js +81 -0
  195. package/dist/components/SidebarMenu/SidebarMenuItem.cjs +126 -0
  196. package/dist/components/SidebarMenu/SidebarMenuItem.js +122 -0
  197. package/dist/components/SidebarMenu/SidebarMenuSection.cjs +14 -0
  198. package/dist/components/SidebarMenu/SidebarMenuSection.js +11 -0
  199. package/dist/components/SidebarMenu/index.cjs +12 -0
  200. package/dist/components/SidebarMenu/index.js +4 -0
  201. package/dist/components/Slider/Slider.cjs +161 -0
  202. package/dist/components/Slider/Slider.js +158 -0
  203. package/dist/components/Slider/index.cjs +6 -0
  204. package/dist/components/Slider/index.js +1 -0
  205. package/dist/components/Spinner/Spinner.cjs +93 -0
  206. package/dist/components/Spinner/Spinner.js +90 -0
  207. package/dist/components/Spinner/index.cjs +5 -0
  208. package/dist/components/Spinner/index.js +1 -0
  209. package/dist/components/Stepper/Stepper.cjs +82 -0
  210. package/dist/components/Stepper/Stepper.js +79 -0
  211. package/dist/components/Stepper/index.cjs +7 -0
  212. package/dist/components/Stepper/index.js +1 -0
  213. package/dist/components/Table/Table.cjs +25 -0
  214. package/dist/components/Table/Table.js +22 -0
  215. package/dist/components/Table/index.cjs +10 -0
  216. package/dist/components/Table/index.js +1 -0
  217. package/dist/components/Table/useTableSelection.cjs +60 -0
  218. package/dist/components/Table/useTableSelection.js +57 -0
  219. package/dist/components/Tabs/Tab.cjs +70 -0
  220. package/dist/components/Tabs/Tab.js +67 -0
  221. package/dist/components/Tabs/Tabs.cjs +133 -0
  222. package/dist/components/Tabs/Tabs.js +129 -0
  223. package/dist/components/Tabs/index.cjs +7 -0
  224. package/dist/components/Tabs/index.js +2 -0
  225. package/dist/components/Tooltip/Tooltip.cjs +101 -0
  226. package/dist/components/Tooltip/Tooltip.js +98 -0
  227. package/dist/components/Tooltip/index.cjs +5 -0
  228. package/dist/components/Tooltip/index.js +1 -0
  229. package/dist/context/ThemeContext.cjs +97 -0
  230. package/dist/context/ThemeContext.js +92 -0
  231. package/dist/fonts/README.md +13 -0
  232. package/dist/fonts/hanken-grotesk-cyrillic-ext-italic.woff2 +0 -0
  233. package/dist/fonts/hanken-grotesk-cyrillic-ext-normal.woff2 +0 -0
  234. package/dist/fonts/hanken-grotesk-latin-ext-italic.woff2 +0 -0
  235. package/dist/fonts/hanken-grotesk-latin-ext-normal.woff2 +0 -0
  236. package/dist/fonts/hanken-grotesk-latin-italic.woff2 +0 -0
  237. package/dist/fonts/hanken-grotesk-latin-normal.woff2 +0 -0
  238. package/dist/fonts/hanken-grotesk-vietnamese-italic.woff2 +0 -0
  239. package/dist/fonts/hanken-grotesk-vietnamese-normal.woff2 +0 -0
  240. package/dist/fonts/hankengrotesk-LICENSE.txt +94 -0
  241. package/dist/fonts/manrope-LICENSE.txt +93 -0
  242. package/dist/fonts/manrope-cyrillic-ext-normal.woff2 +0 -0
  243. package/dist/fonts/manrope-cyrillic-normal.woff2 +0 -0
  244. package/dist/fonts/manrope-greek-normal.woff2 +0 -0
  245. package/dist/fonts/manrope-latin-ext-normal.woff2 +0 -0
  246. package/dist/fonts/manrope-latin-normal.woff2 +0 -0
  247. package/dist/fonts/manrope-vietnamese-normal.woff2 +0 -0
  248. package/dist/fonts.css +148 -0
  249. package/dist/hooks/form/index.cjs +11 -0
  250. package/dist/hooks/form/index.js +3 -0
  251. package/dist/hooks/form/useRaydenFormControl.cjs +81 -0
  252. package/dist/hooks/form/useRaydenFormControl.js +76 -0
  253. package/dist/hooks/form/useRaydenInput.cjs +38 -0
  254. package/dist/hooks/form/useRaydenInput.js +35 -0
  255. package/dist/hooks/form/useRaydenSelect.cjs +45 -0
  256. package/dist/hooks/form/useRaydenSelect.js +42 -0
  257. package/dist/hooks/index.cjs +17 -0
  258. package/dist/hooks/index.js +1 -0
  259. package/dist/hooks/useBodyScrollLock.cjs +24 -0
  260. package/dist/hooks/useBodyScrollLock.js +21 -0
  261. package/dist/hooks/useCollisionAwareSide.cjs +72 -0
  262. package/dist/hooks/useCollisionAwareSide.js +69 -0
  263. package/dist/hooks/useControllableValue.cjs +16 -0
  264. package/dist/hooks/useControllableValue.js +13 -0
  265. package/dist/hooks/useDismissableLayer.cjs +100 -0
  266. package/dist/hooks/useDismissableLayer.js +97 -0
  267. package/dist/hooks/usePopupList.cjs +101 -0
  268. package/dist/hooks/usePopupList.js +98 -0
  269. package/dist/icons-8yD7I5jO.d.cts +430 -0
  270. package/dist/icons-8yD7I5jO.d.ts +430 -0
  271. package/dist/icons.cjs +2111 -418
  272. package/dist/icons.d.cts +1256 -429
  273. package/dist/icons.d.ts +1256 -429
  274. package/dist/icons.js +2089 -1
  275. package/dist/index.cjs +128 -15046
  276. package/dist/index.d.cts +697 -662
  277. package/dist/index.d.ts +697 -662
  278. package/dist/index.js +43 -14962
  279. package/dist/motion/Collapse.cjs +28 -0
  280. package/dist/motion/Collapse.js +25 -0
  281. package/dist/motion/MotionProvider.cjs +43 -0
  282. package/dist/motion/MotionProvider.js +38 -0
  283. package/dist/motion/Pressable.cjs +46 -0
  284. package/dist/motion/Pressable.js +43 -0
  285. package/dist/motion/Reveal.cjs +19 -0
  286. package/dist/motion/Reveal.js +16 -0
  287. package/dist/motion/SharedLayout.cjs +46 -0
  288. package/dist/motion/SharedLayout.js +43 -0
  289. package/dist/motion/index.cjs +19 -0
  290. package/dist/motion/index.js +6 -0
  291. package/dist/motion/presence.cjs +58 -0
  292. package/dist/motion/presence.js +55 -0
  293. package/dist/motion/presets.cjs +88 -0
  294. package/dist/motion/presets.js +84 -0
  295. package/dist/motion.d.cts +51 -0
  296. package/dist/motion.d.ts +51 -0
  297. package/dist/preset.cjs +356 -195
  298. package/dist/preset.d.cts +111 -32
  299. package/dist/preset.d.ts +111 -32
  300. package/dist/preset.js +353 -187
  301. package/dist/presets-Ba5QndHg.d.cts +144 -0
  302. package/dist/presets-Ba5QndHg.d.ts +144 -0
  303. package/dist/styles.css +1 -1
  304. package/dist/utils/cn.cjs +38 -0
  305. package/dist/utils/cn.js +35 -0
  306. package/dist/utils/resolveIcon.cjs +18 -0
  307. package/dist/utils/resolveIcon.js +15 -0
  308. package/package.json +79 -21
@@ -0,0 +1,143 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useId, useMemo, useRef, useState, } from "react";
3
+ import { cn } from "../utils/cn.js";
4
+ import { Alert } from "../components/Alert/index.js";
5
+ import { Button } from "../components/Button/index.js";
6
+ import { Checkbox } from "../components/FormControl/index.js";
7
+ import { Icon } from "../components/Icon/index.js";
8
+ import { Input } from "../components/Input/index.js";
9
+ import { Spinner } from "../components/Spinner/index.js";
10
+ /* ─── Defaults ───────────────────────────────────────────────────────── */
11
+ export const defaultCreateAccountPasswordRules = [
12
+ { id: "length", label: "At least 8 characters", test: (v) => v.length >= 8 },
13
+ {
14
+ id: "case",
15
+ label: "An upper and a lower case letter",
16
+ test: (v) => /[a-z]/.test(v) && /[A-Z]/.test(v),
17
+ },
18
+ { id: "number", label: "At least one number", test: (v) => /\d/.test(v) },
19
+ ];
20
+ // Deliberately permissive: the authority on a deliverable address is the
21
+ // consuming application, not a regular expression.
22
+ const EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
23
+ const emptyValues = {
24
+ firstName: "",
25
+ lastName: "",
26
+ email: "",
27
+ password: "",
28
+ };
29
+ /* ─── Component ──────────────────────────────────────────────────────── */
30
+ export function CreateAccountBlock({ title = "Create your account", description = "Set up a free workspace. No card required.", headingLevel = "h2", onSubmit, status = "idle", errorMessage, successMessage, fieldErrors, defaultValues, passwordRules = defaultCreateAccountPasswordRules, requireConfirmPassword = true, consentLabel, consentErrorMessage = "Please accept the terms before creating an account.", signIn, submitLabel = "Create account", submittingLabel = "Creating account…", disabled = false, disabledMessage = "Account creation is currently unavailable.", labels, className, }) {
31
+ const uid = useId();
32
+ const titleId = `${uid}-title`;
33
+ const consentId = `${uid}-consent`;
34
+ const consentTextId = `${uid}-consent-text`;
35
+ const consentErrorId = `${uid}-consent-error`;
36
+ const passwordRulesId = `${uid}-password-rules`;
37
+ const passwordErrorId = `${uid}-password-error`;
38
+ const unsavedRegionId = `${uid}-form-status`;
39
+ const [values, setValues] = useState({
40
+ ...emptyValues,
41
+ ...defaultValues,
42
+ });
43
+ const [confirmPassword, setConfirmPassword] = useState("");
44
+ const [consent, setConsent] = useState(false);
45
+ const [touched, setTouched] = useState({});
46
+ const [submitAttempted, setSubmitAttempted] = useState(false);
47
+ const [revealPassword, setRevealPassword] = useState(false);
48
+ const fieldRefs = useRef({});
49
+ const pending = status === "submitting";
50
+ const succeeded = status === "success";
51
+ const loading = status === "loading";
52
+ const locked = disabled || pending || succeeded;
53
+ const ruleResults = useMemo(() => passwordRules.map((rule) => ({ ...rule, met: rule.test(values.password) })), [passwordRules, values.password]);
54
+ const localErrors = useMemo(() => {
55
+ const next = {};
56
+ if (!values.firstName.trim())
57
+ next.firstName = "Enter your first name.";
58
+ if (!values.lastName.trim())
59
+ next.lastName = "Enter your last name.";
60
+ if (!values.email.trim())
61
+ next.email = "Enter your email address.";
62
+ else if (!EMAIL_PATTERN.test(values.email.trim()))
63
+ next.email = "Enter an email address such as name@example.com.";
64
+ if (!values.password)
65
+ next.password = "Choose a password.";
66
+ else if (ruleResults.some((rule) => !rule.met))
67
+ next.password = "Your password does not meet all of the requirements below.";
68
+ if (requireConfirmPassword) {
69
+ if (!confirmPassword)
70
+ next.confirmPassword = "Type your password again.";
71
+ else if (confirmPassword !== values.password)
72
+ next.confirmPassword = "The two passwords do not match.";
73
+ }
74
+ if (!consent)
75
+ next.consent = consentErrorMessage;
76
+ return next;
77
+ }, [values, confirmPassword, consent, consentErrorMessage, requireConfirmPassword, ruleResults]);
78
+ const errorFor = useCallback((field) => {
79
+ const serverError = fieldErrors?.[field];
80
+ if (serverError)
81
+ return serverError;
82
+ if (!submitAttempted && !touched[field])
83
+ return undefined;
84
+ return localErrors[field];
85
+ }, [fieldErrors, localErrors, submitAttempted, touched]);
86
+ const markTouched = useCallback((field) => setTouched((prev) => ({ ...prev, [field]: true })), []);
87
+ const handleSubmit = useCallback((event) => {
88
+ event.preventDefault();
89
+ // Guard duplicate submission: a second attempt while one is in flight is
90
+ // dropped rather than queued.
91
+ if (locked)
92
+ return;
93
+ setSubmitAttempted(true);
94
+ const order = [
95
+ "firstName",
96
+ "lastName",
97
+ "email",
98
+ "password",
99
+ "confirmPassword",
100
+ "consent",
101
+ ];
102
+ const firstInvalid = order.find((field) => localErrors[field]);
103
+ if (firstInvalid) {
104
+ fieldRefs.current[firstInvalid]?.focus();
105
+ return;
106
+ }
107
+ onSubmit({ ...values, consent });
108
+ }, [consent, localErrors, locked, onSubmit, values]);
109
+ const Heading = headingLevel;
110
+ const passwordError = errorFor("password");
111
+ const consentError = errorFor("consent");
112
+ const resolvedConsentLabel = consentLabel ?? "I agree to the terms of service and privacy policy.";
113
+ return (
114
+ // `@container` makes the block adapt to the column it is placed in, not
115
+ // to the viewport: a 360px settings column at 1440px behaves like a phone.
116
+ _jsx("section", { "aria-labelledby": titleId, className: cn("@container w-full", className), children: _jsxs("div", { className: "flex flex-col gap-6 rounded-12 border border-surface-border bg-surface p-4 @min-[420px]:p-6", children: [_jsxs("header", { className: "flex flex-col gap-2", children: [_jsx(Heading, { id: titleId, className: "text-h6 font-semibold text-grey-900", children: title }), description && _jsx("p", { className: "text-body-sm text-grey-600", children: description })] }), disabled && (_jsx(Alert, { state: "information", icon: "info-circle", role: "status", title: disabledMessage, className: "bg-surface dark:bg-surface" })), status === "error" && errorMessage && (_jsx(Alert, { state: "error", icon: "info-triangle", role: "alert", title: "We could not create your account", description: errorMessage, className: "bg-surface dark:bg-surface" })), succeeded && successMessage && (_jsx(Alert, { state: "success", icon: "check-circle", role: "status", title: successMessage, className: "bg-surface dark:bg-surface" })), loading ? (_jsx("div", { "aria-busy": "true", className: "flex min-h-40 flex-col items-center justify-center gap-3 rounded-8 border border-dashed border-surface-border-strong p-6", children: _jsx(Spinner, { size: "md", label: "Preparing the sign-up form\u2026", labelPosition: "below" }) })) : (_jsxs("form", { noValidate: true, onSubmit: handleSubmit, className: "flex flex-col gap-5", children: [_jsxs("div", { className: "grid grid-cols-1 gap-4 @min-[520px]:grid-cols-2", children: [_jsx(Input, { id: `${uid}-firstName`, ref: (node) => {
117
+ fieldRefs.current.firstName = node;
118
+ }, name: "firstName", label: labels?.firstName ?? "First name", autoComplete: "given-name", required: true, size: "md", disabled: locked, value: values.firstName, error: errorFor("firstName"), onChange: (e) => setValues((p) => ({ ...p, firstName: e.target.value })), onBlur: () => markTouched("firstName") }), _jsx(Input, { id: `${uid}-lastName`, ref: (node) => {
119
+ fieldRefs.current.lastName = node;
120
+ }, name: "lastName", label: labels?.lastName ?? "Last name", autoComplete: "family-name", required: true, size: "md", disabled: locked, value: values.lastName, error: errorFor("lastName"), onChange: (e) => setValues((p) => ({ ...p, lastName: e.target.value })), onBlur: () => markTouched("lastName") })] }), _jsx(Input, { id: `${uid}-email`, ref: (node) => {
121
+ fieldRefs.current.email = node;
122
+ }, name: "email", type: "email", inputMode: "email", label: labels?.email ?? "Work email", placeholder: "name@example.com", autoComplete: "email", required: true, size: "md", disabled: locked, value: values.email, error: errorFor("email"), onChange: (e) => setValues((p) => ({ ...p, email: e.target.value })), onBlur: () => markTouched("email") }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Input, { id: `${uid}-password`, ref: (node) => {
123
+ fieldRefs.current.password = node;
124
+ }, name: "password", type: revealPassword ? "text" : "password", label: labels?.password ?? "Password", autoComplete: "new-password", required: true, size: "md", disabled: locked, value: values.password,
125
+ // Boolean error: the message and the guidance list are rendered
126
+ // here so a single `aria-describedby` can reference both.
127
+ error: Boolean(passwordError), "aria-describedby": [passwordError ? passwordErrorId : null, passwordRulesId]
128
+ .filter(Boolean)
129
+ .join(" "), onChange: (e) => setValues((p) => ({ ...p, password: e.target.value })), onBlur: () => markTouched("password"), trailingAction: _jsxs("button", { type: "button",
130
+ // Stays available while a submission is in flight: it only
131
+ // changes how an already-entered value is displayed.
132
+ onClick: () => setRevealPassword((v) => !v), className: "inline-flex cursor-pointer items-center rounded p-1 text-grey-500 hover:text-grey-700 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-action-primary-text", children: [_jsx(Icon, { name: revealPassword ? "eye-slash" : "eye", size: "md" }), _jsx("span", { className: "sr-only", children: revealPassword ? "Hide password" : "Show password" })] }) }), passwordError && (_jsx("p", { id: passwordErrorId, className: "text-body-sm text-feedback-error", children: passwordError })), _jsx("ul", { id: passwordRulesId, role: "list", className: "flex flex-col gap-1", children: ruleResults.map((rule) => (_jsxs("li", { className: cn("flex items-center gap-2 text-body-sm", rule.met ? "text-feedback-success" : "text-grey-600"), children: [_jsx(Icon, { name: rule.met ? "check-circle" : "minus-circle", size: "sm" }), _jsx("span", { children: rule.label }), _jsx("span", { className: "sr-only", children: rule.met ? "— met" : "— not met yet" })] }, rule.id))) })] }), requireConfirmPassword && (_jsx(Input, { id: `${uid}-confirmPassword`, ref: (node) => {
133
+ fieldRefs.current.confirmPassword = node;
134
+ }, name: "confirmPassword", type: revealPassword ? "text" : "password", label: labels?.confirmPassword ?? "Confirm password", autoComplete: "new-password", required: true, size: "md", disabled: locked, value: confirmPassword, error: errorFor("confirmPassword"), onChange: (e) => setConfirmPassword(e.target.value), onBlur: () => markTouched("confirmPassword") })), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsxs("div", { className: "flex items-start gap-3", children: [_jsx("span", { className: "pt-0.5", children: _jsx(Checkbox, { id: consentId, ref: (node) => {
135
+ fieldRefs.current.consent = node;
136
+ }, name: "consent", checked: consent, disabled: locked, required: true, "aria-labelledby": consentTextId, "aria-invalid": consentError ? true : undefined, "aria-describedby": consentError ? consentErrorId : undefined, onChange: (e) => {
137
+ setConsent(e.target.checked);
138
+ markTouched("consent");
139
+ } }) }), _jsx("label", { id: consentTextId, htmlFor: consentId, className: cn("text-body-sm text-grey-700", locked ? "opacity-60" : "cursor-pointer"), children: resolvedConsentLabel })] }), consentError && (_jsx("p", { id: consentErrorId, className: "text-body-sm text-feedback-error", children: consentError }))] }), _jsxs("div", { className: "flex flex-col gap-3", children: [_jsx(Button, { type: "submit", size: "lg", variant: "primary", className: "w-full", disabled: locked, children: pending ? submittingLabel : submitLabel }), _jsx("output", { id: unsavedRegionId, className: "sr-only", children: pending ? submittingLabel : "" }), signIn && (_jsxs("p", { className: "text-center text-body-sm text-grey-600", children: ["Already have an account?", " ", _jsx("a", { href: signIn.href, "aria-disabled": pending || undefined, tabIndex: pending ? -1 : undefined, onClick: (event) => {
140
+ if (pending)
141
+ event.preventDefault();
142
+ }, className: cn("rounded font-semibold text-action-primary-text underline underline-offset-2 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-action-primary-text", pending && "pointer-events-none opacity-60"), children: signIn.label })] }))] })] }))] }) }));
143
+ }
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EmptyStateBlock = EmptyStateBlock;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const cn_1 = require("../utils/cn.cjs");
6
+ const EmptyStateIllustration_1 = require("../components/EmptyStateIllustration/index.cjs");
7
+ const Button_1 = require("../components/Button/index.cjs");
8
+ // ─── Component ───────────────────────────────────────────────────────
9
+ function EmptyStateBlock({ illustration, illustrationColored = true, illustrationPalette, illustrationSize = 130, title, description, action, variant = "inline", className, }) {
10
+ const content = ((0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col items-center text-center gap-4", children: [(0, jsx_runtime_1.jsx)(EmptyStateIllustration_1.EmptyStateIllustration, { name: illustration, colored: illustrationColored, palette: illustrationPalette, size: illustrationSize }), (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col items-center gap-0.5", children: [(0, jsx_runtime_1.jsx)("h3", { className: "text-base font-semibold text-grey-900", children: title }), (0, jsx_runtime_1.jsx)("p", { className: "text-xs text-grey-600 whitespace-pre-line", children: description })] }), action && ((0, jsx_runtime_1.jsx)(Button_1.Button, { variant: "primary", size: "sm", icon: action.icon, iconPosition: action.icon ? "leading" : "none", onClick: action.onClick,
11
+ /* Content-driven width: fits the supplied label on one line, wraps
12
+ intentionally past a safe maximum, and keeps the one-line height
13
+ as a floor so a wrapped label grows instead of being clipped. */
14
+ className: "h-auto min-h-9 w-auto max-w-64 whitespace-normal text-balance", children: action.label }))] }));
15
+ if (variant === "card") {
16
+ return ((0, jsx_runtime_1.jsx)("div", { className: (0, cn_1.cn)("bg-surface rounded-16 p-8 inline-flex max-w-full", className), children: content }));
17
+ }
18
+ return (0, jsx_runtime_1.jsx)("div", { className: className, children: content });
19
+ }
@@ -0,0 +1,16 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../utils/cn.js";
3
+ import { EmptyStateIllustration } from "../components/EmptyStateIllustration/index.js";
4
+ import { Button } from "../components/Button/index.js";
5
+ // ─── Component ───────────────────────────────────────────────────────
6
+ export function EmptyStateBlock({ illustration, illustrationColored = true, illustrationPalette, illustrationSize = 130, title, description, action, variant = "inline", className, }) {
7
+ const content = (_jsxs("div", { className: "flex flex-col items-center text-center gap-4", children: [_jsx(EmptyStateIllustration, { name: illustration, colored: illustrationColored, palette: illustrationPalette, size: illustrationSize }), _jsxs("div", { className: "flex flex-col items-center gap-0.5", children: [_jsx("h3", { className: "text-base font-semibold text-grey-900", children: title }), _jsx("p", { className: "text-xs text-grey-600 whitespace-pre-line", children: description })] }), action && (_jsx(Button, { variant: "primary", size: "sm", icon: action.icon, iconPosition: action.icon ? "leading" : "none", onClick: action.onClick,
8
+ /* Content-driven width: fits the supplied label on one line, wraps
9
+ intentionally past a safe maximum, and keeps the one-line height
10
+ as a floor so a wrapped label grows instead of being clipped. */
11
+ className: "h-auto min-h-9 w-auto max-w-64 whitespace-normal text-balance", children: action.label }))] }));
12
+ if (variant === "card") {
13
+ return (_jsx("div", { className: cn("bg-surface rounded-16 p-8 inline-flex max-w-full", className), children: content }));
14
+ }
15
+ return _jsx("div", { className: className, children: content });
16
+ }
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FeatureOverviewBlock = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_1 = require("react");
6
+ const cn_1 = require("../utils/cn.cjs");
7
+ const Badge_1 = require("../components/Badge/index.cjs");
8
+ const Icon_1 = require("../components/Icon/index.cjs");
9
+ const HEADING_ORDER = ["h1", "h2", "h3", "h4", "h5", "h6"];
10
+ function nextHeadingLevel(level) {
11
+ const index = HEADING_ORDER.indexOf(level);
12
+ return HEADING_ORDER[Math.min(index + 1, HEADING_ORDER.length - 1)];
13
+ }
14
+ function isLinkCta(cta) {
15
+ return typeof cta.href === "string";
16
+ }
17
+ /* ─── Appearance ───────────────────────────────────────────────────────── */
18
+ const COLUMN_CLASS = {
19
+ 2: "@min-[600px]:grid-cols-2",
20
+ 3: "@min-[600px]:grid-cols-2 @min-[1136px]:grid-cols-3",
21
+ 4: "@min-[600px]:grid-cols-2 @min-[1136px]:grid-cols-4",
22
+ };
23
+ const INLINE_CTA_CLASS = "inline-flex max-w-full items-center gap-1.5 rounded text-sm font-semibold break-words text-action-primary-text no-underline hover:underline underline-offset-4 cursor-pointer focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-action-primary-text";
24
+ const SECTION_CTA_CLASS = "inline-flex max-w-full items-center justify-center gap-2 min-h-11 rounded-lg border-[1.5px] border-current bg-transparent px-5 py-2.5 text-sm font-semibold break-words text-center text-action-primary-text no-underline transition-colors hover:bg-primary-50 cursor-pointer focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-action-primary-text";
25
+ function FeatureCta({ cta, className, trailingIcon = false }) {
26
+ const content = ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [cta.icon && (0, jsx_runtime_1.jsx)(Icon_1.Icon, { name: cta.icon, size: "sm", className: "shrink-0", "aria-hidden": "true" }), (0, jsx_runtime_1.jsx)("span", { className: "min-w-0", children: cta.label }), trailingIcon && !cta.icon && ((0, jsx_runtime_1.jsx)(Icon_1.Icon, { name: "arrow-right", size: "sm", className: "shrink-0", "aria-hidden": "true" }))] }));
27
+ if (isLinkCta(cta)) {
28
+ const externalAttributes = cta.external
29
+ ? { target: "_blank", rel: "noreferrer" }
30
+ : {};
31
+ return ((0, jsx_runtime_1.jsxs)("a", { href: cta.href, onClick: cta.onClick, className: className, ...externalAttributes, children: [content, cta.external && (0, jsx_runtime_1.jsx)("span", { className: "sr-only", children: "(opens in a new tab)" })] }));
32
+ }
33
+ return ((0, jsx_runtime_1.jsx)("button", { type: "button", onClick: cta.onClick, className: className, children: content }));
34
+ }
35
+ /* ─── Block ────────────────────────────────────────────────────────────── */
36
+ /**
37
+ * Feature Overview — lets a visitor scan differentiated capabilities and follow
38
+ * the details that matter to them.
39
+ *
40
+ * States: `default`, `loading`, `empty`, `error`. There is no success state:
41
+ * the block submits nothing. When `state` is left undefined it resolves to
42
+ * `empty` for an empty `features` array and `default` otherwise, so an empty
43
+ * source is never silently rendered as a blank grid.
44
+ */
45
+ exports.FeatureOverviewBlock = (0, react_1.forwardRef)(({ eyebrow, title, description, headingLevel = "h2", features, columns = 3, variant = "card", footerCta, state, loadingItemCount = 3, loadingLabel = "Loading features", emptyState, errorTitle = "We could not load these features", errorDescription, onRetry, retryLabel = "Try again", className, ...rest }, ref) => {
46
+ const headingId = (0, react_1.useId)();
47
+ const Heading = headingLevel;
48
+ const ItemHeading = nextHeadingLevel(headingLevel);
49
+ const resolvedState = state ?? (features.length === 0 ? "empty" : "default");
50
+ const isCard = variant === "card";
51
+ const itemClass = (0, cn_1.cn)("flex min-w-0 flex-col gap-3", isCard && "rounded-xl border border-surface-border bg-surface p-5 @min-[600px]:p-6");
52
+ return ((0, jsx_runtime_1.jsx)("section", { ref: ref, "aria-labelledby": headingId, "aria-busy": resolvedState === "loading" || undefined, className: (0, cn_1.cn)("@container relative w-full", className), ...rest, children: (0, jsx_runtime_1.jsxs)("div", { className: "mx-auto flex w-full max-w-[1136px] flex-col gap-8 px-4 py-12 @min-[600px]:gap-10 @min-[600px]:px-8 @min-[600px]:py-16", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex min-w-0 max-w-[68ch] flex-col gap-3", children: [eyebrow && ((0, jsx_runtime_1.jsx)("p", { className: "text-sm font-semibold break-words text-action-primary-text", children: eyebrow })), (0, jsx_runtime_1.jsx)(Heading, { id: headingId, className: "text-h5 font-semibold break-words text-balance text-grey-900 @min-[600px]:text-h3", children: title }), description && ((0, jsx_runtime_1.jsx)("p", { className: "text-base break-words text-grey-600 @min-[600px]:text-body-lg", children: description }))] }), resolvedState === "loading" && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("p", { role: "status", className: "sr-only", children: loadingLabel }), (0, jsx_runtime_1.jsx)("div", { className: (0, cn_1.cn)("grid grid-cols-1 gap-5 @min-[600px]:gap-6", COLUMN_CLASS[columns]), "aria-hidden": "true", children: Array.from({ length: Math.max(1, loadingItemCount) }).map((_, index) => ((0, jsx_runtime_1.jsxs)("div", { className: (0, cn_1.cn)(itemClass, !isCard && "gap-3"), children: [(0, jsx_runtime_1.jsx)("div", { className: "size-11 rounded-lg bg-grey-200" }), (0, jsx_runtime_1.jsx)("div", { className: "h-5 w-3/5 rounded bg-grey-200" }), (0, jsx_runtime_1.jsx)("div", { className: "h-4 w-full rounded bg-grey-100" }), (0, jsx_runtime_1.jsx)("div", { className: "h-4 w-4/5 rounded bg-grey-100" })] }, `feature-skeleton-${index}`))) })] })), resolvedState === "error" && ((0, jsx_runtime_1.jsxs)("div", { role: "alert", className: "flex min-w-0 flex-col items-start gap-3 rounded-xl border border-surface-border bg-surface p-5 @min-[600px]:p-6", children: [(0, jsx_runtime_1.jsx)("span", { className: "flex size-10 shrink-0 items-center justify-center rounded-full bg-error-50 text-action-danger-text", children: (0, jsx_runtime_1.jsx)(Icon_1.Icon, { name: "info-triangle", size: "lg", "aria-hidden": "true" }) }), (0, jsx_runtime_1.jsx)("p", { className: "text-base font-semibold break-words text-grey-900", children: errorTitle }), errorDescription && ((0, jsx_runtime_1.jsx)("p", { className: "max-w-[60ch] text-sm break-words text-grey-600", children: errorDescription })), onRetry && ((0, jsx_runtime_1.jsxs)("button", { type: "button", onClick: onRetry, className: SECTION_CTA_CLASS, children: [(0, jsx_runtime_1.jsx)(Icon_1.Icon, { name: "refresh", size: "sm", className: "shrink-0", "aria-hidden": "true" }), (0, jsx_runtime_1.jsx)("span", { className: "min-w-0", children: retryLabel })] }))] })), resolvedState === "empty" && ((0, jsx_runtime_1.jsxs)("div", { className: "flex min-w-0 flex-col items-start gap-3 rounded-xl border border-dashed border-surface-border-strong bg-surface p-6 @min-[600px]:p-8", children: [(0, jsx_runtime_1.jsx)("span", { className: "flex size-10 shrink-0 items-center justify-center rounded-full bg-grey-100 text-grey-600", children: (0, jsx_runtime_1.jsx)(Icon_1.Icon, { name: "layer", size: "lg", "aria-hidden": "true" }) }), (0, jsx_runtime_1.jsx)("p", { className: "text-base font-semibold break-words text-grey-900", children: emptyState?.title ?? "No features to show yet" }), emptyState?.description && ((0, jsx_runtime_1.jsx)("p", { className: "max-w-[60ch] text-sm break-words text-grey-600", children: emptyState.description })), emptyState?.cta && ((0, jsx_runtime_1.jsx)(FeatureCta, { cta: emptyState.cta, className: SECTION_CTA_CLASS }))] })), resolvedState === "default" && features.length > 0 && ((0, jsx_runtime_1.jsx)("ul", { className: (0, cn_1.cn)("grid list-none grid-cols-1 gap-5 p-0 @min-[600px]:gap-6", COLUMN_CLASS[columns]), children: features.map((feature) => ((0, jsx_runtime_1.jsxs)("li", { className: itemClass, children: [feature.icon && ((0, jsx_runtime_1.jsx)("span", { className: "flex size-11 shrink-0 items-center justify-center rounded-lg bg-primary-50 text-action-primary-text", children: (0, jsx_runtime_1.jsx)(Icon_1.Icon, { name: feature.icon, size: "lg", "aria-hidden": "true" }) })), (0, jsx_runtime_1.jsxs)("div", { className: "flex min-w-0 flex-wrap items-center gap-2", children: [(0, jsx_runtime_1.jsx)(ItemHeading, { className: "min-w-0 text-base font-semibold break-words text-grey-900 @min-[600px]:text-body-lg", children: feature.title }), feature.badge && ((0, jsx_runtime_1.jsx)(Badge_1.Badge, { color: "neutral", type: "accent", size: "sm", children: feature.badge }))] }), feature.description && ((0, jsx_runtime_1.jsx)("p", { className: "text-sm break-words text-grey-600 @min-[600px]:text-base", children: feature.description })), feature.cta && ((0, jsx_runtime_1.jsx)(FeatureCta, { cta: feature.cta, className: INLINE_CTA_CLASS, trailingIcon: true }))] }, feature.id))) })), footerCta && resolvedState === "default" && ((0, jsx_runtime_1.jsx)("div", { className: "flex min-w-0", children: (0, jsx_runtime_1.jsx)(FeatureCta, { cta: footerCta, className: SECTION_CTA_CLASS }) }))] }) }));
53
+ });
54
+ exports.FeatureOverviewBlock.displayName = "FeatureOverviewBlock";
@@ -0,0 +1,51 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { forwardRef, useId, } from "react";
3
+ import { cn } from "../utils/cn.js";
4
+ import { Badge } from "../components/Badge/index.js";
5
+ import { Icon } from "../components/Icon/index.js";
6
+ const HEADING_ORDER = ["h1", "h2", "h3", "h4", "h5", "h6"];
7
+ function nextHeadingLevel(level) {
8
+ const index = HEADING_ORDER.indexOf(level);
9
+ return HEADING_ORDER[Math.min(index + 1, HEADING_ORDER.length - 1)];
10
+ }
11
+ function isLinkCta(cta) {
12
+ return typeof cta.href === "string";
13
+ }
14
+ /* ─── Appearance ───────────────────────────────────────────────────────── */
15
+ const COLUMN_CLASS = {
16
+ 2: "@min-[600px]:grid-cols-2",
17
+ 3: "@min-[600px]:grid-cols-2 @min-[1136px]:grid-cols-3",
18
+ 4: "@min-[600px]:grid-cols-2 @min-[1136px]:grid-cols-4",
19
+ };
20
+ const INLINE_CTA_CLASS = "inline-flex max-w-full items-center gap-1.5 rounded text-sm font-semibold break-words text-action-primary-text no-underline hover:underline underline-offset-4 cursor-pointer focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-action-primary-text";
21
+ const SECTION_CTA_CLASS = "inline-flex max-w-full items-center justify-center gap-2 min-h-11 rounded-lg border-[1.5px] border-current bg-transparent px-5 py-2.5 text-sm font-semibold break-words text-center text-action-primary-text no-underline transition-colors hover:bg-primary-50 cursor-pointer focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-action-primary-text";
22
+ function FeatureCta({ cta, className, trailingIcon = false }) {
23
+ const content = (_jsxs(_Fragment, { children: [cta.icon && _jsx(Icon, { name: cta.icon, size: "sm", className: "shrink-0", "aria-hidden": "true" }), _jsx("span", { className: "min-w-0", children: cta.label }), trailingIcon && !cta.icon && (_jsx(Icon, { name: "arrow-right", size: "sm", className: "shrink-0", "aria-hidden": "true" }))] }));
24
+ if (isLinkCta(cta)) {
25
+ const externalAttributes = cta.external
26
+ ? { target: "_blank", rel: "noreferrer" }
27
+ : {};
28
+ return (_jsxs("a", { href: cta.href, onClick: cta.onClick, className: className, ...externalAttributes, children: [content, cta.external && _jsx("span", { className: "sr-only", children: "(opens in a new tab)" })] }));
29
+ }
30
+ return (_jsx("button", { type: "button", onClick: cta.onClick, className: className, children: content }));
31
+ }
32
+ /* ─── Block ────────────────────────────────────────────────────────────── */
33
+ /**
34
+ * Feature Overview — lets a visitor scan differentiated capabilities and follow
35
+ * the details that matter to them.
36
+ *
37
+ * States: `default`, `loading`, `empty`, `error`. There is no success state:
38
+ * the block submits nothing. When `state` is left undefined it resolves to
39
+ * `empty` for an empty `features` array and `default` otherwise, so an empty
40
+ * source is never silently rendered as a blank grid.
41
+ */
42
+ export const FeatureOverviewBlock = forwardRef(({ eyebrow, title, description, headingLevel = "h2", features, columns = 3, variant = "card", footerCta, state, loadingItemCount = 3, loadingLabel = "Loading features", emptyState, errorTitle = "We could not load these features", errorDescription, onRetry, retryLabel = "Try again", className, ...rest }, ref) => {
43
+ const headingId = useId();
44
+ const Heading = headingLevel;
45
+ const ItemHeading = nextHeadingLevel(headingLevel);
46
+ const resolvedState = state ?? (features.length === 0 ? "empty" : "default");
47
+ const isCard = variant === "card";
48
+ const itemClass = cn("flex min-w-0 flex-col gap-3", isCard && "rounded-xl border border-surface-border bg-surface p-5 @min-[600px]:p-6");
49
+ return (_jsx("section", { ref: ref, "aria-labelledby": headingId, "aria-busy": resolvedState === "loading" || undefined, className: cn("@container relative w-full", className), ...rest, children: _jsxs("div", { className: "mx-auto flex w-full max-w-[1136px] flex-col gap-8 px-4 py-12 @min-[600px]:gap-10 @min-[600px]:px-8 @min-[600px]:py-16", children: [_jsxs("div", { className: "flex min-w-0 max-w-[68ch] flex-col gap-3", children: [eyebrow && (_jsx("p", { className: "text-sm font-semibold break-words text-action-primary-text", children: eyebrow })), _jsx(Heading, { id: headingId, className: "text-h5 font-semibold break-words text-balance text-grey-900 @min-[600px]:text-h3", children: title }), description && (_jsx("p", { className: "text-base break-words text-grey-600 @min-[600px]:text-body-lg", children: description }))] }), resolvedState === "loading" && (_jsxs(_Fragment, { children: [_jsx("p", { role: "status", className: "sr-only", children: loadingLabel }), _jsx("div", { className: cn("grid grid-cols-1 gap-5 @min-[600px]:gap-6", COLUMN_CLASS[columns]), "aria-hidden": "true", children: Array.from({ length: Math.max(1, loadingItemCount) }).map((_, index) => (_jsxs("div", { className: cn(itemClass, !isCard && "gap-3"), children: [_jsx("div", { className: "size-11 rounded-lg bg-grey-200" }), _jsx("div", { className: "h-5 w-3/5 rounded bg-grey-200" }), _jsx("div", { className: "h-4 w-full rounded bg-grey-100" }), _jsx("div", { className: "h-4 w-4/5 rounded bg-grey-100" })] }, `feature-skeleton-${index}`))) })] })), resolvedState === "error" && (_jsxs("div", { role: "alert", className: "flex min-w-0 flex-col items-start gap-3 rounded-xl border border-surface-border bg-surface p-5 @min-[600px]:p-6", children: [_jsx("span", { className: "flex size-10 shrink-0 items-center justify-center rounded-full bg-error-50 text-action-danger-text", children: _jsx(Icon, { name: "info-triangle", size: "lg", "aria-hidden": "true" }) }), _jsx("p", { className: "text-base font-semibold break-words text-grey-900", children: errorTitle }), errorDescription && (_jsx("p", { className: "max-w-[60ch] text-sm break-words text-grey-600", children: errorDescription })), onRetry && (_jsxs("button", { type: "button", onClick: onRetry, className: SECTION_CTA_CLASS, children: [_jsx(Icon, { name: "refresh", size: "sm", className: "shrink-0", "aria-hidden": "true" }), _jsx("span", { className: "min-w-0", children: retryLabel })] }))] })), resolvedState === "empty" && (_jsxs("div", { className: "flex min-w-0 flex-col items-start gap-3 rounded-xl border border-dashed border-surface-border-strong bg-surface p-6 @min-[600px]:p-8", children: [_jsx("span", { className: "flex size-10 shrink-0 items-center justify-center rounded-full bg-grey-100 text-grey-600", children: _jsx(Icon, { name: "layer", size: "lg", "aria-hidden": "true" }) }), _jsx("p", { className: "text-base font-semibold break-words text-grey-900", children: emptyState?.title ?? "No features to show yet" }), emptyState?.description && (_jsx("p", { className: "max-w-[60ch] text-sm break-words text-grey-600", children: emptyState.description })), emptyState?.cta && (_jsx(FeatureCta, { cta: emptyState.cta, className: SECTION_CTA_CLASS }))] })), resolvedState === "default" && features.length > 0 && (_jsx("ul", { className: cn("grid list-none grid-cols-1 gap-5 p-0 @min-[600px]:gap-6", COLUMN_CLASS[columns]), children: features.map((feature) => (_jsxs("li", { className: itemClass, children: [feature.icon && (_jsx("span", { className: "flex size-11 shrink-0 items-center justify-center rounded-lg bg-primary-50 text-action-primary-text", children: _jsx(Icon, { name: feature.icon, size: "lg", "aria-hidden": "true" }) })), _jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-2", children: [_jsx(ItemHeading, { className: "min-w-0 text-base font-semibold break-words text-grey-900 @min-[600px]:text-body-lg", children: feature.title }), feature.badge && (_jsx(Badge, { color: "neutral", type: "accent", size: "sm", children: feature.badge }))] }), feature.description && (_jsx("p", { className: "text-sm break-words text-grey-600 @min-[600px]:text-base", children: feature.description })), feature.cta && (_jsx(FeatureCta, { cta: feature.cta, className: INLINE_CTA_CLASS, trailingIcon: true }))] }, feature.id))) })), footerCta && resolvedState === "default" && (_jsx("div", { className: "flex min-w-0", children: _jsx(FeatureCta, { cta: footerCta, className: SECTION_CTA_CLASS }) }))] }) }));
50
+ });
51
+ FeatureOverviewBlock.displayName = "FeatureOverviewBlock";
@@ -0,0 +1,230 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HeaderBlock = HeaderBlock;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_1 = require("react");
6
+ const cn_1 = require("../utils/cn.cjs");
7
+ const Icon_1 = require("../components/Icon/index.cjs");
8
+ const Input_1 = require("../components/Input/index.cjs");
9
+ const TIER = {
10
+ sm: {
11
+ row: "hidden @min-[720px]:flex",
12
+ block: "hidden @min-[720px]:block",
13
+ collapsed: "@min-[720px]:hidden",
14
+ },
15
+ md: {
16
+ row: "hidden @min-[880px]:flex",
17
+ block: "hidden @min-[880px]:block",
18
+ collapsed: "@min-[880px]:hidden",
19
+ },
20
+ lg: {
21
+ row: "hidden @min-[960px]:flex",
22
+ block: "hidden @min-[960px]:block",
23
+ collapsed: "@min-[960px]:hidden",
24
+ },
25
+ xl: {
26
+ row: "hidden @min-[1060px]:flex",
27
+ block: "hidden @min-[1060px]:block",
28
+ collapsed: "@min-[1060px]:hidden",
29
+ },
30
+ xxl: {
31
+ row: "hidden @min-[1280px]:flex",
32
+ block: "hidden @min-[1280px]:block",
33
+ collapsed: "@min-[1280px]:hidden",
34
+ },
35
+ };
36
+ const VARIANT_TIER = {
37
+ 1: "sm",
38
+ 2: "lg",
39
+ 3: "md",
40
+ 4: "md",
41
+ 5: "md",
42
+ 6: "sm",
43
+ 7: "xl",
44
+ 8: "xxl",
45
+ 9: "sm",
46
+ 10: "md",
47
+ 11: "sm",
48
+ };
49
+ /**
50
+ * Shared gutters and a bounded row keep the brand and controls aligned on every variant.
51
+ * Only the surface and announcement stretch on wider screens.
52
+ */
53
+ const ROW_PADDING = "mx-auto w-full max-w-[1440px] px-4 py-3 @min-[640px]:px-6 @min-[1140px]:px-10 @min-[1140px]:py-4";
54
+ /** Nav rows tighten up between the collapse threshold and a comfortable desktop. */
55
+ const NAV_GAP = "gap-4 @min-[1140px]:gap-6";
56
+ const PILL_GAP = "gap-5 @min-[1140px]:gap-8";
57
+ /**
58
+ * A variant that asks for a dark bar has to stay dark in a globally dark document.
59
+ * The grey ramp inverts under `.dark`, so `bg-grey-900` quietly became a near-white
60
+ * bar with unreadable navigation on top of it.
61
+ *
62
+ * `.rayden-dark` is the shared home for that: it is declared next to `.dark` in
63
+ * globals.css, pins the same surface / foreground / ramp values, and is matched by
64
+ * the `dark:` variant, so the same semantic pairs — `bg-surface`/`text-on-surface`
65
+ * for the bar, `text-on-surface-muted` for muted text — resolve to dark values in
66
+ * *both* global modes, and nested components inherit the same roles. It replaces a
67
+ * block-local inline style object, which no nested component could see through.
68
+ */
69
+ const DARK_ISLAND = "rayden-dark";
70
+ const FOCUS_RING = "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-action-primary-text";
71
+ // ─── Sub-components ──────────────────────────────────────────────────
72
+ function NavLink({ link, tone = "default", large, className, onNavigate, }) {
73
+ // A link with an href must be an anchor, or it is not a link: no middle-click, no
74
+ // open-in-new-tab, and the wrong role. This holds at every viewport — the collapsed
75
+ // menu used to rebuild links as buttons and drop the href on the floor.
76
+ const Tag = link.href ? "a" : "button";
77
+ return ((0, jsx_runtime_1.jsxs)(Tag, { ...(link.href ? { href: link.href } : { type: "button" }), "aria-current": link.active ? "page" : undefined, onClick: () => {
78
+ link.onClick?.();
79
+ onNavigate?.();
80
+ }, className: (0, cn_1.cn)("relative flex min-h-11 items-center gap-2 rounded-md font-medium whitespace-nowrap transition-colors duration-150 motion-reduce:transition-none cursor-pointer", FOCUS_RING, large ? "text-base" : "text-sm", link.active
81
+ ? "text-action-primary-text after:absolute after:inset-x-0 after:bottom-0 after:h-0.5 after:rounded-full after:bg-action-primary"
82
+ : tone === "muted"
83
+ ? "text-on-surface-muted hover:text-on-surface"
84
+ : "text-on-surface-secondary hover:text-on-surface", className), children: [link.label, link.dot && (0, jsx_runtime_1.jsx)("span", { className: "size-2 shrink-0 rounded-full bg-primary-400" }), link.hasDropdown && (0, jsx_runtime_1.jsx)(Icon_1.Icon, { name: "chevron-down", size: "xs" })] }));
85
+ }
86
+ function NavLinks({ links, large, gap = NAV_GAP, className, }) {
87
+ if (links.length === 0)
88
+ return null;
89
+ return ((0, jsx_runtime_1.jsx)("div", { className: (0, cn_1.cn)("flex items-center", gap, className), children: links.map((link, i) => ((0, jsx_runtime_1.jsx)(NavLink, { link: link, large: large }, `${link.label}-${i}`))) }));
90
+ }
91
+ /** Same link component, stacked — so an href-only destination is still an anchor. */
92
+ function StackedNavLinks({ links, tone, onNavigate, }) {
93
+ if (links.length === 0)
94
+ return null;
95
+ return ((0, jsx_runtime_1.jsx)("div", { className: "flex flex-col gap-1", children: links.map((link, i) => ((0, jsx_runtime_1.jsx)(NavLink, { link: link, tone: tone, onNavigate: onNavigate, className: (0, cn_1.cn)("justify-between rounded-lg px-3 py-2.5 text-left after:hidden hover:bg-grey-75", link.active && "bg-primary-50 hover:bg-primary-50") }, `${link.label}-${i}`))) }));
96
+ }
97
+ // ─── Component ───────────────────────────────────────────────────────
98
+ function HeaderBlock({ variant = 1, logo, links = [], rightLinks = [], actions = [], announcement, searchPlaceholder = "Search for components...", onSearch, switcher, secondaryLinks = [], navLabel = "Main", secondaryNavLabel = "Secondary", className, }) {
99
+ const [searchQuery, setSearchQuery] = (0, react_1.useState)("");
100
+ const [internalActiveTab, setActiveTab] = (0, react_1.useState)(switcher?.defaultActiveIndex ?? 0);
101
+ /** An index outside the tab list would leave the group with nothing selected. */
102
+ const clampTab = (index) => Math.min(Math.max(index, 0), Math.max((switcher?.tabs.length ?? 1) - 1, 0));
103
+ const isSwitcherControlled = typeof switcher?.activeIndex === "number";
104
+ const activeTab = clampTab(isSwitcherControlled ? switcher.activeIndex : internalActiveTab);
105
+ /** One place decides both modes, so they can never drift apart. */
106
+ const selectTab = (index) => {
107
+ if (index === activeTab)
108
+ return; // not a change; raise nothing
109
+ if (!isSwitcherControlled)
110
+ setActiveTab(index);
111
+ switcher?.onChange?.(index);
112
+ };
113
+ const [menuOpen, setMenuOpen] = (0, react_1.useState)(false);
114
+ const menuId = (0, react_1.useId)();
115
+ const toggleRef = (0, react_1.useRef)(null);
116
+ const isDark = variant === 4 || variant === 8;
117
+ const isLargeLinks = [3, 4, 5, 6, 9, 10, 11].includes(variant);
118
+ const isRoundedButtons = [5, 6, 7, 8, 9, 10].includes(variant);
119
+ const hasSearch = variant === 9;
120
+ const hasSwitcher = (variant === 10 || variant === 11) && !!switcher;
121
+ const tier = TIER[VARIANT_TIER[variant]];
122
+ const rowPadding = ROW_PADDING;
123
+ /** Nothing to collapse means nothing to toggle: do not render an inert control. */
124
+ const hasMenuContent = links.length > 0 ||
125
+ rightLinks.length > 0 ||
126
+ secondaryLinks.length > 0 ||
127
+ actions.length > 0 ||
128
+ hasSearch ||
129
+ hasSwitcher;
130
+ const closeMenu = () => setMenuOpen(false);
131
+ function handleKeyDown(event) {
132
+ if (event.key !== "Escape" || !menuOpen)
133
+ return;
134
+ setMenuOpen(false);
135
+ toggleRef.current?.focus();
136
+ }
137
+ // ─── Render a single action button ─────────────────────────────
138
+ function renderAction(action, index, onNavigate) {
139
+ const Tag = action.href ? "a" : "button";
140
+ const actionVariant = action.variant ?? "primary";
141
+ const isLargeButton = [3, 4, 5, 11].includes(variant);
142
+ return ((0, jsx_runtime_1.jsx)(Tag, { ...(action.href ? { href: action.href } : { type: "button" }), onClick: () => {
143
+ action.onClick?.();
144
+ onNavigate?.();
145
+ }, className: (0, cn_1.cn)("inline-flex min-h-11 cursor-pointer items-center justify-center whitespace-nowrap px-4 py-2 text-sm font-semibold transition-colors duration-150 motion-reduce:transition-none", FOCUS_RING, isRoundedButtons ? "rounded-2xl" : "rounded-lg", actionVariant === "text" &&
146
+ "rounded-lg text-on-surface-secondary hover:bg-grey-75 hover:text-on-surface active:bg-grey-100", actionVariant === "secondary" &&
147
+ "border border-surface-border-strong bg-surface text-on-surface hover:bg-grey-75 active:bg-grey-100", actionVariant === "secondary" &&
148
+ variant === 5 &&
149
+ "border-transparent bg-primary-50 text-action-primary-text hover:bg-primary-75", actionVariant === "secondary" &&
150
+ variant === 8 &&
151
+ "border-transparent bg-grey-100 hover:bg-grey-200", actionVariant === "primary" &&
152
+ "bg-action-primary text-white hover:bg-action-primary-hover active:bg-action-primary-hover", actionVariant !== "text" && isLargeButton && "px-5 text-base", actionVariant === "primary" && (variant === 3 || variant === 4) && "rounded-full"), children: action.label }, index));
153
+ }
154
+ // ─── Actions section ───────────────────────────────────────────
155
+ const actionsSection = actions.length > 0 && ((0, jsx_runtime_1.jsx)("div", { className: "flex shrink-0 items-center gap-2 @min-[1140px]:gap-3", children: actions.map((a, i) => renderAction(a, i)) }));
156
+ // ─── Search ────────────────────────────────────────────────────
157
+ function renderSearch(wrapperClassName) {
158
+ if (!hasSearch)
159
+ return null;
160
+ return ((0, jsx_runtime_1.jsx)(Input_1.Input, { size: "sm", type: "search", "aria-label": searchPlaceholder, placeholder: searchPlaceholder, leadingIcon: "search", value: searchQuery, onChange: (e) => {
161
+ setSearchQuery(e.target.value);
162
+ onSearch?.(e.target.value);
163
+ }, wrapperClassName: wrapperClassName }));
164
+ }
165
+ // ─── Switcher ──────────────────────────────────────────────────
166
+ function renderSwitcher(className) {
167
+ if (!switcher || switcher.tabs.length === 0)
168
+ return null;
169
+ const switcherLabel = switcher.label ?? "Workspace";
170
+ if (variant === 11) {
171
+ // v11: pill-style tabs
172
+ return ((0, jsx_runtime_1.jsx)("div", { role: "group", "aria-label": switcherLabel, className: (0, cn_1.cn)("flex items-center gap-2", className), children: switcher.tabs.map((tab, i) => ((0, jsx_runtime_1.jsx)("button", { type: "button", "aria-pressed": i === activeTab, onClick: () => selectTab(i), className: (0, cn_1.cn)("cursor-pointer rounded-lg px-4 py-2 text-sm font-semibold whitespace-nowrap", FOCUS_RING, i === activeTab
173
+ ? "bg-primary-50 text-action-primary-text"
174
+ : "text-grey-500 hover:text-grey-700"), children: tab }, tab))) }));
175
+ }
176
+ // v10: segmented control. The selected chip is a raised surface rather than a
177
+ // hard-coded white one, so it follows the document's mode.
178
+ return ((0, jsx_runtime_1.jsx)("div", { role: "group", "aria-label": switcherLabel, className: (0, cn_1.cn)("flex items-center", className), children: switcher.tabs.map((tab, i) => ((0, jsx_runtime_1.jsx)("button", { type: "button", "aria-pressed": i === activeTab, onClick: () => selectTab(i), className: (0, cn_1.cn)("cursor-pointer px-4 py-2.5 text-sm whitespace-nowrap", FOCUS_RING, i === 0 && "rounded-l-lg", i === switcher.tabs.length - 1 && "rounded-r-lg", i === activeTab
179
+ ? "border border-surface-border-strong bg-surface font-semibold text-grey-900"
180
+ : "bg-grey-200 font-medium text-grey-600"), children: tab }, tab))) }));
181
+ }
182
+ // ─── Pill nav (variants 3, 4) ──────────────────────────────────
183
+ function renderPillNav() {
184
+ return ((0, jsx_runtime_1.jsx)("div", { className: (0, cn_1.cn)("flex items-center rounded-full px-6 py-1 @min-[1140px]:px-8", PILL_GAP, isDark ? "bg-[#292424]" : "bg-grey-75"), children: links.map((link, i) => ((0, jsx_runtime_1.jsx)(NavLink, { link: link, large: true }, `${link.label}-${i}`))) }));
185
+ }
186
+ // ─── Collapsed menu toggle ─────────────────────────────────────
187
+ function renderMenuToggle() {
188
+ if (!hasMenuContent)
189
+ return null;
190
+ return ((0, jsx_runtime_1.jsx)("button", { ref: toggleRef, type: "button", onClick: () => setMenuOpen((open) => !open), "aria-expanded": menuOpen, "aria-controls": menuId, "aria-label": menuOpen ? "Close menu" : "Open menu", className: (0, cn_1.cn)("flex size-11 shrink-0 cursor-pointer items-center justify-center rounded-lg border border-surface-border-strong text-on-surface-secondary transition-colors hover:bg-grey-75 hover:text-on-surface motion-reduce:transition-none", FOCUS_RING, tier.collapsed), children: (0, jsx_runtime_1.jsx)(Icon_1.Icon, { name: menuOpen ? "multiply" : "list", size: "md" }) }));
191
+ }
192
+ // ─── Collapsed menu ────────────────────────────────────────────
193
+ /**
194
+ * Always in the DOM so `aria-controls` resolves to a real element, and carrying the
195
+ * same destinations, search, switcher and actions the expanded row has.
196
+ */
197
+ function renderMenu() {
198
+ if (!hasMenuContent)
199
+ return null;
200
+ const hasNavContent = links.length > 0 || rightLinks.length > 0 || secondaryLinks.length > 0;
201
+ return ((0, jsx_runtime_1.jsxs)("div", { id: menuId, className: (0, cn_1.cn)("flex flex-col gap-4 border-t border-surface-border bg-surface px-4 pt-4 pb-5 @min-[640px]:px-6", tier.collapsed, !menuOpen && "hidden"), children: [renderSearch("w-full"), hasSwitcher && renderSwitcher("w-full flex-wrap"), hasNavContent && ((0, jsx_runtime_1.jsxs)("nav", { "aria-label": "Menu", className: "flex flex-col gap-3", children: [(0, jsx_runtime_1.jsx)(StackedNavLinks, { links: links, onNavigate: closeMenu }), rightLinks.length > 0 && links.length > 0 && ((0, jsx_runtime_1.jsx)("div", { className: "h-px w-full bg-surface-border" })), (0, jsx_runtime_1.jsx)(StackedNavLinks, { links: rightLinks, onNavigate: closeMenu }), secondaryLinks.length > 0 && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { className: "h-px w-full bg-surface-border" }), (0, jsx_runtime_1.jsx)(StackedNavLinks, { links: secondaryLinks, tone: "muted", onNavigate: closeMenu })] }))] })), actions.length > 0 && ((0, jsx_runtime_1.jsx)("div", { className: "flex flex-col gap-2 border-t border-surface-border pt-4", children: actions.map((action, i) => renderAction(action, i, closeMenu)) }))] }));
202
+ }
203
+ const rootProps = {
204
+ className: (0, cn_1.cn)("@container flex w-full flex-col border-b border-surface-border bg-surface", isDark && DARK_ISLAND, className),
205
+ onKeyDown: handleKeyDown,
206
+ };
207
+ // ═══════════════════════════════════════════════════════════════
208
+ // VARIANT 11: Double-row header
209
+ // ═══════════════════════════════════════════════════════════════
210
+ if (variant === 11) {
211
+ return ((0, jsx_runtime_1.jsxs)("header", { ...rootProps, children: [(0, jsx_runtime_1.jsxs)("div", { className: (0, cn_1.cn)("flex items-center justify-between", rowPadding), children: [(0, jsx_runtime_1.jsx)("div", { className: tier.block, children: renderSwitcher() }), secondaryLinks.length > 0 && ((0, jsx_runtime_1.jsx)("nav", { "aria-label": secondaryNavLabel, className: (0, cn_1.cn)(tier.row, "items-center", NAV_GAP), children: secondaryLinks.map((link, i) => ((0, jsx_runtime_1.jsx)(NavLink, { link: link, tone: "muted" }, `${link.label}-${i}`))) })), (0, jsx_runtime_1.jsx)("div", { className: (0, cn_1.cn)("min-w-0 flex-1", tier.collapsed), children: logo }), renderMenuToggle()] }), (0, jsx_runtime_1.jsx)("div", { className: "h-px w-full bg-surface-border" }), (0, jsx_runtime_1.jsxs)("div", { className: (0, cn_1.cn)(tier.row, "items-center justify-between", rowPadding), children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex min-w-0 items-center gap-6 @min-[1140px]:gap-10", children: [logo, links.length > 0 && ((0, jsx_runtime_1.jsx)("nav", { "aria-label": navLabel, children: (0, jsx_runtime_1.jsx)(NavLinks, { links: links, large: true, gap: NAV_GAP }) }))] }), actionsSection] }), renderMenu()] }));
212
+ }
213
+ // ═══════════════════════════════════════════════════════════════
214
+ // VARIANTS 7, 8: Centered logo layout
215
+ // Left links | Logo (center) | Right links + Actions
216
+ // ═══════════════════════════════════════════════════════════════
217
+ if (variant === 7 || variant === 8) {
218
+ return ((0, jsx_runtime_1.jsxs)("header", { ...rootProps, children: [(0, jsx_runtime_1.jsxs)("div", { className: (0, cn_1.cn)("flex w-full items-center justify-between gap-4", variant === 7
219
+ ? "@min-[1060px]:grid @min-[1060px]:grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)]"
220
+ : "@min-[1280px]:grid @min-[1280px]:grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)]", rowPadding), children: [(0, jsx_runtime_1.jsx)("div", { className: (0, cn_1.cn)(tier.block, "min-w-0"), children: links.length > 0 && ((0, jsx_runtime_1.jsx)("nav", { "aria-label": navLabel, children: (0, jsx_runtime_1.jsx)(NavLinks, { links: links }) })) }), (0, jsx_runtime_1.jsx)("div", { className: "min-w-0", children: logo }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-self-end gap-4 @min-[1140px]:gap-6", children: [(0, jsx_runtime_1.jsxs)("div", { className: (0, cn_1.cn)(tier.row, "items-center gap-4 @min-[1140px]:gap-6"), children: [rightLinks.length > 0 && ((0, jsx_runtime_1.jsx)("nav", { "aria-label": secondaryNavLabel, children: (0, jsx_runtime_1.jsx)(NavLinks, { links: rightLinks }) })), actionsSection] }), renderMenuToggle()] })] }), renderMenu()] }));
221
+ }
222
+ // ═══════════════════════════════════════════════════════════════
223
+ // ALL OTHER VARIANTS (1–6, 9, 10)
224
+ // Logo (left) | Links (center) | Actions (right)
225
+ // ═══════════════════════════════════════════════════════════════
226
+ return ((0, jsx_runtime_1.jsxs)("header", { ...rootProps, children: [variant === 1 && announcement && ((0, jsx_runtime_1.jsxs)("div", { className: "flex w-full flex-wrap items-center justify-center gap-x-3 gap-y-1 bg-[#290b00] px-4 py-2.5 text-center", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-xs text-white", children: announcement.text }), announcement.linkLabel && ((0, jsx_runtime_1.jsx)("button", { type: "button", onClick: announcement.onLinkClick, className: (0, cn_1.cn)("cursor-pointer rounded text-xs font-semibold text-warning-400", FOCUS_RING), children: announcement.linkLabel }))] })), (0, jsx_runtime_1.jsxs)("div", { className: (0, cn_1.cn)("flex w-full items-center justify-between gap-4", rowPadding), children: [(0, jsx_runtime_1.jsxs)("div", { className: (0, cn_1.cn)("flex items-center gap-4 @min-[1140px]:gap-10",
227
+ // The search field is the one element allowed to absorb the slack, so it
228
+ // shrinks instead of pinning the row to a 375px minimum.
229
+ hasSearch ? "min-w-0 flex-1" : "shrink-0"), children: [logo, renderSearch((0, cn_1.cn)(tier.block, "min-w-[200px] max-w-[375px] flex-1")), hasSwitcher && (0, jsx_runtime_1.jsx)("div", { className: tier.block, children: renderSwitcher() })] }), (links.length > 0 || variant === 3 || variant === 4) && ((0, jsx_runtime_1.jsx)("nav", { "aria-label": navLabel, className: tier.block, children: variant === 3 || variant === 4 ? (renderPillNav()) : ((0, jsx_runtime_1.jsx)(NavLinks, { links: links, large: isLargeLinks })) })), (0, jsx_runtime_1.jsxs)("div", { className: "flex shrink-0 items-center gap-4 @min-[1140px]:gap-6", children: [rightLinks.length > 0 && ((0, jsx_runtime_1.jsx)("nav", { "aria-label": secondaryNavLabel, className: tier.block, children: (0, jsx_runtime_1.jsx)(NavLinks, { links: rightLinks, large: isLargeLinks }) })), (0, jsx_runtime_1.jsx)("div", { className: (0, cn_1.cn)(tier.row, "items-center"), children: actionsSection }), renderMenuToggle()] })] }), renderMenu()] }));
230
+ }