@smicolon/fasla-ui 0.5.1 → 0.7.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 (962) hide show
  1. package/README.md +42 -14
  2. package/dist/cli/index.js +2286 -195
  3. package/dist/index.cjs +50 -12
  4. package/dist/index.js +50 -12
  5. package/dist/motion/index.cjs +12 -0
  6. package/dist/motion/index.d.cts +7 -0
  7. package/dist/motion/index.d.ts +7 -0
  8. package/dist/motion/index.js +12 -0
  9. package/dist/registry/index.cjs +4 -4
  10. package/dist/registry/index.js +4 -4
  11. package/dist/tokens/index.cjs +34 -8
  12. package/dist/tokens/index.d.cts +23 -1
  13. package/dist/tokens/index.d.ts +23 -1
  14. package/dist/tokens/index.js +34 -8
  15. package/package.json +12 -3
  16. package/registry/blocks/about-approach-stack-cards/AboutApproachStackCards.tsx +305 -0
  17. package/registry/blocks/about-approach-stack-cards/index.ts +9 -0
  18. package/registry/blocks/about-bento/AboutBento.tsx +442 -0
  19. package/registry/blocks/about-bento/index.ts +9 -0
  20. package/registry/blocks/about-collage-hero/AboutCollageHero.tsx +285 -0
  21. package/registry/blocks/about-collage-hero/index.ts +8 -0
  22. package/registry/blocks/about-color-bento/AboutColorBento.tsx +235 -0
  23. package/registry/blocks/about-color-bento/index.ts +6 -0
  24. package/registry/blocks/about-culture-masonry/AboutCultureMasonry.tsx +305 -0
  25. package/registry/blocks/about-culture-masonry/index.ts +9 -0
  26. package/registry/blocks/about-headline-photos/AboutHeadlinePhotos.tsx +270 -0
  27. package/registry/blocks/about-headline-photos/index.ts +8 -0
  28. package/registry/blocks/about-impact-stats-band/AboutImpactStatsBand.test.tsx +45 -0
  29. package/registry/blocks/about-impact-stats-band/AboutImpactStatsBand.tsx +348 -0
  30. package/registry/blocks/about-impact-stats-band/index.ts +9 -0
  31. package/registry/blocks/about-letter-card/AboutLetterCard.tsx +403 -0
  32. package/registry/blocks/about-letter-card/index.ts +9 -0
  33. package/registry/blocks/about-mission-statement-hero/AboutMissionStatementHero.tsx +282 -0
  34. package/registry/blocks/about-mission-statement-hero/index.ts +8 -0
  35. package/registry/blocks/about-numbered-rows/AboutNumberedRows.tsx +289 -0
  36. package/registry/blocks/about-numbered-rows/index.ts +7 -0
  37. package/registry/blocks/about-split-panel/AboutSplitPanel.tsx +247 -0
  38. package/registry/blocks/about-split-panel/index.ts +8 -0
  39. package/registry/blocks/about-spotlight/AboutSpotlight.tsx +260 -0
  40. package/registry/blocks/about-spotlight/index.ts +7 -0
  41. package/registry/blocks/about-statement-band/AboutStatementBand.tsx +236 -0
  42. package/registry/blocks/about-statement-band/index.ts +7 -0
  43. package/registry/blocks/about-story-photos/AboutStoryPhotos.test.tsx +12 -0
  44. package/registry/blocks/about-story-photos/AboutStoryPhotos.tsx +252 -0
  45. package/registry/blocks/about-story-photos/index.ts +8 -0
  46. package/registry/blocks/about-story-snapshots/AboutStorySnapshots.tsx +239 -0
  47. package/registry/blocks/about-story-snapshots/index.ts +8 -0
  48. package/registry/blocks/about-type-stack/AboutTypeStack.tsx +205 -0
  49. package/registry/blocks/about-type-stack/index.ts +6 -0
  50. package/registry/blocks/about-values-connector-list/AboutValuesConnectorList.tsx +316 -0
  51. package/registry/blocks/about-values-connector-list/index.ts +7 -0
  52. package/registry/blocks/about-values-focus/AboutValuesFocus.tsx +303 -0
  53. package/registry/blocks/about-values-focus/index.ts +8 -0
  54. package/registry/blocks/about-values-spotlight/AboutValuesSpotlight.tsx +265 -0
  55. package/registry/blocks/about-values-spotlight/index.ts +8 -0
  56. package/registry/blocks/auth-centered-card-sign-in/AuthCenteredCardSignIn.tsx +373 -0
  57. package/registry/blocks/auth-centered-card-sign-in/index.ts +9 -0
  58. package/registry/blocks/auth-check-your-email/AuthCheckYourEmail.tsx +366 -0
  59. package/registry/blocks/auth-check-your-email/index.ts +7 -0
  60. package/registry/blocks/auth-forgot-password/AuthForgotPassword.tsx +265 -0
  61. package/registry/blocks/auth-forgot-password/index.ts +8 -0
  62. package/registry/blocks/auth-locked-session-re-authenticate/AuthLockedSessionReAuthenticate.tsx +305 -0
  63. package/registry/blocks/auth-locked-session-re-authenticate/index.ts +8 -0
  64. package/registry/blocks/auth-magic-link-request/AuthMagicLinkRequest.tsx +336 -0
  65. package/registry/blocks/auth-magic-link-request/index.ts +8 -0
  66. package/registry/blocks/auth-minimal-centered-sign-in/AuthMinimalCenteredSignIn.tsx +261 -0
  67. package/registry/blocks/auth-minimal-centered-sign-in/index.ts +8 -0
  68. package/registry/blocks/auth-multi-step-sign-up-wizard/AuthMultiStepSignUpWizard.tsx +460 -0
  69. package/registry/blocks/auth-multi-step-sign-up-wizard/index.ts +8 -0
  70. package/registry/blocks/auth-otp-verification/AuthOtpVerification.tsx +348 -0
  71. package/registry/blocks/auth-otp-verification/index.ts +7 -0
  72. package/registry/blocks/auth-passkey-sign-in/AuthPasskeySignIn.tsx +294 -0
  73. package/registry/blocks/auth-passkey-sign-in/index.ts +8 -0
  74. package/registry/blocks/auth-password-changed/AuthPasswordChanged.tsx +239 -0
  75. package/registry/blocks/auth-password-changed/index.ts +8 -0
  76. package/registry/blocks/auth-phone-number-sign-in/AuthPhoneNumberSignIn.tsx +402 -0
  77. package/registry/blocks/auth-phone-number-sign-in/index.ts +10 -0
  78. package/registry/blocks/auth-photo-background-sign-in/AuthPhotoBackgroundSignIn.tsx +398 -0
  79. package/registry/blocks/auth-photo-background-sign-in/index.ts +10 -0
  80. package/registry/blocks/auth-profile-setup-onboarding/AuthProfileSetupOnboarding.tsx +420 -0
  81. package/registry/blocks/auth-profile-setup-onboarding/index.ts +7 -0
  82. package/registry/blocks/auth-qr-device-login/AuthQrDeviceLogin.tsx +303 -0
  83. package/registry/blocks/auth-qr-device-login/index.ts +7 -0
  84. package/registry/blocks/auth-reset-password/AuthResetPassword.tsx +313 -0
  85. package/registry/blocks/auth-reset-password/index.ts +7 -0
  86. package/registry/blocks/auth-sign-in-error-state/AuthSignInErrorState.tsx +419 -0
  87. package/registry/blocks/auth-sign-in-error-state/index.ts +9 -0
  88. package/registry/blocks/auth-sign-up-benefits-column/AuthSignUpBenefitsColumn.tsx +481 -0
  89. package/registry/blocks/auth-sign-up-benefits-column/index.ts +10 -0
  90. package/registry/blocks/auth-sign-up-password-strength/AuthSignUpPasswordStrength.tsx +511 -0
  91. package/registry/blocks/auth-sign-up-password-strength/index.ts +8 -0
  92. package/registry/blocks/auth-social-first-sign-in/AuthSocialFirstSignIn.tsx +337 -0
  93. package/registry/blocks/auth-social-first-sign-in/index.ts +9 -0
  94. package/registry/blocks/auth-split-photo-sign-in/AuthSplitPhotoSignIn.tsx +472 -0
  95. package/registry/blocks/auth-split-photo-sign-in/index.ts +11 -0
  96. package/registry/blocks/auth-split-testimonial-sign-up/AuthSplitTestimonialSignUp.tsx +548 -0
  97. package/registry/blocks/auth-split-testimonial-sign-up/index.ts +11 -0
  98. package/registry/blocks/auth-tabbed-sign-in-sign-up/AuthTabbedSignInSignUp.tsx +566 -0
  99. package/registry/blocks/auth-tabbed-sign-in-sign-up/index.ts +11 -0
  100. package/registry/blocks/auth-team-invite-accept/AuthTeamInviteAccept.tsx +372 -0
  101. package/registry/blocks/auth-team-invite-accept/index.ts +9 -0
  102. package/registry/blocks/auth-two-factor-authenticator/AuthTwoFactorAuthenticator.tsx +333 -0
  103. package/registry/blocks/auth-two-factor-authenticator/index.ts +7 -0
  104. package/registry/blocks/auth-two-factor-method-chooser/AuthTwoFactorMethodChooser.tsx +290 -0
  105. package/registry/blocks/auth-two-factor-method-chooser/index.ts +8 -0
  106. package/registry/blocks/auth-waitlist-early-access/AuthWaitlistEarlyAccess.tsx +430 -0
  107. package/registry/blocks/auth-waitlist-early-access/index.ts +11 -0
  108. package/registry/blocks/blog-cards-alongside.test.tsx +176 -0
  109. package/registry/blocks/blog-cards-author-spotlight/BlogCardsAuthorSpotlight.tsx +342 -0
  110. package/registry/blocks/blog-cards-author-spotlight/index.ts +6 -0
  111. package/registry/blocks/blog-cards-bento-editorial-grid/BlogCardsBentoEditorialGrid.tsx +345 -0
  112. package/registry/blocks/blog-cards-bento-editorial-grid/index.ts +7 -0
  113. package/registry/blocks/blog-cards-browse.test.tsx +367 -0
  114. package/registry/blocks/blog-cards-classic-3-up-grid/BlogCardsClassic3UpGrid.tsx +287 -0
  115. package/registry/blocks/blog-cards-classic-3-up-grid/index.ts +7 -0
  116. package/registry/blocks/blog-cards-compact-4-up-thumb-grid/BlogCardsCompact4UpThumbGrid.tsx +265 -0
  117. package/registry/blocks/blog-cards-compact-4-up-thumb-grid/index.ts +7 -0
  118. package/registry/blocks/blog-cards-dark-editorial-feature/BlogCardsDarkEditorialFeature.tsx +287 -0
  119. package/registry/blocks/blog-cards-dark-editorial-feature/index.ts +6 -0
  120. package/registry/blocks/blog-cards-featured-two-up/BlogCardsFeaturedTwoUp.tsx +295 -0
  121. package/registry/blocks/blog-cards-featured-two-up/index.ts +7 -0
  122. package/registry/blocks/blog-cards-features.test.tsx +183 -0
  123. package/registry/blocks/blog-cards-filter-sidebar-results/BlogCardsFilterSidebarResults.tsx +582 -0
  124. package/registry/blocks/blog-cards-filter-sidebar-results/index.ts +6 -0
  125. package/registry/blocks/blog-cards-grids.test.tsx +110 -0
  126. package/registry/blocks/blog-cards-hairline-text-index/BlogCardsHairlineTextIndex.tsx +193 -0
  127. package/registry/blocks/blog-cards-hairline-text-index/index.ts +7 -0
  128. package/registry/blocks/blog-cards-journal-bento/BlogCardsJournalBento.tsx +409 -0
  129. package/registry/blocks/blog-cards-journal-bento/index.ts +8 -0
  130. package/registry/blocks/blog-cards-latest-popular-split/BlogCardsLatestPopularSplit.tsx +258 -0
  131. package/registry/blocks/blog-cards-latest-popular-split/index.ts +7 -0
  132. package/registry/blocks/blog-cards-lead-ranked-picks/BlogCardsLeadRankedPicks.tsx +276 -0
  133. package/registry/blocks/blog-cards-lead-ranked-picks/index.ts +6 -0
  134. package/registry/blocks/blog-cards-left-thumb-row-cards/BlogCardsLeftThumbRowCards.tsx +288 -0
  135. package/registry/blocks/blog-cards-left-thumb-row-cards/index.ts +7 -0
  136. package/registry/blocks/blog-cards-lists.test.tsx +93 -0
  137. package/registry/blocks/blog-cards-magazine-cover-story/BlogCardsMagazineCoverStory.tsx +326 -0
  138. package/registry/blocks/blog-cards-magazine-cover-story/index.ts +6 -0
  139. package/registry/blocks/blog-cards-newsletter-latest/BlogCardsNewsletterLatest.tsx +291 -0
  140. package/registry/blocks/blog-cards-newsletter-latest/index.ts +6 -0
  141. package/registry/blocks/blog-cards-numbered-reading-list/BlogCardsNumberedReadingList.tsx +275 -0
  142. package/registry/blocks/blog-cards-numbered-reading-list/index.ts +7 -0
  143. package/registry/blocks/blog-cards-overlay-photo-trio/BlogCardsOverlayPhotoTrio.tsx +279 -0
  144. package/registry/blocks/blog-cards-overlay-photo-trio/index.ts +7 -0
  145. package/registry/blocks/blog-cards-post-carousel-with-controls/BlogCardsPostCarouselWithControls.tsx +257 -0
  146. package/registry/blocks/blog-cards-post-carousel-with-controls/index.ts +6 -0
  147. package/registry/blocks/blog-cards-pull-quote-feature/BlogCardsPullQuoteFeature.tsx +245 -0
  148. package/registry/blocks/blog-cards-pull-quote-feature/index.ts +6 -0
  149. package/registry/blocks/blog-cards-related-posts-strip/BlogCardsRelatedPostsStrip.tsx +247 -0
  150. package/registry/blocks/blog-cards-related-posts-strip/index.ts +6 -0
  151. package/registry/blocks/blog-cards-search-chips-results/BlogCardsSearchChipsResults.tsx +473 -0
  152. package/registry/blocks/blog-cards-search-chips-results/index.ts +6 -0
  153. package/registry/blocks/blog-cards-split-feature/BlogCardsSplitFeature.tsx +214 -0
  154. package/registry/blocks/blog-cards-split-feature/index.ts +6 -0
  155. package/registry/blocks/careers/careers.test.tsx +414 -0
  156. package/registry/blocks/careers/careers.tsx +391 -0
  157. package/registry/blocks/careers/index.ts +27 -0
  158. package/registry/blocks/careers-about-card-role-list/CareersAboutCardRoleList.tsx +235 -0
  159. package/registry/blocks/careers-about-card-role-list/index.ts +8 -0
  160. package/registry/blocks/careers-featured-role-spotlight/CareersFeaturedRoleSpotlight.tsx +271 -0
  161. package/registry/blocks/careers-featured-role-spotlight/index.ts +8 -0
  162. package/registry/blocks/careers-no-open-roles-empty-state/CareersNoOpenRolesEmptyState.tsx +330 -0
  163. package/registry/blocks/careers-no-open-roles-empty-state/index.ts +8 -0
  164. package/registry/blocks/careers-open-roles-hairline-list/CareersOpenRolesHairlineList.tsx +320 -0
  165. package/registry/blocks/careers-open-roles-hairline-list/index.ts +6 -0
  166. package/registry/blocks/careers-open-vacancies-table/CareersOpenVacanciesTable.tsx +493 -0
  167. package/registry/blocks/careers-open-vacancies-table/index.ts +6 -0
  168. package/registry/blocks/careers-perks-benefits/CareersPerksBenefits.tsx +181 -0
  169. package/registry/blocks/careers-perks-benefits/index.ts +8 -0
  170. package/registry/blocks/careers-role-cards-grid/CareersRoleCardsGrid.tsx +405 -0
  171. package/registry/blocks/careers-role-cards-grid/index.ts +6 -0
  172. package/registry/blocks/careers-role-detail-page/CareersRoleDetailPage.tsx +541 -0
  173. package/registry/blocks/careers-role-detail-page/index.ts +10 -0
  174. package/registry/blocks/careers-role-detail-panel/CareersRoleDetailPanel.tsx +255 -0
  175. package/registry/blocks/careers-role-detail-panel/index.ts +6 -0
  176. package/registry/blocks/careers-roles-by-department/CareersRolesByDepartment.tsx +242 -0
  177. package/registry/blocks/careers-roles-by-department/index.ts +7 -0
  178. package/registry/blocks/careers-roles-list-accordion-expanded/CareersRolesListAccordionExpanded.tsx +558 -0
  179. package/registry/blocks/careers-roles-list-accordion-expanded/index.ts +9 -0
  180. package/registry/blocks/contact-us/contact-us.test.tsx +239 -0
  181. package/registry/blocks/contact-us/contact-us.tsx +989 -0
  182. package/registry/blocks/contact-us/index.ts +34 -0
  183. package/registry/blocks/contact-us-bento-grid/ContactUsBentoGrid.tsx +304 -0
  184. package/registry/blocks/contact-us-bento-grid/index.ts +5 -0
  185. package/registry/blocks/contact-us-bento-grid.test.tsx +102 -0
  186. package/registry/blocks/contact-us-book-a-call/ContactUsBookACall.tsx +403 -0
  187. package/registry/blocks/contact-us-book-a-call/index.ts +7 -0
  188. package/registry/blocks/contact-us-book-a-call.test.tsx +134 -0
  189. package/registry/blocks/contact-us-classic-centered/ContactUsClassicCentered.tsx +199 -0
  190. package/registry/blocks/contact-us-classic-centered/index.ts +6 -0
  191. package/registry/blocks/contact-us-dark-statement/ContactUsDarkStatement.tsx +218 -0
  192. package/registry/blocks/contact-us-dark-statement/index.ts +6 -0
  193. package/registry/blocks/contact-us-editorial-and-address-card/ContactUsEditorialAndAddressCard.tsx +212 -0
  194. package/registry/blocks/contact-us-editorial-and-address-card/index.ts +6 -0
  195. package/registry/blocks/contact-us-editorial-statement/ContactUsEditorialStatement.tsx +205 -0
  196. package/registry/blocks/contact-us-editorial-statement/index.ts +6 -0
  197. package/registry/blocks/contact-us-form-contact-methods/ContactUsFormContactMethods.tsx +272 -0
  198. package/registry/blocks/contact-us-form-contact-methods/index.ts +6 -0
  199. package/registry/blocks/contact-us-form-with-faq/ContactUsFormWithFaq.tsx +383 -0
  200. package/registry/blocks/contact-us-form-with-faq/index.ts +7 -0
  201. package/registry/blocks/contact-us-form-with-faq.test.tsx +161 -0
  202. package/registry/blocks/contact-us-forms.test.tsx +333 -0
  203. package/registry/blocks/contact-us-global-offices-table/ContactUsGlobalOfficesTable.tsx +301 -0
  204. package/registry/blocks/contact-us-global-offices-table/index.ts +5 -0
  205. package/registry/blocks/contact-us-global-offices-table.test.tsx +100 -0
  206. package/registry/blocks/contact-us-grand-headline-form/ContactUsGrandHeadlineForm.tsx +180 -0
  207. package/registry/blocks/contact-us-grand-headline-form/index.ts +6 -0
  208. package/registry/blocks/contact-us-hours-and-availability/ContactUsHoursAndAvailability.tsx +412 -0
  209. package/registry/blocks/contact-us-hours-and-availability/index.ts +5 -0
  210. package/registry/blocks/contact-us-hours-and-availability.test.tsx +123 -0
  211. package/registry/blocks/contact-us-hq-feature/ContactUsHqFeature.tsx +281 -0
  212. package/registry/blocks/contact-us-hq-feature/index.ts +5 -0
  213. package/registry/blocks/contact-us-hq-feature.test.tsx +102 -0
  214. package/registry/blocks/contact-us-map-split/ContactUsMapSplit.tsx +211 -0
  215. package/registry/blocks/contact-us-map-split/index.ts +5 -0
  216. package/registry/blocks/contact-us-map-split.test.tsx +70 -0
  217. package/registry/blocks/contact-us-named-owners/ContactUsNamedOwners.tsx +324 -0
  218. package/registry/blocks/contact-us-named-owners/index.ts +6 -0
  219. package/registry/blocks/contact-us-named-owners.test.tsx +116 -0
  220. package/registry/blocks/contact-us-newsletter-combo/ContactUsNewsletterCombo.tsx +363 -0
  221. package/registry/blocks/contact-us-newsletter-combo/index.ts +6 -0
  222. package/registry/blocks/contact-us-newsletter-combo.test.tsx +165 -0
  223. package/registry/blocks/contact-us-office-cards/ContactUsOfficeCards.tsx +235 -0
  224. package/registry/blocks/contact-us-office-cards/index.ts +5 -0
  225. package/registry/blocks/contact-us-office-cards.test.tsx +71 -0
  226. package/registry/blocks/contact-us-oversized-wordmark/ContactUsOversizedWordmark.tsx +222 -0
  227. package/registry/blocks/contact-us-oversized-wordmark/index.ts +6 -0
  228. package/registry/blocks/contact-us-photo-and-dark-panel/ContactUsPhotoAndDarkPanel.tsx +185 -0
  229. package/registry/blocks/contact-us-photo-and-dark-panel/index.ts +6 -0
  230. package/registry/blocks/contact-us-photo-forms.test.tsx +311 -0
  231. package/registry/blocks/contact-us-pitch-and-inquiry/ContactUsPitchAndInquiry.tsx +202 -0
  232. package/registry/blocks/contact-us-pitch-and-inquiry/index.ts +6 -0
  233. package/registry/blocks/contact-us-rtl.test.tsx +105 -0
  234. package/registry/blocks/contact-us-ruled-split-form/ContactUsRuledSplitForm.tsx +214 -0
  235. package/registry/blocks/contact-us-ruled-split-form/index.ts +6 -0
  236. package/registry/blocks/contact-us-social-rail/ContactUsSocialRail.tsx +270 -0
  237. package/registry/blocks/contact-us-social-rail/index.ts +7 -0
  238. package/registry/blocks/contact-us-social-rail.test.tsx +121 -0
  239. package/registry/blocks/contact-us-split-photo-dark/ContactUsSplitPhotoDark.tsx +185 -0
  240. package/registry/blocks/contact-us-split-photo-dark/index.ts +6 -0
  241. package/registry/blocks/contact-us-support-channels/ContactUsSupportChannels.tsx +267 -0
  242. package/registry/blocks/contact-us-support-channels/index.ts +5 -0
  243. package/registry/blocks/contact-us-support-channels.test.tsx +81 -0
  244. package/registry/blocks/data-table/DataTable.test.tsx +1 -34
  245. package/registry/blocks/data-table/DataTable.tsx +0 -139
  246. package/registry/blocks/data-table/index.ts +1 -8
  247. package/registry/blocks/error-state-access-denied-403/ErrorStateAccessDenied403.tsx +324 -0
  248. package/registry/blocks/error-state-access-denied-403/index.ts +10 -0
  249. package/registry/blocks/error-state-broken-trail-breadcrumb/ErrorStateBrokenTrailBreadcrumb.tsx +179 -0
  250. package/registry/blocks/error-state-broken-trail-breadcrumb/index.ts +8 -0
  251. package/registry/blocks/error-state-centered-minimal/ErrorStateCenteredMinimal.tsx +119 -0
  252. package/registry/blocks/error-state-centered-minimal/index.ts +7 -0
  253. package/registry/blocks/error-state-colour-field-scatter/ErrorStateColourFieldScatter.tsx +144 -0
  254. package/registry/blocks/error-state-colour-field-scatter/index.ts +7 -0
  255. package/registry/blocks/error-state-countdown-redirect/ErrorStateCountdownRedirect.tsx +272 -0
  256. package/registry/blocks/error-state-countdown-redirect/index.ts +7 -0
  257. package/registry/blocks/error-state-dark-spotlight/ErrorStateDarkSpotlight.tsx +156 -0
  258. package/registry/blocks/error-state-dark-spotlight/index.ts +7 -0
  259. package/registry/blocks/error-state-display-numeral-split/ErrorStateDisplayNumeralSplit.tsx +156 -0
  260. package/registry/blocks/error-state-display-numeral-split/index.ts +8 -0
  261. package/registry/blocks/error-state-editorial-photo-split/ErrorStateEditorialPhotoSplit.tsx +156 -0
  262. package/registry/blocks/error-state-editorial-photo-split/index.ts +8 -0
  263. package/registry/blocks/error-state-empty-search-results/ErrorStateEmptySearchResults.tsx +243 -0
  264. package/registry/blocks/error-state-empty-search-results/index.ts +6 -0
  265. package/registry/blocks/error-state-error-card-panel/ErrorStateErrorCardPanel.tsx +213 -0
  266. package/registry/blocks/error-state-error-card-panel/index.ts +7 -0
  267. package/registry/blocks/error-state-full-bleed-photo/ErrorStateFullBleedPhoto.tsx +130 -0
  268. package/registry/blocks/error-state-full-bleed-photo/index.ts +8 -0
  269. package/registry/blocks/error-state-ghost-watermark/ErrorStateGhostWatermark.tsx +115 -0
  270. package/registry/blocks/error-state-ghost-watermark/index.ts +7 -0
  271. package/registry/blocks/error-state-helpful-links-list/ErrorStateHelpfulLinksList.tsx +185 -0
  272. package/registry/blocks/error-state-helpful-links-list/index.ts +8 -0
  273. package/registry/blocks/error-state-illustrated-empty-state/ErrorStateIllustratedEmptyState.tsx +201 -0
  274. package/registry/blocks/error-state-illustrated-empty-state/index.ts +7 -0
  275. package/registry/blocks/error-state-layered-badge-centered/ErrorStateLayeredBadgeCentered.tsx +138 -0
  276. package/registry/blocks/error-state-layered-badge-centered/index.ts +7 -0
  277. package/registry/blocks/error-state-light-editorial/ErrorStateLightEditorial.tsx +126 -0
  278. package/registry/blocks/error-state-light-editorial/index.ts +7 -0
  279. package/registry/blocks/error-state-maintenance-mode/ErrorStateMaintenanceMode.tsx +255 -0
  280. package/registry/blocks/error-state-maintenance-mode/index.ts +6 -0
  281. package/registry/blocks/error-state-marquee-404-band/ErrorStateMarquee404Band.tsx +316 -0
  282. package/registry/blocks/error-state-marquee-404-band/index.ts +7 -0
  283. package/registry/blocks/error-state-offline-state/ErrorStateOfflineState.tsx +188 -0
  284. package/registry/blocks/error-state-offline-state/index.ts +7 -0
  285. package/registry/blocks/error-state-photo-collage-scatter/ErrorStatePhotoCollageScatter.tsx +224 -0
  286. package/registry/blocks/error-state-photo-collage-scatter/index.ts +8 -0
  287. package/registry/blocks/error-state-popular-pages-grid/ErrorStatePopularPagesGrid.tsx +195 -0
  288. package/registry/blocks/error-state-popular-pages-grid/index.ts +9 -0
  289. package/registry/blocks/error-state-recovery-bento/ErrorStateRecoveryBento.tsx +274 -0
  290. package/registry/blocks/error-state-recovery-bento/index.ts +7 -0
  291. package/registry/blocks/error-state-search-recovery/ErrorStateSearchRecovery.tsx +173 -0
  292. package/registry/blocks/error-state-search-recovery/index.ts +7 -0
  293. package/registry/blocks/error-state-server-error-500/ErrorStateServerError500.tsx +277 -0
  294. package/registry/blocks/error-state-server-error-500/index.ts +9 -0
  295. package/registry/blocks/error-state-sitemap-columns/ErrorStateSitemapColumns.tsx +192 -0
  296. package/registry/blocks/error-state-sitemap-columns/index.ts +9 -0
  297. package/registry/blocks/error-state-split-colour-panel/ErrorStateSplitColourPanel.tsx +156 -0
  298. package/registry/blocks/error-state-split-colour-panel/index.ts +7 -0
  299. package/registry/blocks/error-state-support-escalation-band/ErrorStateSupportEscalationBand.tsx +186 -0
  300. package/registry/blocks/error-state-support-escalation-band/index.ts +8 -0
  301. package/registry/blocks/error-state-terminal-console/ErrorStateTerminalConsole.tsx +208 -0
  302. package/registry/blocks/error-state-terminal-console/index.ts +8 -0
  303. package/registry/blocks/faq-accent-active-card/FaqAccentActiveCard.tsx +186 -0
  304. package/registry/blocks/faq-accent-active-card/index.ts +1 -0
  305. package/registry/blocks/faq-category-chip-grid/FaqCategoryChipGrid.tsx +425 -0
  306. package/registry/blocks/faq-category-chip-grid/index.ts +8 -0
  307. package/registry/blocks/faq-category-columns/FaqCategoryColumns.tsx +248 -0
  308. package/registry/blocks/faq-category-columns/index.ts +8 -0
  309. package/registry/blocks/faq-chat-thread/FaqChatThread.tsx +273 -0
  310. package/registry/blocks/faq-chat-thread/index.ts +7 -0
  311. package/registry/blocks/faq-classic-centered/FaqClassicCentered.tsx +190 -0
  312. package/registry/blocks/faq-classic-centered/index.ts +7 -0
  313. package/registry/blocks/faq-contact-cta-card/FaqContactCtaCard.tsx +229 -0
  314. package/registry/blocks/faq-contact-cta-card/index.ts +9 -0
  315. package/registry/blocks/faq-dark-panel/FaqDarkPanel.tsx +186 -0
  316. package/registry/blocks/faq-dark-panel/index.ts +1 -0
  317. package/registry/blocks/faq-featured-question/FaqFeaturedQuestion.tsx +221 -0
  318. package/registry/blocks/faq-featured-question/index.ts +1 -0
  319. package/registry/blocks/faq-grouped-sections/FaqGroupedSections.tsx +266 -0
  320. package/registry/blocks/faq-grouped-sections/index.ts +8 -0
  321. package/registry/blocks/faq-help-hero-popular/FaqHelpHeroPopular.tsx +212 -0
  322. package/registry/blocks/faq-help-hero-popular/index.ts +7 -0
  323. package/registry/blocks/faq-masonry-cards/FaqMasonryCards.tsx +268 -0
  324. package/registry/blocks/faq-masonry-cards/index.ts +7 -0
  325. package/registry/blocks/faq-media-split/FaqMediaSplit.tsx +197 -0
  326. package/registry/blocks/faq-media-split/index.ts +8 -0
  327. package/registry/blocks/faq-mono-index/FaqMonoIndex.tsx +247 -0
  328. package/registry/blocks/faq-mono-index/index.ts +1 -0
  329. package/registry/blocks/faq-newsletter-close/FaqNewsletterClose.tsx +314 -0
  330. package/registry/blocks/faq-newsletter-close/index.ts +8 -0
  331. package/registry/blocks/faq-numbered-editorial/FaqNumberedEditorial.tsx +190 -0
  332. package/registry/blocks/faq-numbered-editorial/index.ts +1 -0
  333. package/registry/blocks/faq-search-categories/FaqSearchCategories.tsx +312 -0
  334. package/registry/blocks/faq-search-categories/index.ts +8 -0
  335. package/registry/blocks/faq-sidebar-nav/FaqSidebarNav.tsx +390 -0
  336. package/registry/blocks/faq-sidebar-nav/index.ts +8 -0
  337. package/registry/blocks/faq-soft-filled-rows/FaqSoftFilledRows.tsx +209 -0
  338. package/registry/blocks/faq-soft-filled-rows/index.ts +1 -0
  339. package/registry/blocks/faq-split-sticky/FaqSplitSticky.tsx +205 -0
  340. package/registry/blocks/faq-split-sticky/index.ts +7 -0
  341. package/registry/blocks/faq-support-channels/FaqSupportChannels.tsx +266 -0
  342. package/registry/blocks/faq-support-channels/index.ts +8 -0
  343. package/registry/blocks/faq-support-stats/FaqSupportStats.tsx +242 -0
  344. package/registry/blocks/faq-support-stats/index.ts +8 -0
  345. package/registry/blocks/faq-table-compact/FaqTableCompact.tsx +220 -0
  346. package/registry/blocks/faq-table-compact/index.ts +1 -0
  347. package/registry/blocks/faq-tabs-by-topic/FaqTabsByTopic.tsx +347 -0
  348. package/registry/blocks/faq-tabs-by-topic/index.ts +8 -0
  349. package/registry/blocks/faq-topic-tiles/FaqTopicTiles.tsx +254 -0
  350. package/registry/blocks/faq-topic-tiles/index.ts +7 -0
  351. package/registry/blocks/faq-two-column-grid/FaqTwoColumnGrid.tsx +256 -0
  352. package/registry/blocks/faq-two-column-grid/index.ts +7 -0
  353. package/registry/blocks/faq-uppercase-dense/FaqUppercaseDense.tsx +213 -0
  354. package/registry/blocks/faq-uppercase-dense/index.ts +1 -0
  355. package/registry/blocks/features/features.tsx +164 -0
  356. package/registry/blocks/features/index.ts +12 -0
  357. package/registry/blocks/features-accordion-photo/FeaturesAccordionPhoto.tsx +149 -0
  358. package/registry/blocks/features-accordion-photo/index.ts +7 -0
  359. package/registry/blocks/features-colour-block-cards/FeaturesColourBlockCards.tsx +118 -0
  360. package/registry/blocks/features-colour-block-cards/index.ts +8 -0
  361. package/registry/blocks/features-compact-icon-index/FeaturesCompactIconIndex.tsx +136 -0
  362. package/registry/blocks/features-compact-icon-index/index.ts +7 -0
  363. package/registry/blocks/features-compare-panels-chat-answer/FeaturesComparePanelsChatAnswer.tsx +346 -0
  364. package/registry/blocks/features-compare-panels-chat-answer/index.ts +8 -0
  365. package/registry/blocks/features-editorial-photo-split/FeaturesEditorialPhotoSplit.tsx +118 -0
  366. package/registry/blocks/features-editorial-photo-split/index.ts +7 -0
  367. package/registry/blocks/features-expanding-step-cards/FeaturesExpandingStepCards.tsx +300 -0
  368. package/registry/blocks/features-expanding-step-cards/index.ts +7 -0
  369. package/registry/blocks/features-feature-checklist-columns/FeaturesFeatureChecklistColumns.tsx +158 -0
  370. package/registry/blocks/features-feature-checklist-columns/index.ts +7 -0
  371. package/registry/blocks/features-feature-list-thumbnails/FeaturesFeatureListThumbnails.tsx +137 -0
  372. package/registry/blocks/features-feature-list-thumbnails/index.ts +7 -0
  373. package/registry/blocks/features-full-bleed-photo-banner/FeaturesFullBleedPhotoBanner.tsx +116 -0
  374. package/registry/blocks/features-full-bleed-photo-banner/index.ts +7 -0
  375. package/registry/blocks/features-icon-card-grid/FeaturesIconCardGrid.tsx +120 -0
  376. package/registry/blocks/features-icon-card-grid/index.ts +7 -0
  377. package/registry/blocks/features-icon-titled-preview-rail/FeaturesIconTitledPreviewRail.tsx +321 -0
  378. package/registry/blocks/features-icon-titled-preview-rail/index.ts +7 -0
  379. package/registry/blocks/features-icons-numbers.test.tsx +107 -0
  380. package/registry/blocks/features-interactive.test.tsx +175 -0
  381. package/registry/blocks/features-media-rail/FeaturesMediaRail.tsx +119 -0
  382. package/registry/blocks/features-media-rail/index.ts +7 -0
  383. package/registry/blocks/features-notched-photo-columns/FeaturesNotchedPhotoColumns.tsx +209 -0
  384. package/registry/blocks/features-notched-photo-columns/index.ts +7 -0
  385. package/registry/blocks/features-numbered-feature-rows/FeaturesNumberedFeatureRows.tsx +107 -0
  386. package/registry/blocks/features-numbered-feature-rows/index.ts +7 -0
  387. package/registry/blocks/features-numbered-steps-floating-cards/FeaturesNumberedStepsFloatingCards.tsx +324 -0
  388. package/registry/blocks/features-numbered-steps-floating-cards/index.ts +8 -0
  389. package/registry/blocks/features-perforated-photo-strip/FeaturesPerforatedPhotoStrip.tsx +218 -0
  390. package/registry/blocks/features-perforated-photo-strip/index.ts +7 -0
  391. package/registry/blocks/features-photo-bento/FeaturesPhotoBento.tsx +160 -0
  392. package/registry/blocks/features-photo-bento/index.ts +8 -0
  393. package/registry/blocks/features-photo-carousel/FeaturesPhotoCarousel.tsx +249 -0
  394. package/registry/blocks/features-photo-carousel/index.ts +7 -0
  395. package/registry/blocks/features-photo-collage/FeaturesPhotoCollage.tsx +143 -0
  396. package/registry/blocks/features-photo-collage/index.ts +7 -0
  397. package/registry/blocks/features-photo-feature-chips/FeaturesPhotoFeatureChips.tsx +125 -0
  398. package/registry/blocks/features-photo-feature-chips/index.ts +7 -0
  399. package/registry/blocks/features-photo-led.test.tsx +177 -0
  400. package/registry/blocks/features-photo-tabs-panel/FeaturesPhotoTabsPanel.tsx +197 -0
  401. package/registry/blocks/features-photo-tabs-panel/index.ts +7 -0
  402. package/registry/blocks/features-portrait-photo-cards-link-row/FeaturesPortraitPhotoCardsLinkRow.tsx +202 -0
  403. package/registry/blocks/features-portrait-photo-cards-link-row/index.ts +7 -0
  404. package/registry/blocks/features-product-in-use.test.tsx +188 -0
  405. package/registry/blocks/features-showcase-card-pair/FeaturesShowcaseCardPair.tsx +161 -0
  406. package/registry/blocks/features-showcase-card-pair/index.ts +7 -0
  407. package/registry/blocks/features-solid-colour-cards/FeaturesSolidColourCards.tsx +264 -0
  408. package/registry/blocks/features-solid-colour-cards/index.ts +7 -0
  409. package/registry/blocks/features-split-photo-icon-list/FeaturesSplitPhotoIconList.tsx +117 -0
  410. package/registry/blocks/features-split-photo-icon-list/index.ts +7 -0
  411. package/registry/blocks/features-stat-feature-band/FeaturesStatFeatureBand.tsx +114 -0
  412. package/registry/blocks/features-stat-feature-band/index.ts +7 -0
  413. package/registry/blocks/features-tinted-product-bento/FeaturesTintedProductBento.tsx +252 -0
  414. package/registry/blocks/features-tinted-product-bento/index.ts +7 -0
  415. package/registry/blocks/footer/footer.test.tsx +253 -0
  416. package/registry/blocks/footer/footer.tsx +554 -0
  417. package/registry/blocks/footer/index.ts +31 -0
  418. package/registry/blocks/footer-accordion-sitemap/FooterAccordionSitemap.tsx +260 -0
  419. package/registry/blocks/footer-accordion-sitemap/index.ts +7 -0
  420. package/registry/blocks/footer-bars.test.tsx +121 -0
  421. package/registry/blocks/footer-bento-tile/FooterBentoTile.tsx +331 -0
  422. package/registry/blocks/footer-bento-tile/index.ts +6 -0
  423. package/registry/blocks/footer-big-question-cta/FooterBigQuestionCta.tsx +250 -0
  424. package/registry/blocks/footer-big-question-cta/index.ts +6 -0
  425. package/registry/blocks/footer-big-type-link-stack/FooterBigTypeLinkStack.tsx +275 -0
  426. package/registry/blocks/footer-big-type-link-stack/index.ts +6 -0
  427. package/registry/blocks/footer-brand-panel-split/FooterBrandPanelSplit.tsx +163 -0
  428. package/registry/blocks/footer-brand-panel-split/index.ts +6 -0
  429. package/registry/blocks/footer-brand.test.tsx +608 -0
  430. package/registry/blocks/footer-careers-culture/FooterCareersCulture.tsx +297 -0
  431. package/registry/blocks/footer-careers-culture/index.ts +7 -0
  432. package/registry/blocks/footer-centered-brand-minimal/FooterCenteredBrandMinimal.tsx +121 -0
  433. package/registry/blocks/footer-centered-brand-minimal/index.ts +6 -0
  434. package/registry/blocks/footer-commerce.test.tsx +208 -0
  435. package/registry/blocks/footer-community-events/FooterCommunityEvents.tsx +263 -0
  436. package/registry/blocks/footer-community-events/index.ts +8 -0
  437. package/registry/blocks/footer-community.test.tsx +309 -0
  438. package/registry/blocks/footer-contact-first/FooterContactFirst.tsx +242 -0
  439. package/registry/blocks/footer-contact-first/index.ts +7 -0
  440. package/registry/blocks/footer-contact.test.tsx +285 -0
  441. package/registry/blocks/footer-controls.test.tsx +78 -0
  442. package/registry/blocks/footer-cta-band-columns/FooterCtaBandColumns.tsx +225 -0
  443. package/registry/blocks/footer-cta-band-columns/index.ts +6 -0
  444. package/registry/blocks/footer-cta.test.tsx +445 -0
  445. package/registry/blocks/footer-dashboard-slim-bar/FooterDashboardSlimBar.tsx +148 -0
  446. package/registry/blocks/footer-dashboard-slim-bar/index.ts +6 -0
  447. package/registry/blocks/footer-docs-page/FooterDocsPage.tsx +261 -0
  448. package/registry/blocks/footer-docs-page/index.ts +9 -0
  449. package/registry/blocks/footer-ecommerce-mega/FooterEcommerceMega.tsx +289 -0
  450. package/registry/blocks/footer-ecommerce-mega/index.ts +6 -0
  451. package/registry/blocks/footer-floating-demo-card/FooterFloatingDemoCard.tsx +261 -0
  452. package/registry/blocks/footer-floating-demo-card/index.ts +6 -0
  453. package/registry/blocks/footer-four-column-directory/FooterFourColumnDirectory.tsx +104 -0
  454. package/registry/blocks/footer-four-column-directory/index.ts +6 -0
  455. package/registry/blocks/footer-giant-wordmark/FooterGiantWordmark.tsx +252 -0
  456. package/registry/blocks/footer-giant-wordmark/index.ts +6 -0
  457. package/registry/blocks/footer-image-backed-brand/FooterImageBackedBrand.tsx +251 -0
  458. package/registry/blocks/footer-image-backed-brand/index.ts +6 -0
  459. package/registry/blocks/footer-index-numbered-columns/FooterIndexNumberedColumns.tsx +236 -0
  460. package/registry/blocks/footer-index-numbered-columns/index.ts +7 -0
  461. package/registry/blocks/footer-inline-preferences/FooterInlinePreferences.tsx +164 -0
  462. package/registry/blocks/footer-inline-preferences/index.ts +7 -0
  463. package/registry/blocks/footer-legal-first-slim/FooterLegalFirstSlim.tsx +174 -0
  464. package/registry/blocks/footer-legal-first-slim/index.ts +6 -0
  465. package/registry/blocks/footer-legal.test.tsx +180 -0
  466. package/registry/blocks/footer-local-business/FooterLocalBusiness.tsx +258 -0
  467. package/registry/blocks/footer-local-business/index.ts +7 -0
  468. package/registry/blocks/footer-marquee-strip/FooterMarqueeStrip.tsx +244 -0
  469. package/registry/blocks/footer-marquee-strip/index.ts +6 -0
  470. package/registry/blocks/footer-multi-region/FooterMultiRegion.tsx +310 -0
  471. package/registry/blocks/footer-multi-region/index.ts +7 -0
  472. package/registry/blocks/footer-newsletter-split/FooterNewsletterSplit.tsx +255 -0
  473. package/registry/blocks/footer-newsletter-split/index.ts +6 -0
  474. package/registry/blocks/footer-product-family-switcher/FooterProductFamilySwitcher.tsx +241 -0
  475. package/registry/blocks/footer-product-family-switcher/index.ts +7 -0
  476. package/registry/blocks/footer-service-promise-band/FooterServicePromiseBand.tsx +237 -0
  477. package/registry/blocks/footer-service-promise-band/index.ts +7 -0
  478. package/registry/blocks/footer-single-line-bar/FooterSingleLineBar.tsx +107 -0
  479. package/registry/blocks/footer-single-line-bar/index.ts +6 -0
  480. package/registry/blocks/footer-sitemap.test.tsx +390 -0
  481. package/registry/blocks/footer-split-hairline-micro/FooterSplitHairlineMicro.tsx +100 -0
  482. package/registry/blocks/footer-split-hairline-micro/index.ts +6 -0
  483. package/registry/blocks/footer-support-contact/FooterSupportContact.tsx +204 -0
  484. package/registry/blocks/footer-support-contact/index.ts +7 -0
  485. package/registry/blocks/footer-trust-compliance/FooterTrustCompliance.tsx +234 -0
  486. package/registry/blocks/footer-trust-compliance/index.ts +7 -0
  487. package/registry/blocks/footer-two-row-compact/FooterTwoRowCompact.tsx +124 -0
  488. package/registry/blocks/footer-two-row-compact/index.ts +6 -0
  489. package/registry/blocks/footer-two-tier-doormat/FooterTwoTierDoormat.tsx +232 -0
  490. package/registry/blocks/footer-two-tier-doormat/index.ts +8 -0
  491. package/registry/blocks/footer-utility.test.tsx +406 -0
  492. package/registry/blocks/footer-wide-sitemap-mega/FooterWideSitemapMega.tsx +227 -0
  493. package/registry/blocks/footer-wide-sitemap-mega/index.ts +6 -0
  494. package/registry/blocks/gallery-album-cards/GalleryAlbumCards.tsx +243 -0
  495. package/registry/blocks/gallery-album-cards/index.ts +8 -0
  496. package/registry/blocks/gallery-before-after-compare/GalleryBeforeAfterCompare.tsx +269 -0
  497. package/registry/blocks/gallery-before-after-compare/index.ts +7 -0
  498. package/registry/blocks/gallery-contact-sheet-wall/GalleryContactSheetWall.tsx +299 -0
  499. package/registry/blocks/gallery-contact-sheet-wall/index.ts +9 -0
  500. package/registry/blocks/gallery-culture-photo-grid/GalleryCulturePhotoGrid.tsx +203 -0
  501. package/registry/blocks/gallery-culture-photo-grid/index.ts +8 -0
  502. package/registry/blocks/gallery-dark-cinema-wall/GalleryDarkCinemaWall.tsx +180 -0
  503. package/registry/blocks/gallery-dark-cinema-wall/index.ts +7 -0
  504. package/registry/blocks/gallery-editorial-split/GalleryEditorialSplit.tsx +188 -0
  505. package/registry/blocks/gallery-editorial-split/index.ts +8 -0
  506. package/registry/blocks/gallery-featured-thumb-rail/GalleryFeaturedThumbRail.tsx +397 -0
  507. package/registry/blocks/gallery-featured-thumb-rail/index.ts +8 -0
  508. package/registry/blocks/gallery-filmstrip-carousel/GalleryFilmstripCarousel.tsx +384 -0
  509. package/registry/blocks/gallery-filmstrip-carousel/index.ts +8 -0
  510. package/registry/blocks/gallery-filter-tabs-grid/GalleryFilterTabsGrid.tsx +403 -0
  511. package/registry/blocks/gallery-filter-tabs-grid/index.ts +9 -0
  512. package/registry/blocks/gallery-full-bleed-slider/GalleryFullBleedSlider.tsx +309 -0
  513. package/registry/blocks/gallery-full-bleed-slider/index.ts +8 -0
  514. package/registry/blocks/gallery-horizontal-scroll-rail/GalleryHorizontalScrollRail.tsx +300 -0
  515. package/registry/blocks/gallery-horizontal-scroll-rail/index.ts +8 -0
  516. package/registry/blocks/gallery-hover-caption-cards/GalleryHoverCaptionCards.tsx +244 -0
  517. package/registry/blocks/gallery-hover-caption-cards/index.ts +8 -0
  518. package/registry/blocks/gallery-hover-index-list/GalleryHoverIndexList.tsx +253 -0
  519. package/registry/blocks/gallery-hover-index-list/index.ts +9 -0
  520. package/registry/blocks/gallery-lightbox-viewer/GalleryLightboxViewer.tsx +329 -0
  521. package/registry/blocks/gallery-lightbox-viewer/index.ts +7 -0
  522. package/registry/blocks/gallery-masonry-gallery/GalleryMasonryGallery.tsx +251 -0
  523. package/registry/blocks/gallery-masonry-gallery/index.ts +7 -0
  524. package/registry/blocks/gallery-mosaic-statement-tile/GalleryMosaicStatementTile.tsx +159 -0
  525. package/registry/blocks/gallery-mosaic-statement-tile/index.ts +8 -0
  526. package/registry/blocks/gallery-photo-bento/GalleryPhotoBento.tsx +166 -0
  527. package/registry/blocks/gallery-photo-bento/index.ts +8 -0
  528. package/registry/blocks/gallery-photo-spotlight/GalleryPhotoSpotlight.tsx +305 -0
  529. package/registry/blocks/gallery-photo-spotlight/index.ts +9 -0
  530. package/registry/blocks/gallery-scattered-collage/GalleryScatteredCollage.tsx +286 -0
  531. package/registry/blocks/gallery-scattered-collage/index.ts +9 -0
  532. package/registry/blocks/gallery-social-feed-strip/GallerySocialFeedStrip.tsx +288 -0
  533. package/registry/blocks/gallery-social-feed-strip/index.ts +8 -0
  534. package/registry/blocks/gallery-timeline-gallery/GalleryTimelineGallery.tsx +243 -0
  535. package/registry/blocks/gallery-timeline-gallery/index.ts +8 -0
  536. package/registry/blocks/gallery-video-gallery/GalleryVideoGallery.tsx +362 -0
  537. package/registry/blocks/gallery-video-gallery/index.ts +8 -0
  538. package/registry/blocks/hero-annotated-object/HeroAnnotatedObject.tsx +310 -0
  539. package/registry/blocks/hero-annotated-object/index.ts +10 -0
  540. package/registry/blocks/hero-arc-gallery/HeroArcGallery.tsx +244 -0
  541. package/registry/blocks/hero-arc-gallery/index.ts +9 -0
  542. package/registry/blocks/hero-bento-split/HeroBentoSplit.tsx +266 -0
  543. package/registry/blocks/hero-bento-split/index.ts +9 -0
  544. package/registry/blocks/hero-brand-color-block/HeroBrandColorBlock.tsx +259 -0
  545. package/registry/blocks/hero-brand-color-block/index.ts +8 -0
  546. package/registry/blocks/hero-colour-stage/HeroColourStage.tsx +292 -0
  547. package/registry/blocks/hero-colour-stage/index.ts +8 -0
  548. package/registry/blocks/hero-component-pattern/HeroComponentPattern.tsx +248 -0
  549. package/registry/blocks/hero-component-pattern/index.ts +9 -0
  550. package/registry/blocks/hero-curved-media-rail/HeroCurvedMediaRail.tsx +272 -0
  551. package/registry/blocks/hero-curved-media-rail/index.ts +8 -0
  552. package/registry/blocks/hero-duo-card/HeroDuoCard.tsx +210 -0
  553. package/registry/blocks/hero-duo-card/index.ts +8 -0
  554. package/registry/blocks/hero-editorial-arch/HeroEditorialArch.tsx +226 -0
  555. package/registry/blocks/hero-editorial-arch/index.ts +10 -0
  556. package/registry/blocks/hero-editorial-triptych/HeroEditorialTriptych.tsx +252 -0
  557. package/registry/blocks/hero-editorial-triptych/index.ts +9 -0
  558. package/registry/blocks/hero-floating-integrations/HeroFloatingIntegrations.tsx +454 -0
  559. package/registry/blocks/hero-floating-integrations/index.ts +11 -0
  560. package/registry/blocks/hero-framed-full-bleed/HeroFramedFullBleed.tsx +226 -0
  561. package/registry/blocks/hero-framed-full-bleed/index.ts +9 -0
  562. package/registry/blocks/hero-framed-studio-card/HeroFramedStudioCard.tsx +264 -0
  563. package/registry/blocks/hero-framed-studio-card/index.ts +8 -0
  564. package/registry/blocks/hero-illustration-split/HeroIllustrationSplit.tsx +200 -0
  565. package/registry/blocks/hero-illustration-split/index.ts +8 -0
  566. package/registry/blocks/hero-inline-image-headline/HeroInlineImageHeadline.tsx +305 -0
  567. package/registry/blocks/hero-inline-image-headline/index.ts +8 -0
  568. package/registry/blocks/hero-masked-type-reveal/HeroMaskedTypeReveal.tsx +323 -0
  569. package/registry/blocks/hero-masked-type-reveal/index.ts +9 -0
  570. package/registry/blocks/hero-motion.test.tsx +185 -0
  571. package/registry/blocks/hero-orbit-track/HeroOrbitTrack.tsx +238 -0
  572. package/registry/blocks/hero-orbit-track/index.ts +8 -0
  573. package/registry/blocks/hero-photo-constellation/HeroPhotoConstellation.tsx +228 -0
  574. package/registry/blocks/hero-photo-constellation/index.ts +8 -0
  575. package/registry/blocks/hero-scramble-decode/HeroScrambleDecode.tsx +381 -0
  576. package/registry/blocks/hero-scramble-decode/index.ts +7 -0
  577. package/registry/blocks/hero-shape-grid/HeroShapeGrid.tsx +279 -0
  578. package/registry/blocks/hero-shape-grid/index.ts +8 -0
  579. package/registry/blocks/hero-split-band-headline/HeroSplitBandHeadline.tsx +242 -0
  580. package/registry/blocks/hero-split-band-headline/index.ts +8 -0
  581. package/registry/blocks/hero-split-flap-counters/HeroSplitFlapCounters.tsx +323 -0
  582. package/registry/blocks/hero-split-flap-counters/index.ts +8 -0
  583. package/registry/blocks/hero-studio-mosaic/HeroStudioMosaic.tsx +332 -0
  584. package/registry/blocks/hero-studio-mosaic/index.ts +9 -0
  585. package/registry/blocks/hero-textured-dark/HeroTexturedDark.tsx +378 -0
  586. package/registry/blocks/hero-textured-dark/index.ts +10 -0
  587. package/registry/blocks/hero-twin-panels/HeroTwinPanels.tsx +231 -0
  588. package/registry/blocks/hero-twin-panels/index.ts +9 -0
  589. package/registry/blocks/hero-velocity-marquee/HeroVelocityMarquee.tsx +392 -0
  590. package/registry/blocks/hero-velocity-marquee/index.ts +8 -0
  591. package/registry/blocks/hero-video-in-type/HeroVideoInType.tsx +374 -0
  592. package/registry/blocks/hero-video-in-type/index.ts +9 -0
  593. package/registry/blocks/hero-video-spotlight/HeroVideoSpotlight.tsx +292 -0
  594. package/registry/blocks/hero-video-spotlight/index.ts +9 -0
  595. package/registry/blocks/hero-wave-band/HeroWaveBand.tsx +309 -0
  596. package/registry/blocks/hero-wave-band/index.ts +8 -0
  597. package/registry/blocks/how-it-works/how-it-works.tsx +330 -0
  598. package/registry/blocks/how-it-works/index.ts +28 -0
  599. package/registry/blocks/how-it-works-accordion-steps/HowItWorksAccordionSteps.tsx +213 -0
  600. package/registry/blocks/how-it-works-accordion-steps/index.ts +7 -0
  601. package/registry/blocks/how-it-works-center-spine-timeline/HowItWorksCenterSpineTimeline.tsx +200 -0
  602. package/registry/blocks/how-it-works-center-spine-timeline/index.ts +7 -0
  603. package/registry/blocks/how-it-works-column-panels/HowItWorksColumnPanels.tsx +299 -0
  604. package/registry/blocks/how-it-works-column-panels/index.ts +6 -0
  605. package/registry/blocks/how-it-works-contained-timeline/HowItWorksContainedTimeline.tsx +223 -0
  606. package/registry/blocks/how-it-works-contained-timeline/index.ts +8 -0
  607. package/registry/blocks/how-it-works-diagonal-photo-collage/HowItWorksDiagonalPhotoCollage.tsx +170 -0
  608. package/registry/blocks/how-it-works-diagonal-photo-collage/index.ts +7 -0
  609. package/registry/blocks/how-it-works-fanned-dissolve-cards/HowItWorksFannedDissolveCards.tsx +258 -0
  610. package/registry/blocks/how-it-works-fanned-dissolve-cards/index.ts +8 -0
  611. package/registry/blocks/how-it-works-full-bleed-step-panel/HowItWorksFullBleedStepPanel.tsx +319 -0
  612. package/registry/blocks/how-it-works-full-bleed-step-panel/index.ts +7 -0
  613. package/registry/blocks/how-it-works-grids.test.tsx +292 -0
  614. package/registry/blocks/how-it-works-interactive.test.tsx +262 -0
  615. package/registry/blocks/how-it-works-numbered-editorial-rows/HowItWorksNumberedEditorialRows.tsx +177 -0
  616. package/registry/blocks/how-it-works-numbered-editorial-rows/index.ts +7 -0
  617. package/registry/blocks/how-it-works-numbered-instruction-panels/HowItWorksNumberedInstructionPanels.tsx +226 -0
  618. package/registry/blocks/how-it-works-numbered-instruction-panels/index.ts +6 -0
  619. package/registry/blocks/how-it-works-numbered-photo-steps/HowItWorksNumberedPhotoSteps.tsx +150 -0
  620. package/registry/blocks/how-it-works-numbered-photo-steps/index.ts +7 -0
  621. package/registry/blocks/how-it-works-orbital-process/HowItWorksOrbitalProcess.tsx +317 -0
  622. package/registry/blocks/how-it-works-orbital-process/index.ts +7 -0
  623. package/registry/blocks/how-it-works-overlay-numeral-cards/HowItWorksOverlayNumeralCards.tsx +145 -0
  624. package/registry/blocks/how-it-works-overlay-numeral-cards/index.ts +7 -0
  625. package/registry/blocks/how-it-works-phase-line/HowItWorksPhaseLine.tsx +147 -0
  626. package/registry/blocks/how-it-works-phase-line/index.ts +8 -0
  627. package/registry/blocks/how-it-works-photo-bento-steps/HowItWorksPhotoBentoSteps.tsx +178 -0
  628. package/registry/blocks/how-it-works-photo-bento-steps/index.ts +7 -0
  629. package/registry/blocks/how-it-works-photo-rail-progress/HowItWorksPhotoRailProgress.tsx +231 -0
  630. package/registry/blocks/how-it-works-photo-rail-progress/index.ts +7 -0
  631. package/registry/blocks/how-it-works-pill-timeline/HowItWorksPillTimeline.tsx +181 -0
  632. package/registry/blocks/how-it-works-pill-timeline/index.ts +8 -0
  633. package/registry/blocks/how-it-works-rounded-cards-device-crop/HowItWorksRoundedCardsDeviceCrop.tsx +286 -0
  634. package/registry/blocks/how-it-works-rounded-cards-device-crop/index.ts +6 -0
  635. package/registry/blocks/how-it-works-six-card-grid/HowItWorksSixCardGrid.tsx +385 -0
  636. package/registry/blocks/how-it-works-six-card-grid/index.ts +6 -0
  637. package/registry/blocks/how-it-works-split-feature-band/HowItWorksSplitFeatureBand.tsx +316 -0
  638. package/registry/blocks/how-it-works-split-feature-band/index.ts +6 -0
  639. package/registry/blocks/how-it-works-split-photo-step-list/HowItWorksSplitPhotoStepList.tsx +179 -0
  640. package/registry/blocks/how-it-works-split-photo-step-list/index.ts +7 -0
  641. package/registry/blocks/how-it-works-stacked-media-text/HowItWorksStackedMediaText.tsx +310 -0
  642. package/registry/blocks/how-it-works-stacked-media-text/index.ts +8 -0
  643. package/registry/blocks/how-it-works-step-tabs-photo-panel/HowItWorksStepTabsPhotoPanel.tsx +245 -0
  644. package/registry/blocks/how-it-works-step-tabs-photo-panel/index.ts +7 -0
  645. package/registry/blocks/how-it-works-tall-visual-rail/HowItWorksTallVisualRail.tsx +349 -0
  646. package/registry/blocks/how-it-works-tall-visual-rail/index.ts +7 -0
  647. package/registry/blocks/how-it-works-timelines.test.tsx +265 -0
  648. package/registry/blocks/how-it-works-vertical-timeline-card/HowItWorksVerticalTimelineCard.tsx +169 -0
  649. package/registry/blocks/how-it-works-vertical-timeline-card/index.ts +8 -0
  650. package/registry/blocks/how-it-works-visual-steps.test.tsx +368 -0
  651. package/registry/blocks/logo-cloud-bento-logo-wall/LogoCloudBentoLogoWall.tsx +159 -0
  652. package/registry/blocks/logo-cloud-bento-logo-wall/index.ts +7 -0
  653. package/registry/blocks/logo-cloud-bordered-cell-grid/LogoCloudBorderedCellGrid.tsx +132 -0
  654. package/registry/blocks/logo-cloud-bordered-cell-grid/index.ts +7 -0
  655. package/registry/blocks/logo-cloud-centered-caption-row/LogoCloudCenteredCaptionRow.tsx +141 -0
  656. package/registry/blocks/logo-cloud-centered-caption-row/index.ts +7 -0
  657. package/registry/blocks/logo-cloud-contained-card/LogoCloudContainedCard.tsx +106 -0
  658. package/registry/blocks/logo-cloud-contained-card/index.ts +7 -0
  659. package/registry/blocks/logo-cloud-dark-logo-wall/LogoCloudDarkLogoWall.tsx +123 -0
  660. package/registry/blocks/logo-cloud-dark-logo-wall/index.ts +7 -0
  661. package/registry/blocks/logo-cloud-divider-rule-row/LogoCloudDividerRuleRow.tsx +156 -0
  662. package/registry/blocks/logo-cloud-divider-rule-row/index.ts +7 -0
  663. package/registry/blocks/logo-cloud-editorial-eyebrow-split/LogoCloudEditorialEyebrowSplit.tsx +121 -0
  664. package/registry/blocks/logo-cloud-editorial-eyebrow-split/index.ts +7 -0
  665. package/registry/blocks/logo-cloud-enterprise-wall/LogoCloudEnterpriseWall.tsx +114 -0
  666. package/registry/blocks/logo-cloud-enterprise-wall/index.ts +7 -0
  667. package/registry/blocks/logo-cloud-flanked-heading/LogoCloudFlankedHeading.tsx +162 -0
  668. package/registry/blocks/logo-cloud-flanked-heading/index.ts +8 -0
  669. package/registry/blocks/logo-cloud-grid-closing-cta/LogoCloudGridClosingCta.tsx +173 -0
  670. package/registry/blocks/logo-cloud-grid-closing-cta/index.ts +8 -0
  671. package/registry/blocks/logo-cloud-heading-grid/LogoCloudHeadingGrid.tsx +124 -0
  672. package/registry/blocks/logo-cloud-heading-grid/index.ts +7 -0
  673. package/registry/blocks/logo-cloud-heading-left-grid-right/LogoCloudHeadingLeftGridRight.tsx +172 -0
  674. package/registry/blocks/logo-cloud-heading-left-grid-right/index.ts +8 -0
  675. package/registry/blocks/logo-cloud-logo-metric-tiles/LogoCloudLogoMetricTiles.tsx +159 -0
  676. package/registry/blocks/logo-cloud-logo-metric-tiles/index.ts +8 -0
  677. package/registry/blocks/logo-cloud-logo-quote-cards/LogoCloudLogoQuoteCards.tsx +162 -0
  678. package/registry/blocks/logo-cloud-logo-quote-cards/index.ts +8 -0
  679. package/registry/blocks/logo-cloud-logo-testimonial-strip/LogoCloudLogoTestimonialStrip.tsx +159 -0
  680. package/registry/blocks/logo-cloud-logo-testimonial-strip/index.ts +8 -0
  681. package/registry/blocks/logo-cloud-named-integration-cards/LogoCloudNamedIntegrationCards.tsx +186 -0
  682. package/registry/blocks/logo-cloud-named-integration-cards/index.ts +9 -0
  683. package/registry/blocks/logo-cloud-pyramid-row-stack/LogoCloudPyramidRowStack.tsx +156 -0
  684. package/registry/blocks/logo-cloud-pyramid-row-stack/index.ts +7 -0
  685. package/registry/blocks/logo-cloud-quote-panel-grid/LogoCloudQuotePanelGrid.tsx +177 -0
  686. package/registry/blocks/logo-cloud-quote-panel-grid/index.ts +8 -0
  687. package/registry/blocks/logo-cloud-scatter-field/LogoCloudScatterField.tsx +190 -0
  688. package/registry/blocks/logo-cloud-scatter-field/index.ts +8 -0
  689. package/registry/blocks/logo-cloud-side-label-rail/LogoCloudSideLabelRail.tsx +139 -0
  690. package/registry/blocks/logo-cloud-side-label-rail/index.ts +7 -0
  691. package/registry/blocks/logo-cloud-stat-rail-above-grid/LogoCloudStatRailAboveGrid.tsx +173 -0
  692. package/registry/blocks/logo-cloud-stat-rail-above-grid/index.ts +8 -0
  693. package/registry/blocks/logo-cloud-tabbed-industry-filter/LogoCloudTabbedIndustryFilter.tsx +200 -0
  694. package/registry/blocks/logo-cloud-tabbed-industry-filter/index.ts +8 -0
  695. package/registry/blocks/navbar/index.ts +22 -11
  696. package/registry/blocks/navbar/navbar.test.tsx +42 -0
  697. package/registry/blocks/navbar/navbar.tsx +455 -0
  698. package/registry/blocks/navbar-active-action-with-border/NavbarActiveActionWithBorder.tsx +120 -0
  699. package/registry/blocks/navbar-active-action-with-border/index.ts +6 -0
  700. package/registry/blocks/navbar-active-action-with-round/NavbarActiveActionWithRound.tsx +120 -0
  701. package/registry/blocks/navbar-active-action-with-round/index.ts +6 -0
  702. package/registry/blocks/navbar-centered-floating/NavbarCenteredFloating.tsx +121 -0
  703. package/registry/blocks/navbar-centered-floating/index.ts +6 -0
  704. package/registry/blocks/navbar-default/NavbarDefault.tsx +102 -0
  705. package/registry/blocks/navbar-default/index.ts +6 -0
  706. package/registry/blocks/navbar-e-commerce/NavbarECommerce.tsx +212 -0
  707. package/registry/blocks/navbar-e-commerce/index.ts +6 -0
  708. package/registry/blocks/navbar-extended-at-top/NavbarExtendedAtTop.tsx +155 -0
  709. package/registry/blocks/navbar-extended-at-top/index.ts +7 -0
  710. package/registry/blocks/navbar-multiple-action/NavbarMultipleAction.tsx +133 -0
  711. package/registry/blocks/navbar-multiple-action/index.ts +6 -0
  712. package/registry/blocks/navbar-multiple-icon/NavbarMultipleIcon.tsx +132 -0
  713. package/registry/blocks/navbar-multiple-icon/index.ts +6 -0
  714. package/registry/blocks/navbar-rounded/NavbarRounded.tsx +140 -0
  715. package/registry/blocks/navbar-rounded/index.ts +6 -0
  716. package/registry/blocks/navbar-rounded-small/NavbarRoundedSmall.tsx +143 -0
  717. package/registry/blocks/navbar-rounded-small/index.ts +6 -0
  718. package/registry/blocks/navbar-small/NavbarSmall.tsx +142 -0
  719. package/registry/blocks/navbar-small/index.ts +6 -0
  720. package/registry/blocks/navbar-with-centered-logo/NavbarWithCenteredLogo.tsx +118 -0
  721. package/registry/blocks/navbar-with-centered-logo/index.ts +6 -0
  722. package/registry/blocks/navbar-with-cta-button/NavbarWithCtaButton.tsx +131 -0
  723. package/registry/blocks/navbar-with-cta-button/index.ts +6 -0
  724. package/registry/blocks/navbar-with-dropdown/NavbarWithDropdown.tsx +118 -0
  725. package/registry/blocks/navbar-with-dropdown/index.ts +6 -0
  726. package/registry/blocks/navbar-with-icon-indicator-avatar/NavbarWithIconIndicatorAvatar.tsx +85 -0
  727. package/registry/blocks/navbar-with-icon-indicator-avatar/index.ts +6 -0
  728. package/registry/blocks/navbar-with-search-input/NavbarWithSearchInput.tsx +90 -0
  729. package/registry/blocks/navbar-with-search-input/index.ts +6 -0
  730. package/registry/blocks/navbar-with-submenu/NavbarWithSubmenu.tsx +156 -0
  731. package/registry/blocks/navbar-with-submenu/index.ts +6 -0
  732. package/registry/blocks/page-header/PageHeader.tsx +5 -9
  733. package/registry/blocks/pricing/index.ts +20 -0
  734. package/registry/blocks/pricing/pricing.test.tsx +82 -0
  735. package/registry/blocks/pricing/pricing.tsx +343 -0
  736. package/registry/blocks/pricing-comparison-matrix/PricingComparisonMatrix.tsx +336 -0
  737. package/registry/blocks/pricing-comparison-matrix/index.ts +10 -0
  738. package/registry/blocks/pricing-plan-picker-summary/PricingPlanPickerSummary.tsx +399 -0
  739. package/registry/blocks/pricing-plan-picker-summary/index.ts +8 -0
  740. package/registry/blocks/pricing-plan-spotlight/PricingPlanSpotlight.tsx +281 -0
  741. package/registry/blocks/pricing-plan-spotlight/index.ts +7 -0
  742. package/registry/blocks/pricing-tiers/PricingTiers.tsx +264 -0
  743. package/registry/blocks/pricing-tiers/index.ts +8 -0
  744. package/registry/blocks/pricing-two-plan-compare/PricingTwoPlanCompare.tsx +187 -0
  745. package/registry/blocks/pricing-two-plan-compare/index.ts +7 -0
  746. package/registry/blocks/social-proof/index.ts +36 -0
  747. package/registry/blocks/social-proof/social-proof.test.tsx +504 -0
  748. package/registry/blocks/social-proof/social-proof.tsx +759 -0
  749. package/registry/blocks/social-proof-app-review-row/SocialProofAppReviewRow.tsx +168 -0
  750. package/registry/blocks/social-proof-app-review-row/index.ts +7 -0
  751. package/registry/blocks/social-proof-arch-portrait-quote/SocialProofArchPortraitQuote.tsx +114 -0
  752. package/registry/blocks/social-proof-arch-portrait-quote/index.ts +6 -0
  753. package/registry/blocks/social-proof-avatar-selector-row/SocialProofAvatarSelectorRow.tsx +276 -0
  754. package/registry/blocks/social-proof-avatar-selector-row/index.ts +7 -0
  755. package/registry/blocks/social-proof-benefit-title-cards/SocialProofBenefitTitleCards.tsx +182 -0
  756. package/registry/blocks/social-proof-benefit-title-cards/index.ts +8 -0
  757. package/registry/blocks/social-proof-bracket-framed-quote/SocialProofBracketFramedQuote.tsx +120 -0
  758. package/registry/blocks/social-proof-bracket-framed-quote/index.ts +6 -0
  759. package/registry/blocks/social-proof-case-study-row/SocialProofCaseStudyRow.tsx +210 -0
  760. package/registry/blocks/social-proof-case-study-row/index.ts +7 -0
  761. package/registry/blocks/social-proof-centered-logo-spotlight/SocialProofCenteredLogoSpotlight.tsx +103 -0
  762. package/registry/blocks/social-proof-centered-logo-spotlight/index.ts +6 -0
  763. package/registry/blocks/social-proof-classic-trio-grid/SocialProofClassicTrioGrid.tsx +138 -0
  764. package/registry/blocks/social-proof-classic-trio-grid/index.ts +7 -0
  765. package/registry/blocks/social-proof-dark-editorial-split/SocialProofDarkEditorialSplit.tsx +111 -0
  766. package/registry/blocks/social-proof-dark-editorial-split/index.ts +6 -0
  767. package/registry/blocks/social-proof-giant-quote-duo/SocialProofGiantQuoteDuo.tsx +156 -0
  768. package/registry/blocks/social-proof-giant-quote-duo/index.ts +8 -0
  769. package/registry/blocks/social-proof-highlighted-phrase-spotlight/SocialProofHighlightedPhraseSpotlight.tsx +130 -0
  770. package/registry/blocks/social-proof-highlighted-phrase-spotlight/index.ts +7 -0
  771. package/registry/blocks/social-proof-overlay-photo-quote/SocialProofOverlayPhotoQuote.tsx +111 -0
  772. package/registry/blocks/social-proof-overlay-photo-quote/index.ts +7 -0
  773. package/registry/blocks/social-proof-persona-pillars/SocialProofPersonaPillars.tsx +253 -0
  774. package/registry/blocks/social-proof-persona-pillars/index.ts +7 -0
  775. package/registry/blocks/social-proof-progress-carousel/SocialProofProgressCarousel.tsx +249 -0
  776. package/registry/blocks/social-proof-progress-carousel/index.ts +8 -0
  777. package/registry/blocks/social-proof-proof-bento/SocialProofProofBento.tsx +218 -0
  778. package/registry/blocks/social-proof-proof-bento/index.ts +6 -0
  779. package/registry/blocks/social-proof-spotlight-carousel/SocialProofSpotlightCarousel.tsx +212 -0
  780. package/registry/blocks/social-proof-spotlight-carousel/index.ts +7 -0
  781. package/registry/blocks/social-proof-staggered-columns/SocialProofStaggeredColumns.tsx +227 -0
  782. package/registry/blocks/social-proof-staggered-columns/index.ts +7 -0
  783. package/registry/blocks/social-proof-stat-story-cards/SocialProofStatStoryCards.tsx +218 -0
  784. package/registry/blocks/social-proof-stat-story-cards/index.ts +7 -0
  785. package/registry/blocks/social-proof-tinted-panel-portrait/SocialProofTintedPanelPortrait.tsx +130 -0
  786. package/registry/blocks/social-proof-tinted-panel-portrait/index.ts +7 -0
  787. package/registry/blocks/social-proof-tweet-wall/SocialProofTweetWall.tsx +179 -0
  788. package/registry/blocks/social-proof-tweet-wall/index.ts +7 -0
  789. package/registry/blocks/social-proof-vertical-column-wall/SocialProofVerticalColumnWall.tsx +220 -0
  790. package/registry/blocks/social-proof-vertical-column-wall/index.ts +7 -0
  791. package/registry/blocks/social-proof-video-story-cards/SocialProofVideoStoryCards.tsx +177 -0
  792. package/registry/blocks/social-proof-video-story-cards/index.ts +7 -0
  793. package/registry/blocks/social-proof-workplace-portrait-stories/SocialProofWorkplacePortraitStories.tsx +320 -0
  794. package/registry/blocks/social-proof-workplace-portrait-stories/index.ts +7 -0
  795. package/registry/blocks/team/index.ts +21 -0
  796. package/registry/blocks/team/team-classes.test.ts +35 -0
  797. package/registry/blocks/team/team.tsx +315 -0
  798. package/registry/blocks/team-annotated-portrait-lineup/TeamAnnotatedPortraitLineup.tsx +310 -0
  799. package/registry/blocks/team-annotated-portrait-lineup/index.ts +8 -0
  800. package/registry/blocks/team-annotated-portrait-lineup/team-annotated-portrait-lineup.test.tsx +71 -0
  801. package/registry/blocks/team-compact-directory-rows/TeamCompactDirectoryRows.tsx +190 -0
  802. package/registry/blocks/team-compact-directory-rows/compact-directory-rows.test.tsx +40 -0
  803. package/registry/blocks/team-compact-directory-rows/index.ts +7 -0
  804. package/registry/blocks/team-department-tabs-directory/TeamDepartmentTabsDirectory.tsx +414 -0
  805. package/registry/blocks/team-department-tabs-directory/department-tabs-directory.test.tsx +87 -0
  806. package/registry/blocks/team-department-tabs-directory/index.ts +10 -0
  807. package/registry/blocks/team-founder-spotlight/TeamFounderSpotlight.tsx +176 -0
  808. package/registry/blocks/team-founder-spotlight/index.ts +8 -0
  809. package/registry/blocks/team-founder-spotlight/team-founder-spotlight.test.tsx +70 -0
  810. package/registry/blocks/team-grid-backdrop-roster/TeamGridBackdropRoster.tsx +234 -0
  811. package/registry/blocks/team-grid-backdrop-roster/grid-backdrop-roster.test.tsx +72 -0
  812. package/registry/blocks/team-grid-backdrop-roster/index.ts +8 -0
  813. package/registry/blocks/team-hairline-grid/TeamHairlineGrid.tsx +190 -0
  814. package/registry/blocks/team-hairline-grid/hairline-grid.test.tsx +49 -0
  815. package/registry/blocks/team-hairline-grid/index.ts +1 -0
  816. package/registry/blocks/team-leadership-grid/TeamLeadershipGrid.tsx +183 -0
  817. package/registry/blocks/team-leadership-grid/index.ts +7 -0
  818. package/registry/blocks/team-leadership-grid/team-leadership-grid.test.tsx +38 -0
  819. package/registry/blocks/team-member-carousel/TeamMemberCarousel.tsx +178 -0
  820. package/registry/blocks/team-member-carousel/index.ts +7 -0
  821. package/registry/blocks/team-member-carousel/team-member-carousel.test.tsx +58 -0
  822. package/registry/blocks/team-member-table/TeamMemberTable.tsx +184 -0
  823. package/registry/blocks/team-member-table/index.ts +7 -0
  824. package/registry/blocks/team-member-table/member-table.test.tsx +43 -0
  825. package/registry/blocks/team-meta-profile-cards/TeamMetaProfileCards.tsx +155 -0
  826. package/registry/blocks/team-meta-profile-cards/index.ts +7 -0
  827. package/registry/blocks/team-meta-profile-cards/team-meta-profile-cards.test.tsx +29 -0
  828. package/registry/blocks/team-overlay-portrait-cards/TeamOverlayPortraitCards.tsx +145 -0
  829. package/registry/blocks/team-overlay-portrait-cards/index.ts +7 -0
  830. package/registry/blocks/team-overlay-portrait-cards/team-overlay-portrait-cards.test.tsx +49 -0
  831. package/registry/blocks/team-portrait-wall/TeamPortraitWall.tsx +131 -0
  832. package/registry/blocks/team-portrait-wall/index.ts +1 -0
  833. package/registry/blocks/team-portrait-wall/portrait-wall.test.tsx +45 -0
  834. package/registry/blocks/team-split-intro-member-rail/TeamSplitIntroMemberRail.tsx +203 -0
  835. package/registry/blocks/team-split-intro-member-rail/index.ts +7 -0
  836. package/registry/blocks/team-split-intro-member-rail/split-intro-member-rail.test.tsx +52 -0
  837. package/registry/blocks/team-we-re-hiring-cta/TeamWeReHiringCta.tsx +192 -0
  838. package/registry/blocks/team-we-re-hiring-cta/index.ts +7 -0
  839. package/registry/blocks/team-we-re-hiring-cta/we-re-hiring-cta.test.tsx +44 -0
  840. package/registry/blocks/video-call/index.ts +53 -0
  841. package/registry/blocks/video-call/video-call-around.test.tsx +427 -0
  842. package/registry/blocks/video-call/video-call-before.test.tsx +438 -0
  843. package/registry/blocks/video-call/video-call-hero.test.tsx +236 -0
  844. package/registry/blocks/video-call/video-call-stages.test.tsx +477 -0
  845. package/registry/blocks/video-call/video-call.test.tsx +794 -0
  846. package/registry/blocks/video-call/video-call.tsx +1282 -0
  847. package/registry/blocks/video-call-ai-meeting-recap/VideoCallAiMeetingRecap.tsx +466 -0
  848. package/registry/blocks/video-call-ai-meeting-recap/index.ts +9 -0
  849. package/registry/blocks/video-call-call-controls-dock/VideoCallCallControlsDock.tsx +211 -0
  850. package/registry/blocks/video-call-call-controls-dock/index.ts +6 -0
  851. package/registry/blocks/video-call-call-ended-summary/VideoCallCallEndedSummary.tsx +464 -0
  852. package/registry/blocks/video-call-call-ended-summary/index.ts +10 -0
  853. package/registry/blocks/video-call-call-with-side-panel/VideoCallCallWithSidePanel.tsx +357 -0
  854. package/registry/blocks/video-call-call-with-side-panel/index.ts +6 -0
  855. package/registry/blocks/video-call-captions-reactions/VideoCallCaptionsReactions.tsx +467 -0
  856. package/registry/blocks/video-call-captions-reactions/index.ts +10 -0
  857. package/registry/blocks/video-call-grid-gallery-call/VideoCallGridGalleryCall.tsx +312 -0
  858. package/registry/blocks/video-call-grid-gallery-call/index.ts +6 -0
  859. package/registry/blocks/video-call-hero-call-mockup/VideoCallHeroCallMockup.tsx +349 -0
  860. package/registry/blocks/video-call-hero-call-mockup/index.ts +7 -0
  861. package/registry/blocks/video-call-incoming-call/VideoCallIncomingCall.tsx +336 -0
  862. package/registry/blocks/video-call-incoming-call/index.ts +7 -0
  863. package/registry/blocks/video-call-join-link-dial-in/VideoCallJoinLinkDialIn.tsx +409 -0
  864. package/registry/blocks/video-call-join-link-dial-in/index.ts +9 -0
  865. package/registry/blocks/video-call-pre-join-lobby/VideoCallPreJoinLobby.tsx +345 -0
  866. package/registry/blocks/video-call-pre-join-lobby/index.ts +7 -0
  867. package/registry/blocks/video-call-screen-share-stage/VideoCallScreenShareStage.tsx +379 -0
  868. package/registry/blocks/video-call-screen-share-stage/index.ts +7 -0
  869. package/registry/blocks/video-call-speaker-filmstrip/VideoCallSpeakerFilmstrip.tsx +303 -0
  870. package/registry/blocks/video-call-speaker-filmstrip/index.ts +6 -0
  871. package/registry/blocks/video-call-upcoming-meetings/VideoCallUpcomingMeetings.tsx +349 -0
  872. package/registry/blocks/video-call-upcoming-meetings/index.ts +7 -0
  873. package/registry/blocks/video-call-waiting-room-knock/VideoCallWaitingRoomKnock.tsx +412 -0
  874. package/registry/blocks/video-call-waiting-room-knock/index.ts +7 -0
  875. package/registry/text-props-undefined.test.tsx +3 -26
  876. package/registry/ui/accordion/accordion.test.tsx +320 -0
  877. package/registry/ui/accordion/accordion.tsx +353 -0
  878. package/registry/ui/accordion/index.ts +15 -0
  879. package/registry/ui/alert/alert.test.tsx +325 -0
  880. package/registry/ui/alert/alert.tsx +384 -0
  881. package/registry/ui/alert/index.ts +10 -0
  882. package/registry/ui/alert-dialog/alert-dialog.test.tsx +396 -0
  883. package/registry/ui/alert-dialog/alert-dialog.tsx +423 -0
  884. package/registry/ui/alert-dialog/index.ts +21 -0
  885. package/registry/ui/avatar/avatar.test.tsx +2 -2
  886. package/registry/ui/avatar/avatar.tsx +3 -7
  887. package/registry/ui/breadcrumb/breadcrumb.test.tsx +305 -0
  888. package/registry/ui/breadcrumb/breadcrumb.tsx +205 -0
  889. package/registry/ui/breadcrumb/index.ts +11 -0
  890. package/registry/ui/button/button.test.tsx +353 -40
  891. package/registry/ui/button/button.tsx +304 -30
  892. package/registry/ui/calendar/calendar.test.tsx +192 -0
  893. package/registry/ui/calendar/calendar.tsx +405 -0
  894. package/registry/ui/calendar/index.ts +1 -0
  895. package/registry/ui/carousel/carousel.test.tsx +720 -0
  896. package/registry/ui/carousel/carousel.tsx +554 -0
  897. package/registry/ui/carousel/index.ts +11 -0
  898. package/registry/ui/checkbox/checkbox.test.tsx +318 -2
  899. package/registry/ui/checkbox/checkbox.tsx +253 -67
  900. package/registry/ui/checkbox/index.ts +1 -1
  901. package/registry/ui/content-carousel/content-carousel.test.tsx +303 -0
  902. package/registry/ui/content-carousel/content-carousel.tsx +180 -0
  903. package/registry/ui/content-carousel/index.ts +8 -0
  904. package/registry/ui/date-picker/date-picker.test.tsx +216 -0
  905. package/registry/ui/date-picker/date-picker.tsx +461 -0
  906. package/registry/ui/date-picker/index.ts +8 -0
  907. package/registry/ui/dialog/dialog.test.tsx +438 -0
  908. package/registry/ui/dialog/dialog.tsx +533 -0
  909. package/registry/ui/dialog/index.ts +20 -0
  910. package/registry/ui/dropdown-menu/dropdown-menu.test.tsx +502 -0
  911. package/registry/ui/dropdown-menu/dropdown-menu.tsx +627 -0
  912. package/registry/ui/dropdown-menu/index.ts +32 -0
  913. package/registry/ui/fasla-logo/fasla-logo.tsx +101 -0
  914. package/registry/ui/fasla-logo/index.ts +1 -0
  915. package/registry/ui/input/index.ts +10 -1
  916. package/registry/ui/input/input.test.tsx +251 -44
  917. package/registry/ui/input/input.tsx +311 -40
  918. package/registry/ui/input-number/index.ts +1 -0
  919. package/registry/ui/input-number/input-number.test.tsx +302 -0
  920. package/registry/ui/input-number/input-number.tsx +398 -0
  921. package/registry/ui/input-otp/index.ts +7 -0
  922. package/registry/ui/input-otp/input-otp.test.tsx +299 -0
  923. package/registry/ui/input-otp/input-otp.tsx +279 -0
  924. package/registry/ui/pagination/index.ts +12 -0
  925. package/registry/ui/pagination/pagination.test.tsx +330 -0
  926. package/registry/ui/pagination/pagination.tsx +336 -0
  927. package/registry/ui/progress/index.ts +8 -0
  928. package/registry/ui/progress/progress.test.tsx +168 -0
  929. package/registry/ui/progress/progress.tsx +165 -0
  930. package/registry/ui/select/index.ts +12 -1
  931. package/registry/ui/select/select.test.tsx +505 -0
  932. package/registry/ui/select/select.tsx +658 -56
  933. package/registry/ui/sheet/index.ts +19 -0
  934. package/registry/ui/sheet/sheet.test.tsx +411 -0
  935. package/registry/ui/sheet/sheet.tsx +496 -0
  936. package/registry/{blocks → ui}/sidebar/index.ts +10 -5
  937. package/registry/ui/sidebar/sidebar.test.tsx +224 -0
  938. package/registry/ui/sidebar/sidebar.tsx +754 -0
  939. package/registry/ui/slider/index.ts +8 -0
  940. package/registry/ui/slider/slider.test.tsx +231 -0
  941. package/registry/ui/slider/slider.tsx +319 -0
  942. package/registry/ui/social-icons/index.ts +1 -0
  943. package/registry/ui/social-icons/social-icons.tsx +284 -0
  944. package/registry/ui/steps/index.ts +16 -0
  945. package/registry/ui/steps/steps.test.tsx +435 -0
  946. package/registry/ui/steps/steps.tsx +571 -0
  947. package/registry/ui/switch/switch.tsx +3 -2
  948. package/registry/ui/tabs/index.ts +4 -0
  949. package/registry/ui/tabs/tabs.test.tsx +576 -0
  950. package/registry/ui/tabs/tabs.tsx +324 -112
  951. package/registry/ui/textarea/index.ts +1 -1
  952. package/registry/ui/textarea/textarea.test.tsx +294 -0
  953. package/registry/ui/textarea/textarea.tsx +329 -56
  954. package/registry/ui/tooltip/index.ts +14 -0
  955. package/registry/ui/tooltip/tooltip.test.tsx +301 -0
  956. package/registry/ui/tooltip/tooltip.tsx +409 -0
  957. package/registry.json +9703 -140
  958. package/tailwind-preset.ts +2 -2
  959. package/registry/blocks/navbar/Navbar.test.tsx +0 -12
  960. package/registry/blocks/navbar/Navbar.tsx +0 -175
  961. package/registry/blocks/sidebar/Sidebar.test.tsx +0 -39
  962. package/registry/blocks/sidebar/Sidebar.tsx +0 -212
@@ -0,0 +1,989 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { Check } from "lucide-react"
5
+ import { cn } from "../../../src/lib/utils"
6
+ import { badgeVariants } from "../../ui/badge/badge"
7
+ import { Button, type ButtonProps } from "../../ui/button/button"
8
+ import { Checkbox } from "../../ui/checkbox/checkbox"
9
+ import { DatePicker } from "../../ui/date-picker/date-picker"
10
+ import { Input } from "../../ui/input/input"
11
+ import { Select } from "../../ui/select/select"
12
+ import { SOCIAL_PLATFORM_NAMES, SocialIcon, type SocialPlatform } from "../../ui/social-icons/social-icons"
13
+ import { Textarea } from "../../ui/textarea/textarea"
14
+
15
+ /*
16
+ * Figma: `Contact Us` (42334:293941). The parts every `contact-us-*` block is
17
+ * built from, one per thing the Figma set repeats: the section and its
18
+ * 1248px column, the header, the hairline, the contact form (its fields, its
19
+ * state machine and its three outcomes), the single-choice chips, the
20
+ * social links, and the small motion helper the entrances use.
21
+ *
22
+ * Mobile first, and sized by the block's own width, not the window's, as the
23
+ * Careers, Pricing and Gallery blocks are: the section is a query container,
24
+ * so a block lays out for the room it is given, such as a docs preview
25
+ * narrower than the screen. The base classes are Figma's Mobile (360);
26
+ * `[@container(min-width:40rem)]:` is its Tablet, from 640px of content
27
+ * (Figma's Tablet, 768 less its padding), and `[@container(min-width:64rem)]:`
28
+ * its Desktop, from 1024px of content. The section's own padding still
29
+ * follows the window, since an element can't query itself. The page's `dir`
30
+ * sets the direction; nothing here takes one.
31
+ *
32
+ * Forms: there is no backend. A form validates on the client, then calls the
33
+ * block's `onSubmit(values)`: resolve to show the thank-you panel, reject (or
34
+ * throw) to show the send error.
35
+ *
36
+ * Required fields: a required text field, select or textarea shows the word
37
+ * `requiredLabel` ("Required", «مطلوب») and nothing else, in the atom's own Top
38
+ * Right label slot (`labelEnd`), at the end of the label row. A field with no
39
+ * visible label (`hideLabel`) shows no word and no extra row; it is still
40
+ * required in behaviour. No label or placeholder carries an asterisk,
41
+ * anywhere. `aria-required` and the error on submit are the behaviour. Consent and terms checkboxes are required too, with neither
42
+ * the word nor an asterisk.
43
+ *
44
+ * STAND-INS. Figma components this family has no code atom for. Each is
45
+ * replaced by plain markup inside this file, so it can be swapped for the
46
+ * real atom, in one place, once the atom exists. Rating is used by no Type.
47
+ *
48
+ * - Toggle (15088:228441): the 12 time-slot buttons of Book a Call.
49
+ * Stand-in: `aria-pressed` buttons styled with `buttonVariants`. Used by
50
+ * Book a Call.
51
+ * - Table Cell (39961:4559): the 30 cells of Global Offices Table.
52
+ * Stand-in: native `<table>` markup, a `th` or `td` styled like the cell.
53
+ * Used by Global Offices Table.
54
+ * - Separator (14860:69243): the 3 dividing lines of Editorial & Address Card.
55
+ * Stand-in: `ContactUsRule`, a 1px `bg-border` line. Used by Editorial &
56
+ * Address Card, and by Ruled Split Form and Oversized Wordmark, where Figma
57
+ * draws a plain frame.
58
+ *
59
+ * Tints: `soft-warning-5`, `soft-warning-30` and `soft-success-30` are
60
+ * Figma's `opacity/warning/5`, `opacity/warning/30` and `opacity/success/30`.
61
+ * They live in the theme with the other soft tints.
62
+ *
63
+ * Motion uses the repo's presets, copied in (120, 180 and 260ms,
64
+ * `cubic-bezier(0.2, 0, 0, 1)`), as the Web Animations API, once, when the
65
+ * block is a fifth in view, and never when the reader prefers reduced motion:
66
+ * then a block only fades.
67
+ */
68
+
69
+ export type ContactUsLocale = "en" | "ar"
70
+
71
+ /**
72
+ * Splits a block's props into its copy (every key the default copy has, with
73
+ * the default where the prop is left out) and the rest.
74
+ */
75
+ export function splitCopy<C extends object, P extends object>(defaults: C, props: P) {
76
+ const copy: Record<string, unknown> = { ...(defaults as Record<string, unknown>) }
77
+ const rest: Record<string, unknown> = {}
78
+ for (const [key, value] of Object.entries(props)) {
79
+ if (key in defaults) {
80
+ if (value !== undefined) copy[key] = value
81
+ } else rest[key] = value
82
+ }
83
+ return { copy: copy as C, rest: rest as Omit<P, keyof C> }
84
+ }
85
+
86
+ /** The props every form block takes, besides its copy. */
87
+ export interface ContactUsFormBlockProps extends Omit<React.HTMLAttributes<HTMLElement>, "title" | "onSubmit"> {
88
+ /** Which language the default copy is in. The page's `dir` sets the direction. */
89
+ locale?: ContactUsLocale
90
+ /**
91
+ * Called with the checked fields, by name. Resolve to show the thank-you
92
+ * panel; reject, or throw, to show the send error. The block sends nothing
93
+ * itself.
94
+ */
95
+ onSubmit?: (values: ContactUsValues) => void | Promise<void>
96
+ /** Turns every field and the button off. */
97
+ disabled?: boolean
98
+ }
99
+
100
+ /* ───────────────────────────── Motion ───────────────────────────── */
101
+
102
+ const STANDARD = "cubic-bezier(0.2, 0, 0, 1)"
103
+ const FAST_MS = 120
104
+ const BASE_MS = 180
105
+ const SLOW_MS = 260
106
+ const STAGGER_MS = 50
107
+ const MAX_STAGGER_MS = 300
108
+
109
+ const useIsomorphicLayoutEffect = typeof window !== "undefined" ? React.useLayoutEffect : React.useEffect
110
+
111
+ /** Whether the reader asked the system for less motion. */
112
+ export function prefersReducedMotion() {
113
+ return typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches
114
+ }
115
+
116
+ /**
117
+ * Plays one entrance on each node, in order. What a node does is named by its
118
+ * `data-reveal`: `rise` (the default: fade up 12px), `fade`, `word` (fade up
119
+ * 14px, for a sentence set word by word), `draw-x` and `draw-y` (a rule drawn
120
+ * from its start or its top; set its `transform-origin` in CSS) and `wipe`
121
+ * (opens from the start edge while it scales in). A node's own
122
+ * `data-reveal-delay` (ms) replaces its place in the stagger.
123
+ */
124
+ export function playReveal(nodes: HTMLElement[]) {
125
+ const reduced = prefersReducedMotion()
126
+ return nodes
127
+ .filter((node) => typeof node.animate === "function")
128
+ .map((node, index) => {
129
+ const kind = node.dataset.reveal || "rise"
130
+ const own = node.dataset.revealDelay === undefined ? Number.NaN : Number(node.dataset.revealDelay)
131
+ const delay = reduced ? 0 : Number.isFinite(own) ? own : Math.min(index * STAGGER_MS, MAX_STAGGER_MS)
132
+ const rtl = getComputedStyle(node).direction === "rtl"
133
+ let frames: Keyframe[]
134
+ if (reduced || kind === "fade") frames = [{ opacity: 0 }, { opacity: 1 }]
135
+ else if (kind === "draw-x") frames = [{ transform: "scaleX(0)" }, { transform: "scaleX(1)" }]
136
+ else if (kind === "draw-y") frames = [{ transform: "scaleY(0)" }, { transform: "scaleY(1)" }]
137
+ else if (kind === "pan") frames = [{ transform: "scale(1.08)" }, { transform: "none" }]
138
+ else if (kind === "drop")
139
+ // The pin: falls 24px, overshoots by 3px and settles, one keyframe of overshoot.
140
+ frames = [
141
+ { opacity: 0, transform: "translateY(-24px)" },
142
+ { opacity: 1, transform: "translateY(3px)", offset: 0.7 },
143
+ { opacity: 1, transform: "none" },
144
+ ]
145
+ else if (kind === "ring") frames = [{ opacity: 0.5, transform: "scale(0.6)" }, { opacity: 0, transform: "scale(1.8)" }]
146
+ else if (kind === "settle") frames = [{ opacity: 0, transform: "scale(1.04)" }, { opacity: 1, transform: "none" }]
147
+ else if (kind === "slide") {
148
+ // Travels 16px to its place, from the end side (the default) or the start side, whichever way the page reads.
149
+ const side = (node.dataset.revealFrom === "start" ? -1 : 1) * (rtl ? -1 : 1)
150
+ frames = [{ opacity: 0, transform: `translateX(${16 * side}px)` }, { opacity: 1, transform: "none" }]
151
+ } else if (kind === "wipe")
152
+ frames = [
153
+ { clipPath: rtl ? "inset(0 0 0 100%)" : "inset(0 100% 0 0)", opacity: 0, transform: "scale(0.94)" },
154
+ { clipPath: "inset(0 0 0 0)", opacity: 1, transform: "none" },
155
+ ]
156
+ else frames = [{ opacity: 0, transform: `translateY(${kind === "word" ? 14 : 12}px)` }, { opacity: 1, transform: "none" }]
157
+ return node.animate(frames, {
158
+ duration: reduced ? FAST_MS : ["wipe", "settle", "slide", "pan", "drop", "ring"].includes(kind) ? SLOW_MS : BASE_MS,
159
+ delay,
160
+ easing: STANDARD,
161
+ fill: "backwards",
162
+ })
163
+ })
164
+ }
165
+
166
+ /**
167
+ * Plays `playReveal` once on the `[data-reveal]` nodes inside the returned
168
+ * ref's element, when a fifth of it is in view. Before then, and without
169
+ * script, everything is simply there.
170
+ */
171
+ export function useReveal<T extends HTMLElement>() {
172
+ const ref = React.useRef<T>(null)
173
+ useIsomorphicLayoutEffect(() => {
174
+ const node = ref.current
175
+ if (!node || typeof IntersectionObserver === "undefined") return
176
+ let animations: Animation[] = []
177
+ const observer = new IntersectionObserver(
178
+ (entries) => {
179
+ if (!entries.some((entry) => entry.isIntersecting)) return
180
+ observer.disconnect()
181
+ animations = playReveal(Array.from(node.querySelectorAll<HTMLElement>("[data-reveal]")))
182
+ },
183
+ { threshold: 0.2 }
184
+ )
185
+ observer.observe(node)
186
+ return () => {
187
+ observer.disconnect()
188
+ animations.forEach((animation) => animation.cancel())
189
+ }
190
+ }, [])
191
+ return ref
192
+ }
193
+
194
+ /**
195
+ * A sentence set word by word, each word a `data-reveal="word"` span that
196
+ * rises 30ms after the one before. The words stay one run of text for
197
+ * assistive technology.
198
+ */
199
+ export function ContactUsWords({ children, className }: { children: string; className?: string }) {
200
+ const words = children.split(/\s+/).filter(Boolean)
201
+ return (
202
+ <span className={className}>
203
+ {words.map((word, index) => (
204
+ <React.Fragment key={index}>
205
+ {index > 0 ? " " : null}
206
+ <span data-reveal="word" data-reveal-delay={Math.min(index * 30, 360)} className="inline-block">
207
+ {word}
208
+ </span>
209
+ </React.Fragment>
210
+ ))}
211
+ </span>
212
+ )
213
+ }
214
+
215
+ /* ───────────────────────────── Live time ───────────────────────────── */
216
+
217
+ /** One day's opening hours, "09:00" to "18:00" in the place's own time, or `null` when it is closed. */
218
+ export type ContactUsHours = { open: string; close: string } | null
219
+
220
+ /** A week of opening hours, Sunday first, as `Date#getDay` counts. */
221
+ export type ContactUsWeek = readonly [ContactUsHours, ContactUsHours, ContactUsHours, ContactUsHours, ContactUsHours, ContactUsHours, ContactUsHours]
222
+
223
+ const WEEKDAYS: Record<string, number> = { Sun: 0, Mon: 1, Tue: 2, Wed: 3, Thu: 4, Fri: 5, Sat: 6 }
224
+ const partFormatters = new Map<string, Intl.DateTimeFormat>()
225
+
226
+ /** The weekday (0 is Sunday) and the minutes since midnight at `date` in `timeZone`. */
227
+ export function zoneParts(date: Date, timeZone: string) {
228
+ let format = partFormatters.get(timeZone)
229
+ if (!format) {
230
+ format = new Intl.DateTimeFormat("en-US", { timeZone, weekday: "short", hour: "2-digit", minute: "2-digit", hourCycle: "h23" })
231
+ partFormatters.set(timeZone, format)
232
+ }
233
+ const parts = format.formatToParts(date)
234
+ const get = (type: string) => parts.find((part) => part.type === type)?.value ?? ""
235
+ return { day: WEEKDAYS[get("weekday")] ?? 0, minutes: Number(get("hour")) * 60 + Number(get("minute")) }
236
+ }
237
+
238
+ /** "14:20": the time at `date` in `timeZone`, 24-hour, always with Western digits, in any language. */
239
+ export function formatClock(date: Date, timeZone: string) {
240
+ return new Intl.DateTimeFormat("en-GB", { timeZone, hour: "2-digit", minute: "2-digit", hourCycle: "h23" }).format(date)
241
+ }
242
+
243
+ const toMinutes = (time: string) => Number(time.slice(0, 2)) * 60 + Number(time.slice(3, 5))
244
+
245
+ export interface ContactUsOpenState {
246
+ open: boolean
247
+ /** The weekday at `now` in the place's time zone, 0 is Sunday. */
248
+ day: number
249
+ /** While open: the minutes until it closes. */
250
+ closesInMinutes?: number
251
+ /** While closed: when it opens next, `daysAhead` days on (0 is later today) at `time`. */
252
+ next?: { daysAhead: number; day: number; time: string }
253
+ }
254
+
255
+ /** Whether a place on `week` is open at `now`, and when that next changes. */
256
+ export function openState(week: ContactUsWeek, timeZone: string, now: Date): ContactUsOpenState {
257
+ const { day, minutes } = zoneParts(now, timeZone)
258
+ const today = week[day]
259
+ if (today && minutes >= toMinutes(today.open) && minutes < toMinutes(today.close)) return { open: true, day, closesInMinutes: toMinutes(today.close) - minutes }
260
+ if (today && minutes < toMinutes(today.open)) return { open: false, day, next: { daysAhead: 0, day, time: today.open } }
261
+ for (let ahead = 1; ahead <= 7; ahead++) {
262
+ const d = (day + ahead) % 7
263
+ const hours = week[d]
264
+ if (hours) return { open: false, day, next: { daysAhead: ahead, day: d, time: hours.open } }
265
+ }
266
+ return { open: false, day }
267
+ }
268
+
269
+ /** 220 minutes becomes { hours: 3, minutes: 40 }. */
270
+ export const splitMinutes = (total: number) => ({ hours: Math.floor(total / 60), minutes: total % 60 })
271
+
272
+ /**
273
+ * The time a live block shows. Give it `now` and it is frozen there, the same
274
+ * on the server and in the browser. Without it the first render is `null`, so
275
+ * the server and the browser agree, then it is the real time, ticking on every
276
+ * minute and paused while the tab is hidden.
277
+ */
278
+ export function useLiveNow(now?: Date | number | string): Date | null {
279
+ const frozen = now === undefined ? null : new Date(now)
280
+ const frozenAt = frozen ? frozen.getTime() : null
281
+ const [live, setLive] = React.useState<Date | null>(null)
282
+ React.useEffect(() => {
283
+ if (frozenAt !== null) return
284
+ let timer: ReturnType<typeof setTimeout> | undefined
285
+ const tick = () => {
286
+ setLive(new Date())
287
+ const at = new Date()
288
+ timer = setTimeout(tick, 60_000 - (at.getSeconds() * 1000 + at.getMilliseconds()))
289
+ }
290
+ const sync = () => {
291
+ clearTimeout(timer)
292
+ if (!document.hidden) tick()
293
+ }
294
+ sync()
295
+ document.addEventListener("visibilitychange", sync)
296
+ return () => {
297
+ clearTimeout(timer)
298
+ document.removeEventListener("visibilitychange", sync)
299
+ }
300
+ }, [frozenAt])
301
+ return frozen ?? live
302
+ }
303
+
304
+ /**
305
+ * Counts `target` up from zero once, the first time half of the element is in
306
+ * view, over the slow preset. Reduced motion, and a browser without
307
+ * IntersectionObserver, show the target at once. The first render is the target.
308
+ */
309
+ export function useCountUp<T extends HTMLElement>(target: number) {
310
+ const ref = React.useRef<T>(null)
311
+ const [value, setValue] = React.useState(target)
312
+ React.useEffect(() => {
313
+ setValue(target)
314
+ const node = ref.current
315
+ if (!node || typeof IntersectionObserver === "undefined" || prefersReducedMotion()) return
316
+ let frame = 0
317
+ const observer = new IntersectionObserver(
318
+ (entries) => {
319
+ if (!entries.some((entry) => entry.isIntersecting)) return
320
+ observer.disconnect()
321
+ const start = performance.now()
322
+ const step = (at: number) => {
323
+ const t = Math.min(1, (at - start) / SLOW_MS)
324
+ setValue(Math.round(target * (1 - (1 - t) * (1 - t))))
325
+ if (t < 1) frame = requestAnimationFrame(step)
326
+ }
327
+ setValue(0)
328
+ frame = requestAnimationFrame(step)
329
+ },
330
+ { threshold: 0.5 }
331
+ )
332
+ observer.observe(node)
333
+ return () => {
334
+ observer.disconnect()
335
+ cancelAnimationFrame(frame)
336
+ }
337
+ }, [target])
338
+ return [ref, value] as const
339
+ }
340
+
341
+ /* ─────────────────────────── The dark band ─────────────────────────── */
342
+
343
+ /**
344
+ * Which theme scope a dark band takes: `dark` on a light page and `light` on a
345
+ * dark one, so the band is the page's opposite, as Figma's `background-inverse`
346
+ * panels are. A light page is assumed until the browser says otherwise.
347
+ */
348
+ export function useInverseScope(): "dark" | "light" {
349
+ const [scope, setScope] = React.useState<"dark" | "light">("dark")
350
+ useIsomorphicLayoutEffect(() => {
351
+ const root = document.documentElement
352
+ const read = () => setScope(root.classList.contains("dark") ? "light" : "dark")
353
+ read()
354
+ const observer = new MutationObserver(read)
355
+ observer.observe(root, { attributes: true, attributeFilter: ["class"] })
356
+ return () => observer.disconnect()
357
+ }, [])
358
+ return scope
359
+ }
360
+
361
+ /**
362
+ * A panel on the page's opposite theme. Everything inside it, the atoms
363
+ * included, draws with the other mode's tokens, so a field on it is dark with no
364
+ * colour of ours. Inside, use the plain tokens (`text-foreground`,
365
+ * `text-muted-foreground`): they already mean what Figma's `*-inverse` tokens do.
366
+ */
367
+ export function ContactUsInverseBand({ className, children, ...props }: React.HTMLAttributes<HTMLDivElement>) {
368
+ const scope = useInverseScope()
369
+ return (
370
+ <div className={cn(scope, "bg-background text-foreground", className)} {...props}>
371
+ {children}
372
+ </div>
373
+ )
374
+ }
375
+
376
+ /* ──────────────────────────── Page parts ──────────────────────────── */
377
+
378
+ export interface ContactUsSectionProps extends React.HTMLAttributes<HTMLElement> {
379
+ /** The id of the heading that names the section. */
380
+ labelledBy?: string
381
+ /** Classes for the 1248px column inside the padding. */
382
+ contentClassName?: string
383
+ /** The padding around the column: 96px on Desktop (`lg`) or 64px (`md`), as Figma sets each Type. */
384
+ padding?: "lg" | "md"
385
+ /** The space between the column's parts: 48px (`lg`), 40px (`md`) or none. */
386
+ gap?: "lg" | "md" | "none"
387
+ /** The section's ground: `background`, or `muted`. */
388
+ tone?: "background" | "muted"
389
+ /** Something drawn behind the column and across the whole section, such as Grand Headline Form's grid. */
390
+ backdrop?: React.ReactNode
391
+ }
392
+
393
+ /**
394
+ * Figma's section: padding 96 on Desktop, 64 × 24 on Tablet and 32 × 16 on
395
+ * phones (64 on Desktop for `md`), and a 1248px column.
396
+ */
397
+ export function ContactUsSection({ labelledBy, className, contentClassName, padding = "lg", gap = "lg", tone = "background", backdrop, children, ...props }: ContactUsSectionProps) {
398
+ return (
399
+ <section
400
+ aria-labelledby={labelledBy}
401
+ className={cn(
402
+ "[container-type:inline-size] px-4 py-8 md:px-6 md:py-16",
403
+ padding === "lg" ? "lg:p-24" : "lg:p-16",
404
+ tone === "muted" ? "bg-muted" : "bg-background",
405
+ backdrop ? "relative overflow-hidden" : undefined,
406
+ className
407
+ )}
408
+ {...props}
409
+ >
410
+ {backdrop}
411
+ <div className={cn("mx-auto flex w-full max-w-[1248px] flex-col", backdrop ? "relative" : undefined, gap === "lg" ? "gap-12" : gap === "md" ? "gap-10" : "gap-0", contentClassName)}>{children}</div>
412
+ </section>
413
+ )
414
+ }
415
+
416
+ export interface ContactUsHeaderProps extends Omit<React.HTMLAttributes<HTMLElement>, "title"> {
417
+ /** The id the section's `aria-labelledby` points at. */
418
+ headingId: string
419
+ /** The small line above the heading. Hidden when empty. */
420
+ eyebrow?: React.ReactNode
421
+ /** The section's heading, an `h2`. */
422
+ heading: React.ReactNode
423
+ /** The line under the heading. Hidden when empty. */
424
+ description?: React.ReactNode
425
+ /** Where the header's lines sit: at the start, or centred. */
426
+ align?: "start" | "center"
427
+ /** Replaces the eyebrow's classes. */
428
+ eyebrowClassName?: string
429
+ /** Replaces the heading's classes. */
430
+ headingClassName?: string
431
+ /** Replaces the description's classes. */
432
+ descriptionClassName?: string
433
+ /** Lets the heading rise into place once, when the block is in view. */
434
+ reveal?: boolean
435
+ }
436
+
437
+ /** The header: an optional eyebrow, the heading and its description in `muted-foreground`, 12px apart. */
438
+ export function ContactUsHeader({
439
+ headingId,
440
+ eyebrow,
441
+ heading,
442
+ description,
443
+ align = "start",
444
+ eyebrowClassName,
445
+ headingClassName,
446
+ descriptionClassName,
447
+ reveal,
448
+ className,
449
+ children,
450
+ ...props
451
+ }: ContactUsHeaderProps) {
452
+ return (
453
+ <header className={cn("flex w-full flex-col gap-3", align === "center" ? "items-center text-center" : "items-start", className)} {...props}>
454
+ {eyebrow ? <p className={cn("text-sm font-semibold text-primary", eyebrowClassName)}>{eyebrow}</p> : null}
455
+ <h2 id={headingId} data-reveal={reveal ? "rise" : undefined} className={cn("text-4xl font-semibold text-foreground", headingClassName)}>
456
+ {heading}
457
+ </h2>
458
+ {description ? <p className={cn("text-base text-muted-foreground", descriptionClassName)}>{description}</p> : null}
459
+ {children}
460
+ </header>
461
+ )
462
+ }
463
+
464
+ /** The plain hairline: a 1px `border` line, across (the default) or down. Stand-in for Separator. */
465
+ export function ContactUsRule({ orientation = "horizontal", className, ...props }: React.HTMLAttributes<HTMLDivElement> & { orientation?: "horizontal" | "vertical" }) {
466
+ return <div aria-hidden className={cn("shrink-0 bg-border", orientation === "horizontal" ? "h-px w-full" : "w-px self-stretch", className)} {...props} />
467
+ }
468
+
469
+ /**
470
+ * A photo a block shows. No block ships with one: pass your own. Alt is empty
471
+ * ("") for a decorative photo. The slot crops the photo to fill it, around its
472
+ * middle; `focus` moves that middle, as Figma's image crops do.
473
+ */
474
+ export interface ContactUsPhoto {
475
+ src: string
476
+ alt: string
477
+ /**
478
+ * Where the slot's crop is centred, as CSS `object-position` values such as
479
+ * "50% 81%": on a phone (`base`), from Tablet (`tablet`) and from Desktop
480
+ * (`desktop`). Each falls back to the one before, and all to the middle.
481
+ */
482
+ focus?: { base?: string; tablet?: string; desktop?: string }
483
+ }
484
+
485
+ /**
486
+ * A photo slot: the box Figma calls Image Slot, which crops its photo to fill
487
+ * it. A `muted` ground shows without a photo. `photoReveal` puts a `data-reveal`
488
+ * on the photo itself, so a `settle` stays inside the box's clip.
489
+ */
490
+ export function ContactUsPhotoSlot({ photo, photoReveal, className, children, ...props }: React.HTMLAttributes<HTMLDivElement> & { photo?: ContactUsPhoto; photoReveal?: string }) {
491
+ const focus = photo?.focus
492
+ const style = focus
493
+ ? ({ "--cu-pos": focus.base, "--cu-pos-t": focus.tablet, "--cu-pos-d": focus.desktop } as React.CSSProperties)
494
+ : undefined
495
+ return (
496
+ <div className={cn("relative overflow-hidden bg-muted", className)} {...props}>
497
+ {photo ? (
498
+ <img
499
+ src={photo.src}
500
+ alt={photo.alt}
501
+ loading="lazy"
502
+ decoding="async"
503
+ data-reveal={photoReveal}
504
+ style={style}
505
+ className="absolute inset-0 size-full object-cover [object-position:var(--cu-pos,50%_50%)] [@container(min-width:40rem)]:[object-position:var(--cu-pos-t,var(--cu-pos,50%_50%))] [@container(min-width:64rem)]:[object-position:var(--cu-pos-d,var(--cu-pos-t,var(--cu-pos,50%_50%)))]"
506
+ />
507
+ ) : null}
508
+ {children}
509
+ </div>
510
+ )
511
+ }
512
+
513
+ export interface ContactUsSocialLink {
514
+ /** Which brand's mark. */
515
+ platform: SocialPlatform
516
+ /** Where it goes. */
517
+ href: string
518
+ /** Names the link for assistive technology. The brand's own name without it. */
519
+ label?: string
520
+ }
521
+
522
+ /** A row of brand marks, each a link, 12px apart. The marks come from the Social Icons atom. */
523
+ export function ContactUsSocialLinks({ links, className }: { links: ContactUsSocialLink[]; className?: string }) {
524
+ return (
525
+ <ul className={cn("flex items-center gap-3", className)}>
526
+ {links.map((link) => (
527
+ <li key={link.platform} className="flex">
528
+ <a
529
+ href={link.href}
530
+ aria-label={link.label ?? SOCIAL_PLATFORM_NAMES[link.platform]}
531
+ className="inline-flex rounded-sm focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50"
532
+ >
533
+ <SocialIcon platform={link.platform} className="size-6" />
534
+ </a>
535
+ </li>
536
+ ))}
537
+ </ul>
538
+ )
539
+ }
540
+
541
+ /* ───────────────────────────── The form ───────────────────────────── */
542
+
543
+ export type ContactUsValue = string | boolean
544
+ /** What `onSubmit` receives: every field by name. Text and choices are strings (trimmed), a checkbox is a boolean. */
545
+ export type ContactUsValues = Record<string, ContactUsValue>
546
+ export type ContactUsStatus = "idle" | "pending" | "success" | "error"
547
+
548
+ export interface ContactUsOption {
549
+ value: string
550
+ label: string
551
+ }
552
+
553
+ export interface ContactUsFieldDef {
554
+ /** The key in `onSubmit`'s values. */
555
+ name: string
556
+ kind: "text" | "email" | "tel" | "date" | "select" | "textarea" | "checkbox"
557
+ /** The label, as Figma writes it. A required field gets no asterisk: the atom shows the word `requiredLabel` at the end of its label row. */
558
+ label: string
559
+ /** Keeps the label for assistive technology but draws none, as Editorial Statement's fields do. */
560
+ hideLabel?: boolean
561
+ placeholder?: string
562
+ required?: boolean
563
+ autoComplete?: string
564
+ /** A select's choices. */
565
+ options?: readonly ContactUsOption[]
566
+ /** Size of the field or checkbox: `sm`, `md` (the default) or `lg`. Fields are 32, 36 and 40px tall. */
567
+ size?: "sm" | "md" | "lg"
568
+ /** Corner shape of a text field or select: Figma's 8px `standard` (the default), or a `rounded` pill. */
569
+ radius?: "standard" | "rounded"
570
+ /** An icon after a text field's value. Sized to 16px by the atom. */
571
+ endIcon?: React.ReactNode
572
+ /** Classes for the field group, such as the min height Figma gives a Textarea. */
573
+ groupClassName?: string
574
+ /** A textarea's limit. The counter shows from 80% of it. */
575
+ maxLength?: number
576
+ }
577
+
578
+ /** What a block says about the outcomes of a send. Default copy comes from `contactUsFormMessages`. */
579
+ export interface ContactUsFormMessages {
580
+ /** Shown on the submit button while the message sends. */
581
+ pendingLabel: string
582
+ /** Said once the message is sent. */
583
+ success: React.ReactNode
584
+ /** Said when sending failed. */
585
+ error: React.ReactNode
586
+ /** Said under a required field left empty. */
587
+ requiredError: string
588
+ /** Said under an email field that is not an address. */
589
+ emailError: string
590
+ /** Said under a required checkbox left clear. */
591
+ consentError: string
592
+ /** The word at the end of a required field's label row, in the atom's Top Right label slot. */
593
+ requiredLabel: string
594
+ /** The thank-you panel's button, which brings the form back. */
595
+ againLabel: string
596
+ }
597
+
598
+ export const contactUsFormMessages: Record<ContactUsLocale, ContactUsFormMessages> = {
599
+ en: {
600
+ pendingLabel: "Sending",
601
+ success: "Thank you. Your message is on its way to the team.",
602
+ error: "We could not send that. Please try again.",
603
+ requiredError: "This field is required.",
604
+ emailError: "Enter a valid email address.",
605
+ consentError: "Accept this to continue.",
606
+ requiredLabel: "Required",
607
+ againLabel: "Send another message",
608
+ },
609
+ ar: {
610
+ pendingLabel: "جارٍ الإرسال",
611
+ success: "شكرًا لك. وصلت رسالتك إلى الفريق.",
612
+ error: "تعذّر الإرسال. حاول مرة أخرى.",
613
+ requiredError: "هذا الحقل مطلوب.",
614
+ emailError: "اكتب بريدًا إلكترونيًا صحيحًا.",
615
+ consentError: "وافق للمتابعة.",
616
+ requiredLabel: "مطلوب",
617
+ againLabel: "أرسل رسالة أخرى",
618
+ },
619
+ }
620
+
621
+ /** The messages for a language, with any the block was given in place of the defaults. */
622
+ export function pickFormMessages(locale: ContactUsLocale, given: Partial<ContactUsFormMessages>): ContactUsFormMessages {
623
+ const defaults = contactUsFormMessages[locale]
624
+ return {
625
+ pendingLabel: given.pendingLabel ?? defaults.pendingLabel,
626
+ success: given.success ?? defaults.success,
627
+ error: given.error ?? defaults.error,
628
+ requiredError: given.requiredError ?? defaults.requiredError,
629
+ emailError: given.emailError ?? defaults.emailError,
630
+ consentError: given.consentError ?? defaults.consentError,
631
+ requiredLabel: given.requiredLabel ?? defaults.requiredLabel,
632
+ againLabel: given.againLabel ?? defaults.againLabel,
633
+ }
634
+ }
635
+
636
+ const EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
637
+
638
+ export interface ContactUsFormState {
639
+ values: ContactUsValues
640
+ errors: Record<string, string>
641
+ status: ContactUsStatus
642
+ /** True while sending, or when the block is disabled. */
643
+ disabled: boolean
644
+ messages: ContactUsFormMessages
645
+ formRef: React.MutableRefObject<HTMLFormElement | null>
646
+ setValue: (name: string, value: ContactUsValue) => void
647
+ submit: (event: React.FormEvent) => void
648
+ reset: () => void
649
+ }
650
+
651
+ export interface UseContactUsFormOptions {
652
+ fields: readonly ContactUsFieldDef[]
653
+ messages: ContactUsFormMessages
654
+ onSubmit?: (values: ContactUsValues) => void | Promise<void>
655
+ disabled?: boolean
656
+ /** What the fields hold at first, and again after "send another". */
657
+ defaultValues?: ContactUsValues
658
+ }
659
+
660
+ /**
661
+ * The form's state machine: idle, pending, success or error. Submitting
662
+ * checks every field, puts the reader's focus on the first one that fails,
663
+ * and only then calls `onSubmit`.
664
+ */
665
+ export function useContactUsForm({ fields, messages, onSubmit, disabled = false, defaultValues }: UseContactUsFormOptions): ContactUsFormState {
666
+ const initial = React.useRef<ContactUsValues>(defaultValues ?? {})
667
+ const [values, setValues] = React.useState<ContactUsValues>(initial.current)
668
+ const [errors, setErrors] = React.useState<Record<string, string>>({})
669
+ const [status, setStatus] = React.useState<ContactUsStatus>("idle")
670
+ const formRef = React.useRef<HTMLFormElement | null>(null)
671
+ const focusFirst = React.useRef(false)
672
+ const alive = React.useRef(true)
673
+ React.useEffect(() => {
674
+ alive.current = true
675
+ return () => {
676
+ alive.current = false
677
+ }
678
+ }, [])
679
+
680
+ const setValue = React.useCallback((name: string, value: ContactUsValue) => {
681
+ setValues((current) => ({ ...current, [name]: value }))
682
+ setErrors((current) => {
683
+ if (!(name in current)) return current
684
+ const rest = { ...current }
685
+ delete rest[name]
686
+ return rest
687
+ })
688
+ setStatus((current) => (current === "error" ? "idle" : current))
689
+ }, [])
690
+
691
+ const complete = (): ContactUsValues =>
692
+ Object.fromEntries(
693
+ fields.map((field) => {
694
+ const value = values[field.name]
695
+ if (field.kind === "checkbox") return [field.name, value === true]
696
+ return [field.name, typeof value === "string" ? value.trim() : ""]
697
+ })
698
+ )
699
+
700
+ const validate = (clean: ContactUsValues) => {
701
+ const next: Record<string, string> = {}
702
+ for (const field of fields) {
703
+ const value = clean[field.name]
704
+ if (field.kind === "checkbox") {
705
+ if (field.required && value !== true) next[field.name] = messages.consentError
706
+ } else if (value === "") {
707
+ if (field.required) next[field.name] = messages.requiredError
708
+ } else if (field.kind === "email" && !EMAIL.test(String(value))) {
709
+ next[field.name] = messages.emailError
710
+ }
711
+ }
712
+ return next
713
+ }
714
+
715
+ const submit = async (event: React.FormEvent) => {
716
+ event.preventDefault()
717
+ if (status === "pending" || disabled) return
718
+ const clean = complete()
719
+ const next = validate(clean)
720
+ setErrors(next)
721
+ if (Object.keys(next).length > 0) {
722
+ focusFirst.current = true
723
+ return
724
+ }
725
+ setStatus("pending")
726
+ try {
727
+ await onSubmit?.(clean)
728
+ if (alive.current) setStatus("success")
729
+ } catch {
730
+ if (alive.current) setStatus("error")
731
+ }
732
+ }
733
+
734
+ // After a failed check, the first field that failed takes focus.
735
+ React.useEffect(() => {
736
+ if (!focusFirst.current) return
737
+ focusFirst.current = false
738
+ formRef.current?.querySelector<HTMLElement>("[data-invalid] input, [data-invalid] textarea, [data-invalid] button")?.focus()
739
+ }, [errors])
740
+
741
+ const reset = React.useCallback(() => {
742
+ setValues(initial.current)
743
+ setErrors({})
744
+ setStatus("idle")
745
+ }, [])
746
+
747
+ return { values, errors, status, disabled: disabled || status === "pending", messages, formRef, setValue, submit, reset }
748
+ }
749
+
750
+ const ContactUsFormContext = React.createContext<ContactUsFormState | null>(null)
751
+
752
+ function useFormContext() {
753
+ const form = React.useContext(ContactUsFormContext)
754
+ if (!form) throw new Error("A Contact Us field must sit inside a ContactUsForm.")
755
+ return form
756
+ }
757
+
758
+ export interface ContactUsFormProps extends Omit<React.FormHTMLAttributes<HTMLFormElement>, "onSubmit"> {
759
+ /** The state from `useContactUsForm`. */
760
+ form: ContactUsFormState
761
+ }
762
+
763
+ /**
764
+ * The `<form>`, which checks its own fields. Once the message is sent it shows
765
+ * the thank-you panel in place of its children; when sending fails it adds the
766
+ * error under them.
767
+ */
768
+ export function ContactUsForm({ form, className, children, ...props }: ContactUsFormProps) {
769
+ return (
770
+ <ContactUsFormContext.Provider value={form}>
771
+ <form ref={form.formRef} noValidate onSubmit={form.submit} aria-busy={form.status === "pending" || undefined} className={className} {...props}>
772
+ {form.status === "success" ? (
773
+ <div role="status" className="flex flex-col items-start gap-4">
774
+ <span className="flex size-10 items-center justify-center rounded-full bg-soft-success text-success">
775
+ <Check aria-hidden size={20} strokeWidth={1.5} />
776
+ </span>
777
+ <p className="text-base font-medium text-foreground">{form.messages.success}</p>
778
+ <Button type="button" variant="outline" size="md" onClick={form.reset}>
779
+ {form.messages.againLabel}
780
+ </Button>
781
+ </div>
782
+ ) : (
783
+ <>
784
+ {children}
785
+ {form.status === "error" ? (
786
+ <p role="alert" className="text-sm text-destructive">
787
+ {form.messages.error}
788
+ </p>
789
+ ) : null}
790
+ </>
791
+ )}
792
+ </form>
793
+ </ContactUsFormContext.Provider>
794
+ )
795
+ }
796
+
797
+ /** The submit button: it shows the pending label while the message sends. */
798
+ export function ContactUsSubmit({ children, ...props }: Omit<ButtonProps, "type" | "loading" | "loadingLabel">) {
799
+ const form = useFormContext()
800
+ return (
801
+ <Button type="submit" loading={form.status === "pending"} loadingLabel={form.messages.pendingLabel} disabled={form.disabled && form.status !== "pending"} {...props}>
802
+ {children}
803
+ </Button>
804
+ )
805
+ }
806
+
807
+ // Textarea's own box is 60px, with its label row 88px: a group can ask for
808
+ // more, as Figma's fixed 104px Textarea does. Tailwind reads classes as written.
809
+ export const CONTACT_US_TEXTAREA_88 = "min-h-[88px]"
810
+ export const CONTACT_US_TEXTAREA_104 = "min-h-[104px]"
811
+
812
+ /**
813
+ * A phone field. Browsers give `type=tel` a left-to-right direction of its own,
814
+ * so in an Arabic page its value and placeholder would sit on the left while
815
+ * every other field's sit on the right. The direction stays left-to-right, so
816
+ * "+971 50 123 4567" never reverses, and only the alignment follows the page.
817
+ */
818
+ const TEL_ALIGN = "rtl:[&_[data-slot=input]]:text-right"
819
+
820
+ /** A date field keeps its value as `yyyy-mm-dd`, the same text a native date input sends. */
821
+ const isoDate = (date: Date) => `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`
822
+ const parseIsoDate = (text: string): Date | null => {
823
+ const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(text)
824
+ return match ? new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3])) : null
825
+ }
826
+
827
+ /** One field of the form, drawn with the atom its `kind` names. */
828
+ export function ContactUsField({ field, className, reveal }: { field: ContactUsFieldDef; className?: string; reveal?: boolean }) {
829
+ const form = useFormContext()
830
+ const errorId = React.useId()
831
+ const error = form.errors[field.name]
832
+ const value = form.values[field.name]
833
+ const text = typeof value === "string" ? value : ""
834
+ const wrap = { "data-contact-field": field.name, "data-invalid": error ? "" : undefined, "data-reveal": reveal ? "rise" : undefined, className: cn("min-w-0", className) }
835
+ const aria = { "aria-required": field.required || undefined, "aria-label": field.hideLabel ? field.label : undefined }
836
+ // The word sits in the atom's Top Right label slot, so a field with no visible label has no slot and shows nothing. It is still required.
837
+ const requiredWord = field.required && !field.hideLabel ? form.messages.requiredLabel : undefined
838
+ const label = field.hideLabel ? undefined : field.label
839
+
840
+ if (field.kind === "select")
841
+ return (
842
+ <div {...wrap}>
843
+ <Select
844
+ name={field.name}
845
+ size={field.size ?? "md"}
846
+ radius={field.radius}
847
+ label={label}
848
+ labelEnd={requiredWord}
849
+ placeholder={field.placeholder}
850
+ items={field.options ?? []}
851
+ value={text === "" ? null : text}
852
+ onValueChange={(next) => form.setValue(field.name, next ?? "")}
853
+ disabled={form.disabled}
854
+ description={error ? <span className="text-destructive">{error}</span> : undefined}
855
+ {...aria}
856
+ />
857
+ </div>
858
+ )
859
+
860
+ if (field.kind === "textarea") {
861
+ const max = field.maxLength
862
+ const near = max !== undefined && text.length >= max * 0.8
863
+ return (
864
+ <div {...wrap}>
865
+ <Textarea
866
+ name={field.name}
867
+ label={label}
868
+ labelEnd={requiredWord}
869
+ placeholder={field.placeholder}
870
+ maxLength={max}
871
+ disabled={form.disabled}
872
+ value={text}
873
+ onChange={(event) => form.setValue(field.name, event.target.value)}
874
+ invalid={Boolean(error)}
875
+ error={error}
876
+ descriptionEnd={
877
+ near && max !== undefined ? (
878
+ <span className={text.length >= max ? "text-destructive" : text.length >= max * 0.9 ? "text-warning" : undefined}>
879
+ {text.length}/{max}
880
+ </span>
881
+ ) : undefined
882
+ }
883
+ className={field.groupClassName}
884
+ {...aria}
885
+ />
886
+ </div>
887
+ )
888
+ }
889
+
890
+ if (field.kind === "checkbox")
891
+ return (
892
+ <div {...wrap}>
893
+ <Checkbox
894
+ name={field.name}
895
+ size={field.size ?? "md"}
896
+ label={field.label}
897
+ checked={value === true}
898
+ disabled={form.disabled}
899
+ onChange={(event) => form.setValue(field.name, event.target.checked)}
900
+ aria-invalid={error ? true : undefined}
901
+ aria-describedby={error ? errorId : undefined}
902
+ {...aria}
903
+ />
904
+ {error ? (
905
+ <p id={errorId} className="mt-1 text-xs text-destructive">
906
+ {error}
907
+ </p>
908
+ ) : null}
909
+ </div>
910
+ )
911
+
912
+ if (field.kind === "date")
913
+ return (
914
+ <div {...wrap}>
915
+ <DatePicker
916
+ size={field.size ?? "md"}
917
+ label={label}
918
+ labelEnd={requiredWord}
919
+ placeholder={field.placeholder}
920
+ disabled={form.disabled}
921
+ value={parseIsoDate(text)}
922
+ onValueChange={(next) => form.setValue(field.name, next ? isoDate(next) : "")}
923
+ description={error ? <span className="text-destructive">{error}</span> : undefined}
924
+ aria-label={field.hideLabel ? field.label : undefined}
925
+ />
926
+ </div>
927
+ )
928
+
929
+ return (
930
+ <div {...wrap}>
931
+ <Input
932
+ name={field.name}
933
+ type={field.kind}
934
+ size={field.size ?? "md"}
935
+ radius={field.radius}
936
+ endIcon={field.endIcon}
937
+ label={label}
938
+ labelEnd={requiredWord}
939
+ className={cn(field.kind === "tel" && TEL_ALIGN, field.groupClassName)}
940
+ placeholder={field.placeholder}
941
+ autoComplete={field.autoComplete}
942
+ disabled={form.disabled}
943
+ value={text}
944
+ onChange={(event) => form.setValue(field.name, event.target.value)}
945
+ invalid={Boolean(error)}
946
+ error={error}
947
+ {...aria}
948
+ />
949
+ </div>
950
+ )
951
+ }
952
+
953
+ /**
954
+ * A single choice, as chips: Figma's Badge, Solid Success when chosen and
955
+ * Outline otherwise. Each chip is an `aria-pressed` button; pressing the chosen one
956
+ * clears the choice. The chosen chip's `value` is the field's value.
957
+ */
958
+ export function ContactUsChoiceGroup({ name, label, options, className }: { name: string; label: string; options: readonly ContactUsOption[]; className?: string }) {
959
+ const form = useFormContext()
960
+ const labelId = React.useId()
961
+ const chosen = form.values[name]
962
+ return (
963
+ <div role="group" aria-labelledby={labelId} className={cn("flex min-w-0 flex-col gap-3", className)}>
964
+ <p id={labelId} className="text-sm font-medium text-foreground">
965
+ {label}
966
+ </p>
967
+ <div className="flex flex-wrap gap-2">
968
+ {options.map((option) => {
969
+ const pressed = chosen === option.value
970
+ return (
971
+ <button
972
+ key={option.value}
973
+ type="button"
974
+ aria-pressed={pressed}
975
+ disabled={form.disabled}
976
+ onClick={() => form.setValue(name, pressed ? "" : option.value)}
977
+ className={cn(
978
+ badgeVariants({ variant: pressed ? "solid" : "outline", tone: "primary", size: "lg", radius: "rounded" }),
979
+ "cursor-pointer focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 motion-safe:transition-[transform,background-color,color] motion-safe:duration-[120ms] active:motion-safe:scale-[0.97]"
980
+ )}
981
+ >
982
+ {option.label}
983
+ </button>
984
+ )
985
+ })}
986
+ </div>
987
+ </div>
988
+ )
989
+ }