@businessdash/sdk 0.9.80 → 0.9.90

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 (264) hide show
  1. package/README.md +133 -949
  2. package/assets/{biab-sw.js → bd-sw.js} +2 -2
  3. package/dist/account-core/index.d.ts +1 -1
  4. package/dist/account-core/index.d.ts.map +1 -1
  5. package/dist/adapters-revalidate.d.ts +2 -2
  6. package/dist/adapters-revalidate.js +2 -2
  7. package/dist/adapters-revalidate.js.map +1 -1
  8. package/dist/analytics-core.d.ts +6 -6
  9. package/dist/analytics-core.d.ts.map +1 -1
  10. package/dist/analytics-core.js +4 -4
  11. package/dist/analytics-core.js.map +1 -1
  12. package/dist/analytics-server.d.ts +6 -6
  13. package/dist/analytics-server.d.ts.map +1 -1
  14. package/dist/analytics-server.js +5 -5
  15. package/dist/auth-handler.d.ts +14 -14
  16. package/dist/auth-handler.d.ts.map +1 -1
  17. package/dist/auth-handler.js +16 -16
  18. package/dist/auth-handler.js.map +1 -1
  19. package/dist/auth-links.d.ts +2 -2
  20. package/dist/auth-links.d.ts.map +1 -1
  21. package/dist/auth-links.js +2 -2
  22. package/dist/auth-links.js.map +1 -1
  23. package/dist/{biab-forms.css → bd-forms.css} +158 -158
  24. package/dist/cli.d.ts +4 -4
  25. package/dist/cli.js +78 -78
  26. package/dist/cli.js.map +1 -1
  27. package/dist/client.d.ts +158 -147
  28. package/dist/client.d.ts.map +1 -1
  29. package/dist/client.js +156 -137
  30. package/dist/client.js.map +1 -1
  31. package/dist/consent-core/index.cjs +2 -2
  32. package/dist/consent-core/index.d.ts +2 -2
  33. package/dist/consent-core/index.d.ts.map +1 -1
  34. package/dist/consent-core/index.js +10 -8
  35. package/dist/consent-core/index.js.map +1 -1
  36. package/dist/contracts.d.ts +411 -52
  37. package/dist/contracts.d.ts.map +1 -1
  38. package/dist/contracts.js +89 -15
  39. package/dist/contracts.js.map +1 -1
  40. package/dist/data-model-schema.d.ts +15 -15
  41. package/dist/data-model-schema.d.ts.map +1 -1
  42. package/dist/data-model-schema.js +19 -17
  43. package/dist/data-model-schema.js.map +1 -1
  44. package/dist/distribution.d.ts +5 -5
  45. package/dist/distribution.d.ts.map +1 -1
  46. package/dist/distribution.js +4 -4
  47. package/dist/element.d.ts +36 -36
  48. package/dist/element.d.ts.map +1 -1
  49. package/dist/element.js +43 -43
  50. package/dist/element.js.map +1 -1
  51. package/dist/embed-protocol.d.ts +11 -11
  52. package/dist/embed-protocol.d.ts.map +1 -1
  53. package/dist/embed-protocol.js +1 -1
  54. package/dist/embed-protocol.js.map +1 -1
  55. package/dist/errors.d.ts +15 -15
  56. package/dist/errors.d.ts.map +1 -1
  57. package/dist/errors.js +18 -18
  58. package/dist/errors.js.map +1 -1
  59. package/dist/forms-core/autocomplete.d.ts +3 -3
  60. package/dist/forms-core/autocomplete.d.ts.map +1 -1
  61. package/dist/forms-core/autocomplete.js +2 -2
  62. package/dist/forms-core/controller.d.ts +10 -10
  63. package/dist/forms-core/controller.d.ts.map +1 -1
  64. package/dist/forms-core/controller.js +4 -4
  65. package/dist/forms-core/controller.js.map +1 -1
  66. package/dist/forms-core/elements.d.ts +4 -4
  67. package/dist/forms-core/elements.d.ts.map +1 -1
  68. package/dist/forms-core/elements.js +7 -7
  69. package/dist/forms-core/elements.js.map +1 -1
  70. package/dist/forms-core/formula.d.ts +1 -1
  71. package/dist/forms-core/formula.js +1 -1
  72. package/dist/forms-core/index.d.ts +6 -6
  73. package/dist/forms-core/index.d.ts.map +1 -1
  74. package/dist/forms-core/index.js +5 -5
  75. package/dist/forms-core/index.js.map +1 -1
  76. package/dist/forms-core/units.d.ts +1 -1
  77. package/dist/forms-core/units.js +1 -1
  78. package/dist/forms-core/us-states.d.ts +1 -1
  79. package/dist/forms-core/us-states.js +1 -1
  80. package/dist/forms-core/validation.d.ts +12 -3
  81. package/dist/forms-core/validation.d.ts.map +1 -1
  82. package/dist/forms-core/validation.js +152 -17
  83. package/dist/forms-core/validation.js.map +1 -1
  84. package/dist/forms.d.ts +2 -2
  85. package/dist/forms.d.ts.map +1 -1
  86. package/dist/forms.js +2 -2
  87. package/dist/forms.js.map +1 -1
  88. package/dist/framework-support.js +1 -1
  89. package/dist/framework-support.js.map +1 -1
  90. package/dist/gallery-types.d.ts +1 -1
  91. package/dist/graphql.d.ts +3 -3
  92. package/dist/graphql.d.ts.map +1 -1
  93. package/dist/graphql.js +3 -3
  94. package/dist/graphql.js.map +1 -1
  95. package/dist/index.cjs +351 -272
  96. package/dist/index.d.cts +5 -5
  97. package/dist/index.d.mts +5 -5
  98. package/dist/index.d.ts +5 -5
  99. package/dist/index.d.ts.map +1 -1
  100. package/dist/index.js +5 -5
  101. package/dist/index.js.map +1 -1
  102. package/dist/legal-core/index.cjs +8 -8
  103. package/dist/legal-core/index.d.ts +3 -3
  104. package/dist/legal-core/index.d.ts.map +1 -1
  105. package/dist/legal-core/index.js +11 -11
  106. package/dist/legal-core/index.js.map +1 -1
  107. package/dist/marketing-core/index.d.ts +51 -1
  108. package/dist/marketing-core/index.d.ts.map +1 -1
  109. package/dist/marketing-core/index.js +35 -0
  110. package/dist/marketing-core/index.js.map +1 -1
  111. package/dist/marketing-schema.d.ts +9 -9
  112. package/dist/marketing-schema.d.ts.map +1 -1
  113. package/dist/marketing-schema.js +1 -1
  114. package/dist/mcp.d.ts +4 -4
  115. package/dist/mcp.js +4 -4
  116. package/dist/native.d.ts +6 -6
  117. package/dist/native.d.ts.map +1 -1
  118. package/dist/native.js +4 -4
  119. package/dist/native.js.map +1 -1
  120. package/dist/next-revalidate.d.ts +8 -8
  121. package/dist/next-revalidate.d.ts.map +1 -1
  122. package/dist/next-revalidate.js +7 -7
  123. package/dist/next-revalidate.js.map +1 -1
  124. package/dist/notifications-core/index.d.ts +1 -1
  125. package/dist/notifications-core/index.js +1 -1
  126. package/dist/parallel-pages.d.ts +4 -4
  127. package/dist/parallel-pages.d.ts.map +1 -1
  128. package/dist/parallel-pages.js +5 -5
  129. package/dist/parallel-pages.js.map +1 -1
  130. package/dist/portal-core/index.d.ts +1 -1
  131. package/dist/portal-core/index.js +1 -1
  132. package/dist/proxy.d.ts +18 -18
  133. package/dist/proxy.d.ts.map +1 -1
  134. package/dist/proxy.js +10 -10
  135. package/dist/proxy.js.map +1 -1
  136. package/dist/push-core/index.cjs +6 -2
  137. package/dist/push-core/index.d.ts +1 -1
  138. package/dist/push-core/index.d.ts.map +1 -1
  139. package/dist/push-core/index.js +6 -2
  140. package/dist/push-core/index.js.map +1 -1
  141. package/dist/react-analytics.d.ts +14 -14
  142. package/dist/react-analytics.d.ts.map +1 -1
  143. package/dist/react-analytics.js +15 -15
  144. package/dist/react-analytics.js.map +1 -1
  145. package/dist/react-attribution.d.ts +2 -2
  146. package/dist/react-attribution.d.ts.map +1 -1
  147. package/dist/react-attribution.js +1 -1
  148. package/dist/react-marketing.js +5 -5
  149. package/dist/react-marketing.js.map +1 -1
  150. package/dist/react.d.ts +38 -61
  151. package/dist/react.d.ts.map +1 -1
  152. package/dist/react.js +247 -261
  153. package/dist/react.js.map +1 -1
  154. package/dist/reserved-paths.d.ts +2 -2
  155. package/dist/reserved-paths.js +3 -3
  156. package/dist/reserved-paths.js.map +1 -1
  157. package/dist/revalidate-verify.d.ts +3 -3
  158. package/dist/revalidate-verify.d.ts.map +1 -1
  159. package/dist/revalidate-verify.js +1 -1
  160. package/dist/revalidate-verify.js.map +1 -1
  161. package/dist/sdk.d.ts +51 -45
  162. package/dist/sdk.d.ts.map +1 -1
  163. package/dist/sdk.js +48 -50
  164. package/dist/sdk.js.map +1 -1
  165. package/dist/seed-core/index.js +8 -8
  166. package/dist/seed-core/index.js.map +1 -1
  167. package/dist/seo-core/blog.d.ts +1 -1
  168. package/dist/seo-core/blog.js +1 -1
  169. package/dist/seo-core/blog.js.map +1 -1
  170. package/dist/seo-core/index.cjs +2 -2
  171. package/dist/seo-core/index.js +1 -1
  172. package/dist/seo-core/index.js.map +1 -1
  173. package/dist/seo.d.ts +1 -1
  174. package/dist/seo.d.ts.map +1 -1
  175. package/dist/seo.js +1 -1
  176. package/dist/seo.js.map +1 -1
  177. package/dist/sitemap-core/index.cjs +5 -5
  178. package/dist/sitemap-core/index.d.ts +13 -5
  179. package/dist/sitemap-core/index.d.ts.map +1 -1
  180. package/dist/sitemap-core/index.js +4 -4
  181. package/dist/sitemap-core/index.js.map +1 -1
  182. package/dist/socials.d.ts +6 -6
  183. package/dist/socials.d.ts.map +1 -1
  184. package/dist/socials.js +5 -5
  185. package/dist/socials.js.map +1 -1
  186. package/dist/solid-compat.d.ts +1 -1
  187. package/dist/solid-compat.js +4 -4
  188. package/dist/solid-compat.js.map +1 -1
  189. package/dist/store-core/index.d.ts +1 -1
  190. package/dist/vanilla.d.ts +12 -12
  191. package/dist/vanilla.d.ts.map +1 -1
  192. package/dist/vanilla.js +192 -145
  193. package/dist/vanilla.js.map +1 -1
  194. package/openapi.json +264 -1
  195. package/package.json +8 -8
  196. package/src/account-core/index.ts +1 -1
  197. package/src/adapters-revalidate.ts +3 -3
  198. package/src/analytics-core.ts +8 -8
  199. package/src/analytics-server.ts +6 -6
  200. package/src/angular/{biab-form.component.ts → bd-form.component.ts} +269 -161
  201. package/src/angular/headless.ts +12 -12
  202. package/src/angular/index.ts +9 -9
  203. package/src/auth-handler.ts +22 -22
  204. package/src/auth-links.ts +2 -2
  205. package/src/cli.ts +81 -81
  206. package/src/client.ts +218 -193
  207. package/src/consent-core/index.ts +10 -8
  208. package/src/contracts.ts +99 -15
  209. package/src/data-model-schema.ts +29 -26
  210. package/src/distribution.ts +5 -5
  211. package/src/element.ts +54 -54
  212. package/src/embed-protocol.ts +14 -14
  213. package/src/errors.ts +27 -27
  214. package/src/forms-core/autocomplete.ts +3 -3
  215. package/src/forms-core/controller.ts +12 -12
  216. package/src/forms-core/elements.ts +7 -7
  217. package/src/forms-core/formula.ts +1 -1
  218. package/src/forms-core/index.ts +8 -8
  219. package/src/forms-core/units.ts +1 -1
  220. package/src/forms-core/us-states.ts +1 -1
  221. package/src/forms-core/validation.ts +158 -14
  222. package/src/forms.ts +2 -2
  223. package/src/framework-support.ts +1 -1
  224. package/src/gallery-types.ts +1 -1
  225. package/src/graphql.ts +4 -4
  226. package/src/index.ts +38 -38
  227. package/src/legal-core/index.ts +12 -12
  228. package/src/marketing-core/index.ts +62 -0
  229. package/src/marketing-schema.ts +9 -9
  230. package/src/mcp.ts +4 -4
  231. package/src/native.ts +14 -14
  232. package/src/next-revalidate.ts +10 -10
  233. package/src/notifications-core/index.ts +1 -1
  234. package/src/parallel-pages.ts +6 -6
  235. package/src/portal-core/index.ts +1 -1
  236. package/src/proxy.ts +28 -28
  237. package/src/push-core/index.ts +11 -5
  238. package/src/qwik.tsx +262 -166
  239. package/src/react-analytics.tsx +17 -17
  240. package/src/react-attribution.tsx +2 -2
  241. package/src/react-marketing.tsx +5 -5
  242. package/src/react.tsx +541 -500
  243. package/src/reserved-paths.ts +3 -3
  244. package/src/revalidate-verify.ts +5 -5
  245. package/src/sdk.ts +131 -101
  246. package/src/seed-core/index.ts +8 -8
  247. package/src/seo-core/blog.ts +2 -2
  248. package/src/seo-core/index.ts +1 -1
  249. package/src/seo.ts +1 -1
  250. package/src/sitemap-core/index.ts +17 -9
  251. package/src/socials.ts +6 -6
  252. package/src/solid-compat.tsx +5 -5
  253. package/src/solid-surfaces.tsx +28 -28
  254. package/src/solid.tsx +264 -175
  255. package/src/store-core/index.ts +1 -1
  256. package/src/styles/{biab-forms.css → bd-forms.css} +158 -158
  257. package/src/svelte/{BiabForm.svelte → BdForm.svelte} +240 -173
  258. package/src/svelte/context.ts +16 -16
  259. package/src/svelte/headless.ts +10 -10
  260. package/src/svelte/index.ts +16 -16
  261. package/src/vanilla.ts +250 -169
  262. package/src/vue/{BiabForm.vue → BdForm.vue} +270 -157
  263. package/src/vue/headless.ts +11 -11
  264. package/src/vue/index.ts +26 -26
package/src/react.tsx CHANGED
@@ -10,7 +10,7 @@
10
10
  * WHY THE DIRECTIVE IS HERE
11
11
  *
12
12
  * Without it, a React Server Components framework treats this module as server
13
- * code by default, and importing `<BiabForm>` into a page throws "you're
13
+ * code by default, and importing `<BdForm>` into a page throws "you're
14
14
  * importing a component that needs useState". The only way out is for every
15
15
  * consumer to hand-write a `"use client"` wrapper per component — which is
16
16
  * exactly what the Next App Router starter had to do, sixteen times, for
@@ -40,45 +40,27 @@ import {
40
40
  createFollowersController,
41
41
  createUserController,
42
42
  } from "./account-core/index.js";
43
- import {
44
- buildAuthHref,
45
- clearLocalFollow,
46
- DEFAULT_AUTH_BASE_PATH,
47
- followerStoreKey,
48
- type LocalFollow,
49
- readLocalFollow,
50
- writeLocalFollow,
51
- } from "./auth-links.js";
43
+ import { buildAuthHref, DEFAULT_AUTH_BASE_PATH } from "./auth-links.js";
52
44
  import { createBlogController } from "./blog-core/index.js";
53
45
  import { createChatController } from "./chat-core/index.js";
54
46
  import { createCheckoutController } from "./checkout-core/index.js";
55
- import { BiabDevClient } from "./client.js";
47
+ import { BdApiClient } from "./client.js";
56
48
  import type {
57
49
  BlogSessionInput,
58
50
  BlogSessionResponse,
59
51
  CartSessionResponse,
60
52
  ChatbotAvailability,
61
- ChatbotChatInput,
62
- ChatbotChatResponse,
63
- ChatbotConfig,
64
53
  ChatbotFormDefinition,
65
- ChatbotFormSubmitInput,
66
- ChatbotFormSubmitResponse,
67
- ChatbotMessage,
68
54
  ChatbotSessionResponse,
69
- ChatbotUiAction,
70
55
  CheckoutSessionInput,
71
56
  CheckoutSessionResponse,
72
57
  DashboardSessionResponse,
73
58
  GetSiteMarketingPageResponse,
74
- ListSiteMarketingPagesResponse,
75
- SiteMarketingPage,
76
- SiteMarketingPageSummary,
77
59
  StorefrontSessionInput,
78
60
  StorefrontSessionResponse,
79
61
  } from "./contracts.js";
80
62
  import {
81
- type BiabEmbedEvent,
63
+ type BdEmbedEvent,
82
64
  DEFAULT_EMBED_SANDBOX,
83
65
  DEFAULT_REFRESH_LEAD_MS,
84
66
  type EmbedSessionState,
@@ -87,7 +69,7 @@ import {
87
69
  } from "./embed-protocol.js";
88
70
  import {
89
71
  type AutocompleteConfig,
90
- type BiabFormsClient,
72
+ type BdFormsClient,
91
73
  buildAcceptAttr,
92
74
  type CalculationConfig,
93
75
  type CreateFormControllerOptions,
@@ -117,10 +99,16 @@ import {
117
99
  unitsForCategory,
118
100
  } from "./forms-core/units.js";
119
101
  import { US_STATES } from "./forms-core/us-states.js";
120
- import { createMarketingController } from "./marketing-core/index.js";
102
+ import {
103
+ createMarketingController,
104
+ type MarketingListView,
105
+ type MarketingPageView,
106
+ marketingListView,
107
+ marketingPageView,
108
+ } from "./marketing-core/index.js";
121
109
  import { createPortalController } from "./portal-core/index.js";
122
110
  import type { FormFileValue, FormSchema, FormSubmitResult } from "./sdk.js";
123
- import { createBiabClient } from "./sdk.js";
111
+ import { createBdClient } from "./sdk.js";
124
112
  import {
125
113
  resolveSocialLinks,
126
114
  SOCIAL_FALLBACK_GLYPH,
@@ -138,7 +126,7 @@ import {
138
126
  type SessionState<T> = EmbedSessionState<T>;
139
127
 
140
128
  function useEmbedSession<T extends { url: string; expiresAt: string }>(
141
- loader: (client: BiabDevClient) => Promise<T>,
129
+ loader: (client: BdApiClient) => Promise<T>,
142
130
  options: {
143
131
  apiKey: string;
144
132
  baseUrl: string;
@@ -147,7 +135,7 @@ function useEmbedSession<T extends { url: string; expiresAt: string }>(
147
135
  ) {
148
136
  const { apiKey, baseUrl, refreshLeadMs = DEFAULT_REFRESH_LEAD_MS } = options;
149
137
  const client = useMemo(
150
- () => new BiabDevClient({ apiKey, baseUrl }),
138
+ () => new BdApiClient({ apiKey, baseUrl }),
151
139
  [apiKey, baseUrl],
152
140
  );
153
141
  const [state, setState] = useState<SessionState<T>>({ status: "idle" });
@@ -200,11 +188,11 @@ function useEmbedSession<T extends { url: string; expiresAt: string }>(
200
188
  * that guards it live together. Kept exported here so existing
201
189
  * `@businessdash/sdk/react` imports keep working.
202
190
  */
203
- export type { BiabEmbedEvent } from "./embed-protocol.js";
191
+ export type { BdEmbedEvent } from "./embed-protocol.js";
204
192
 
205
193
  function useEmbedMessages(
206
194
  iframeRef: React.RefObject<HTMLIFrameElement | null>,
207
- handler: (event: BiabEmbedEvent) => void,
195
+ handler: (event: BdEmbedEvent) => void,
208
196
  ) {
209
197
  // The listener, and the origin check inside it, live in
210
198
  // `embed-protocol.ts` so the Solid binding enforces exactly the same rule.
@@ -341,7 +329,7 @@ type EmbedIframeProps<T extends { url: string }> = {
341
329
  errorFallback?:
342
330
  | ((error: Error, refresh: () => void) => ReactNode)
343
331
  | undefined;
344
- onEvent?: ((event: BiabEmbedEvent) => void) | undefined;
332
+ onEvent?: ((event: BdEmbedEvent) => void) | undefined;
345
333
  /** Extra `sandbox` tokens beyond the safe defaults. */
346
334
  extraSandbox?: string | undefined;
347
335
  };
@@ -416,7 +404,7 @@ export type StorefrontProps = {
416
404
  loadingFallback?: ReactNode;
417
405
  errorFallback?: (error: Error, refresh: () => void) => ReactNode;
418
406
  /** Fires for every event the embed posts. */
419
- onEvent?: (event: BiabEmbedEvent) => void;
407
+ onEvent?: (event: BdEmbedEvent) => void;
420
408
  /** Convenience: fires only on cart updates. */
421
409
  onCartChange?: (count: number, subtotalCents: number) => void;
422
410
  refreshLeadMs?: number;
@@ -444,9 +432,9 @@ export function Storefront({
444
432
  { apiKey, baseUrl, refreshLeadMs },
445
433
  );
446
434
  const handleEvent = useCallback(
447
- (event: BiabEmbedEvent) => {
435
+ (event: BdEmbedEvent) => {
448
436
  onEvent?.(event);
449
- if (event.type === "biab:cart:update") {
437
+ if (event.type === "bd:cart:update") {
450
438
  onCartChange?.(event.count, event.subtotalCents);
451
439
  }
452
440
  },
@@ -476,7 +464,7 @@ export type CartProps = {
476
464
  title?: string;
477
465
  loadingFallback?: ReactNode;
478
466
  errorFallback?: (error: Error, refresh: () => void) => ReactNode;
479
- onEvent?: (event: BiabEmbedEvent) => void;
467
+ onEvent?: (event: BdEmbedEvent) => void;
480
468
  onCartChange?: (count: number, subtotalCents: number) => void;
481
469
  refreshLeadMs?: number;
482
470
  };
@@ -498,9 +486,9 @@ export function Cart({
498
486
  { apiKey, baseUrl, refreshLeadMs },
499
487
  );
500
488
  const handleEvent = useCallback(
501
- (event: BiabEmbedEvent) => {
489
+ (event: BdEmbedEvent) => {
502
490
  onEvent?.(event);
503
- if (event.type === "biab:cart:update") {
491
+ if (event.type === "bd:cart:update") {
504
492
  onCartChange?.(event.count, event.subtotalCents);
505
493
  }
506
494
  },
@@ -536,7 +524,7 @@ export type CheckoutProps = {
536
524
  children?: ReactNode;
537
525
  loadingFallback?: ReactNode;
538
526
  errorFallback?: (error: Error, refresh: () => void) => ReactNode;
539
- onEvent?: (event: BiabEmbedEvent) => void;
527
+ onEvent?: (event: BdEmbedEvent) => void;
540
528
  onSuccess?: (sessionId: string) => void;
541
529
  onCancel?: (sessionId: string) => void;
542
530
  onError?: (error: Error) => void;
@@ -561,7 +549,7 @@ export function Checkout(props: CheckoutProps) {
561
549
  } = props;
562
550
 
563
551
  const client = useMemo(
564
- () => new BiabDevClient({ apiKey, baseUrl }),
552
+ () => new BdApiClient({ apiKey, baseUrl }),
565
553
  [apiKey, baseUrl],
566
554
  );
567
555
 
@@ -585,12 +573,11 @@ export function Checkout(props: CheckoutProps) {
585
573
  }, [client, cart, mode, onError]);
586
574
 
587
575
  const handleEvent = useCallback(
588
- (event: BiabEmbedEvent) => {
576
+ (event: BdEmbedEvent) => {
589
577
  onEvent?.(event);
590
- if (event.type === "biab:checkout:success") onSuccess?.(event.sessionId);
591
- else if (event.type === "biab:checkout:cancel")
592
- onCancel?.(event.sessionId);
593
- else if (event.type === "biab:checkout:error")
578
+ if (event.type === "bd:checkout:success") onSuccess?.(event.sessionId);
579
+ else if (event.type === "bd:checkout:cancel") onCancel?.(event.sessionId);
580
+ else if (event.type === "bd:checkout:error")
594
581
  onError?.(new Error(event.message));
595
582
  },
596
583
  [onEvent, onSuccess, onCancel, onError],
@@ -674,7 +661,7 @@ export function CheckoutResult(props: CheckoutResultProps) {
674
661
  setState({ status: "error", error: new Error("Missing session id.") });
675
662
  return;
676
663
  }
677
- const client = new BiabDevClient({ apiKey, baseUrl });
664
+ const client = new BdApiClient({ apiKey, baseUrl });
678
665
  // We hit the *public* tRPC endpoint via fetch — no API key required.
679
666
  const trpcUrl = baseUrl.replace(/\/api\/package\/v1\/?$/, "");
680
667
  const requestUrl = `${trpcUrl}/api/trpc/store.checkoutSessionStatus?input=${encodeURIComponent(
@@ -743,7 +730,7 @@ export type BlogProps = {
743
730
  title?: string;
744
731
  loadingFallback?: ReactNode;
745
732
  errorFallback?: (error: Error, refresh: () => void) => ReactNode;
746
- onEvent?: (event: BiabEmbedEvent) => void;
733
+ onEvent?: (event: BdEmbedEvent) => void;
747
734
  refreshLeadMs?: number;
748
735
  };
749
736
 
@@ -757,7 +744,7 @@ export type ChatbotProps = {
757
744
  title?: string;
758
745
  loadingFallback?: ReactNode;
759
746
  errorFallback?: (error: Error, refresh: () => void) => ReactNode;
760
- onEvent?: (event: BiabEmbedEvent) => void;
747
+ onEvent?: (event: BdEmbedEvent) => void;
761
748
  /** Convenience: fires when the assistant replies. */
762
749
  onAssistantMessage?: (content: string, model?: string) => void;
763
750
  refreshLeadMs?: number;
@@ -780,9 +767,9 @@ export function Chatbot({
780
767
  { apiKey, baseUrl, refreshLeadMs },
781
768
  );
782
769
  const handleEvent = useCallback(
783
- (event: BiabEmbedEvent) => {
770
+ (event: BdEmbedEvent) => {
784
771
  onEvent?.(event);
785
- if (event.type === "biab:chatbot:assistant-message") {
772
+ if (event.type === "bd:chatbot:assistant-message") {
786
773
  onAssistantMessage?.(event.content, event.model);
787
774
  }
788
775
  },
@@ -817,7 +804,7 @@ export function useChatbot(options: { apiKey: string; baseUrl: string }) {
817
804
  const controller = useMemo(
818
805
  () =>
819
806
  createChatController({
820
- client: new BiabDevClient({ apiKey, baseUrl }),
807
+ client: new BdApiClient({ apiKey, baseUrl }),
821
808
  }),
822
809
  [apiKey, baseUrl],
823
810
  );
@@ -832,7 +819,7 @@ export function useChatbot(options: { apiKey: string; baseUrl: string }) {
832
819
 
833
820
  const uploadFile = useCallback(
834
821
  (args: { sessionId: string; file: File; uploaderEmail?: string | null }) =>
835
- new BiabDevClient({ apiKey, baseUrl }).chatbot.uploadFile(args),
822
+ new BdApiClient({ apiKey, baseUrl }).chatbot.uploadFile(args),
836
823
  [apiKey, baseUrl],
837
824
  );
838
825
 
@@ -876,7 +863,7 @@ export function useStorefront(options: {
876
863
  const controller = useMemo(
877
864
  () =>
878
865
  createStoreController({
879
- client: new BiabDevClient({ apiKey, baseUrl }),
866
+ client: new BdApiClient({ apiKey, baseUrl }),
880
867
  ...(pageSize ? { pageSize } : {}),
881
868
  ...(categoryId ? { categoryId } : {}),
882
869
  }),
@@ -1009,52 +996,52 @@ const inlineLayoutStyles = {
1009
996
  * Opt-in default visual theme for {@link ChatbotInline}. Every rule is wrapped
1010
997
  * in `:where(…)` so it has **zero specificity** — any class a consumer passes
1011
998
  * via `classNames` (or their global CSS) overrides it without `!important`.
1012
- * Scoped to the `[data-biab-chatbot]` root (set only when `unstyled` is false),
999
+ * Scoped to the `[data-bd-chatbot]` root (set only when `unstyled` is false),
1013
1000
  * and targets the per-slot `data-slot` attributes the component stamps on each
1014
1001
  * element. Injected once into `<head>` (id-guarded). This is what makes the
1015
1002
  * inline component a polished drop-in instead of bare divs, while preserving
1016
1003
  * the headless contract for anyone who wants to style it themselves.
1017
1004
  */
1018
- const CHATBOT_INLINE_STYLE_ID = "biab-chatbot-inline-styles";
1005
+ const CHATBOT_INLINE_STYLE_ID = "bd-chatbot-inline-styles";
1019
1006
  const CHATBOT_INLINE_CSS = `
1020
- :where([data-biab-chatbot]){font-size:14px;color:inherit;background:var(--biab-chatbot-bg,Canvas);border:1px solid color-mix(in srgb,currentColor 12%,transparent);border-radius:16px;}
1021
- :where([data-biab-chatbot] [data-slot="header"]){padding:12px 16px;border-bottom:1px solid color-mix(in srgb,currentColor 10%,transparent);}
1022
- :where([data-biab-chatbot] [data-slot="header-title"]){font-weight:600;}
1023
- :where([data-biab-chatbot] [data-slot="header-subtitle"]){font-size:12px;opacity:.6;margin-top:2px;}
1024
- :where([data-biab-chatbot] [data-slot="presence-strip"]){display:flex;align-items:center;justify-content:space-between;gap:8px;padding:8px 16px;font-size:12px;border-bottom:1px solid color-mix(in srgb,currentColor 8%,transparent);opacity:.85;}
1025
- :where([data-biab-chatbot] [data-slot="message-list"]){padding:16px;gap:12px;}
1026
- :where([data-biab-chatbot] [data-slot="message-row"]){max-width:85%;width:fit-content;padding:8px 12px;border-radius:16px;line-height:1.5;white-space:pre-wrap;overflow-wrap:anywhere;box-shadow:0 1px 2px color-mix(in srgb,currentColor 8%,transparent);}
1027
- :where([data-biab-chatbot] [data-slot="message-row"][data-role="user"]){align-self:flex-end;background:var(--biab-chatbot-accent,#0d9488);color:#fff;border-bottom-right-radius:4px;}
1028
- :where([data-biab-chatbot] [data-slot="message-row"][data-role="assistant"]){align-self:flex-start;background:color-mix(in srgb,currentColor 8%,transparent);border-bottom-left-radius:4px;}
1029
- :where([data-biab-chatbot] [data-slot="message-pending"]){align-self:flex-start;width:fit-content;padding:8px 12px;background-image:linear-gradient(90deg,currentColor 35%,color-mix(in srgb,currentColor 25%,transparent) 50%,currentColor 65%);background-size:200% 100%;-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;animation:biab-chatbot-shimmer 2s linear infinite;}
1030
- :where([data-biab-chatbot] [data-slot="error-text"]){align-self:center;font-size:12px;color:#dc2626;}
1031
- :where([data-biab-chatbot] [data-slot="input-bar"]){gap:8px;padding:12px;border-top:1px solid color-mix(in srgb,currentColor 10%,transparent);}
1032
- :where([data-biab-chatbot] [data-slot="input-field"]){padding:8px 12px;border-radius:10px;border:1px solid color-mix(in srgb,currentColor 15%,transparent);background:transparent;color:inherit;font:inherit;outline:none;}
1033
- :where([data-biab-chatbot] [data-slot="input-field"]:focus-visible){border-color:var(--biab-chatbot-accent,#0d9488);}
1034
- :where([data-biab-chatbot] [data-slot="send-button"]){padding:8px 14px;border-radius:10px;border:none;background:var(--biab-chatbot-accent,#0d9488);color:#fff;font-weight:500;cursor:pointer;}
1035
- :where([data-biab-chatbot] [data-slot="send-button"]:disabled){opacity:.5;cursor:default;}
1036
- :where([data-biab-chatbot] [data-slot="upload-button"]){display:inline-flex;align-items:center;justify-content:center;padding:0 6px;border:none;background:transparent;color:inherit;font-size:18px;line-height:1;cursor:pointer;opacity:.7;}
1037
- :where([data-biab-chatbot] [data-slot="upload-button"]:hover){opacity:1;}
1038
- :where([data-biab-chatbot] [data-slot="upload-button"]:disabled){opacity:.4;cursor:default;}
1039
- :where([data-biab-chatbot] [data-slot="message-file"]){align-self:flex-end;max-width:85%;padding:8px 12px;border-radius:16px;border-bottom-right-radius:4px;background:var(--biab-chatbot-accent,#0d9488);color:#fff;text-decoration:none;font-size:13px;word-break:break-all;}
1040
- :where([data-biab-chatbot] [data-slot="message-image"]){align-self:flex-end;max-width:85%;border-radius:12px;overflow:hidden;}
1041
- :where([data-biab-chatbot] [data-slot="jump-latest"]){position:absolute;left:50%;bottom:72px;transform:translateX(-50%);display:inline-flex;align-items:center;gap:4px;padding:6px 12px;border-radius:999px;border:1px solid color-mix(in srgb,currentColor 15%,transparent);background:var(--biab-chatbot-bg,Canvas);font-size:12px;font-weight:500;cursor:pointer;box-shadow:0 2px 10px color-mix(in srgb,currentColor 20%,transparent);}
1042
- :where([data-biab-chatbot] [data-slot="form-card"]){display:flex;flex-direction:column;gap:10px;align-self:stretch;padding:14px;border-radius:14px;border:1px solid color-mix(in srgb,currentColor 14%,transparent);background:color-mix(in srgb,currentColor 4%,transparent);}
1043
- :where([data-biab-chatbot] [data-slot="form-title"]){font-weight:600;}
1044
- :where([data-biab-chatbot] [data-slot="form-description"]){font-size:12px;opacity:.7;}
1045
- :where([data-biab-chatbot] [data-slot="form-field"]){gap:4px;}
1046
- :where([data-biab-chatbot] [data-slot="form-label"]){font-size:12px;font-weight:500;opacity:.85;}
1047
- :where([data-biab-chatbot] [data-slot="form-input"],[data-biab-chatbot] [data-slot="form-textarea"],[data-biab-chatbot] [data-slot="form-select"]){padding:8px 10px;border-radius:8px;border:1px solid color-mix(in srgb,currentColor 15%,transparent);background:transparent;color:inherit;font:inherit;outline:none;}
1048
- :where([data-biab-chatbot] [data-slot="form-input"]:focus-visible,[data-biab-chatbot] [data-slot="form-textarea"]:focus-visible,[data-biab-chatbot] [data-slot="form-select"]:focus-visible){border-color:var(--biab-chatbot-accent,#0d9488);}
1049
- :where([data-biab-chatbot] [data-slot="form-submit"]){padding:8px 14px;border-radius:8px;border:none;background:var(--biab-chatbot-accent,#0d9488);color:#fff;font-weight:500;cursor:pointer;}
1050
- :where([data-biab-chatbot] [data-slot="form-submit"]:disabled){opacity:.5;cursor:default;}
1051
- :where([data-biab-chatbot] [data-slot="form-error"]){font-size:12px;color:#dc2626;}
1052
- :where([data-biab-chatbot] [data-slot="form-receipt"]){font-size:13px;opacity:.8;}
1053
- @keyframes biab-chatbot-shimmer{from{background-position:100% 0;}to{background-position:0 0;}}
1054
- @media (prefers-reduced-motion:reduce){:where([data-biab-chatbot] [data-slot="message-pending"]){animation:none;-webkit-text-fill-color:currentColor;}}
1055
- @property --biab-chatbot-fade{syntax:"<length>";inherits:false;initial-value:0px;}
1056
- @keyframes biab-chatbot-fade-b{from{--biab-chatbot-fade:24px;}to{--biab-chatbot-fade:0px;}}
1057
- @supports (animation-timeline:scroll()){:where([data-biab-chatbot] [data-slot="message-list"]){-webkit-mask-image:linear-gradient(to bottom,#000 0,#000 calc(100% - var(--biab-chatbot-fade,0px)),transparent 100%);mask-image:linear-gradient(to bottom,#000 0,#000 calc(100% - var(--biab-chatbot-fade,0px)),transparent 100%);animation:biab-chatbot-fade-b 1ms linear both;animation-timeline:scroll(self y);animation-range:calc(100% - 96px) 100%;}}
1007
+ :where([data-bd-chatbot]){font-size:14px;color:inherit;background:var(--bd-chatbot-bg,Canvas);border:1px solid color-mix(in srgb,currentColor 12%,transparent);border-radius:16px;}
1008
+ :where([data-bd-chatbot] [data-slot="header"]){padding:12px 16px;border-bottom:1px solid color-mix(in srgb,currentColor 10%,transparent);}
1009
+ :where([data-bd-chatbot] [data-slot="header-title"]){font-weight:600;}
1010
+ :where([data-bd-chatbot] [data-slot="header-subtitle"]){font-size:12px;opacity:.6;margin-top:2px;}
1011
+ :where([data-bd-chatbot] [data-slot="presence-strip"]){display:flex;align-items:center;justify-content:space-between;gap:8px;padding:8px 16px;font-size:12px;border-bottom:1px solid color-mix(in srgb,currentColor 8%,transparent);opacity:.85;}
1012
+ :where([data-bd-chatbot] [data-slot="message-list"]){padding:16px;gap:12px;}
1013
+ :where([data-bd-chatbot] [data-slot="message-row"]){max-width:85%;width:fit-content;padding:8px 12px;border-radius:16px;line-height:1.5;white-space:pre-wrap;overflow-wrap:anywhere;box-shadow:0 1px 2px color-mix(in srgb,currentColor 8%,transparent);}
1014
+ :where([data-bd-chatbot] [data-slot="message-row"][data-role="user"]){align-self:flex-end;background:var(--bd-chatbot-accent,#0d9488);color:#fff;border-bottom-right-radius:4px;}
1015
+ :where([data-bd-chatbot] [data-slot="message-row"][data-role="assistant"]){align-self:flex-start;background:color-mix(in srgb,currentColor 8%,transparent);border-bottom-left-radius:4px;}
1016
+ :where([data-bd-chatbot] [data-slot="message-pending"]){align-self:flex-start;width:fit-content;padding:8px 12px;background-image:linear-gradient(90deg,currentColor 35%,color-mix(in srgb,currentColor 25%,transparent) 50%,currentColor 65%);background-size:200% 100%;-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;animation:bd-chatbot-shimmer 2s linear infinite;}
1017
+ :where([data-bd-chatbot] [data-slot="error-text"]){align-self:center;font-size:12px;color:#dc2626;}
1018
+ :where([data-bd-chatbot] [data-slot="input-bar"]){gap:8px;padding:12px;border-top:1px solid color-mix(in srgb,currentColor 10%,transparent);}
1019
+ :where([data-bd-chatbot] [data-slot="input-field"]){padding:8px 12px;border-radius:10px;border:1px solid color-mix(in srgb,currentColor 15%,transparent);background:transparent;color:inherit;font:inherit;outline:none;}
1020
+ :where([data-bd-chatbot] [data-slot="input-field"]:focus-visible){border-color:var(--bd-chatbot-accent,#0d9488);}
1021
+ :where([data-bd-chatbot] [data-slot="send-button"]){padding:8px 14px;border-radius:10px;border:none;background:var(--bd-chatbot-accent,#0d9488);color:#fff;font-weight:500;cursor:pointer;}
1022
+ :where([data-bd-chatbot] [data-slot="send-button"]:disabled){opacity:.5;cursor:default;}
1023
+ :where([data-bd-chatbot] [data-slot="upload-button"]){display:inline-flex;align-items:center;justify-content:center;padding:0 6px;border:none;background:transparent;color:inherit;font-size:18px;line-height:1;cursor:pointer;opacity:.7;}
1024
+ :where([data-bd-chatbot] [data-slot="upload-button"]:hover){opacity:1;}
1025
+ :where([data-bd-chatbot] [data-slot="upload-button"]:disabled){opacity:.4;cursor:default;}
1026
+ :where([data-bd-chatbot] [data-slot="message-file"]){align-self:flex-end;max-width:85%;padding:8px 12px;border-radius:16px;border-bottom-right-radius:4px;background:var(--bd-chatbot-accent,#0d9488);color:#fff;text-decoration:none;font-size:13px;word-break:break-all;}
1027
+ :where([data-bd-chatbot] [data-slot="message-image"]){align-self:flex-end;max-width:85%;border-radius:12px;overflow:hidden;}
1028
+ :where([data-bd-chatbot] [data-slot="jump-latest"]){position:absolute;left:50%;bottom:72px;transform:translateX(-50%);display:inline-flex;align-items:center;gap:4px;padding:6px 12px;border-radius:999px;border:1px solid color-mix(in srgb,currentColor 15%,transparent);background:var(--bd-chatbot-bg,Canvas);font-size:12px;font-weight:500;cursor:pointer;box-shadow:0 2px 10px color-mix(in srgb,currentColor 20%,transparent);}
1029
+ :where([data-bd-chatbot] [data-slot="form-card"]){display:flex;flex-direction:column;gap:10px;align-self:stretch;padding:14px;border-radius:14px;border:1px solid color-mix(in srgb,currentColor 14%,transparent);background:color-mix(in srgb,currentColor 4%,transparent);}
1030
+ :where([data-bd-chatbot] [data-slot="form-title"]){font-weight:600;}
1031
+ :where([data-bd-chatbot] [data-slot="form-description"]){font-size:12px;opacity:.7;}
1032
+ :where([data-bd-chatbot] [data-slot="form-field"]){gap:4px;}
1033
+ :where([data-bd-chatbot] [data-slot="form-label"]){font-size:12px;font-weight:500;opacity:.85;}
1034
+ :where([data-bd-chatbot] [data-slot="form-input"],[data-bd-chatbot] [data-slot="form-textarea"],[data-bd-chatbot] [data-slot="form-select"]){padding:8px 10px;border-radius:8px;border:1px solid color-mix(in srgb,currentColor 15%,transparent);background:transparent;color:inherit;font:inherit;outline:none;}
1035
+ :where([data-bd-chatbot] [data-slot="form-input"]:focus-visible,[data-bd-chatbot] [data-slot="form-textarea"]:focus-visible,[data-bd-chatbot] [data-slot="form-select"]:focus-visible){border-color:var(--bd-chatbot-accent,#0d9488);}
1036
+ :where([data-bd-chatbot] [data-slot="form-submit"]){padding:8px 14px;border-radius:8px;border:none;background:var(--bd-chatbot-accent,#0d9488);color:#fff;font-weight:500;cursor:pointer;}
1037
+ :where([data-bd-chatbot] [data-slot="form-submit"]:disabled){opacity:.5;cursor:default;}
1038
+ :where([data-bd-chatbot] [data-slot="form-error"]){font-size:12px;color:#dc2626;}
1039
+ :where([data-bd-chatbot] [data-slot="form-receipt"]){font-size:13px;opacity:.8;}
1040
+ @keyframes bd-chatbot-shimmer{from{background-position:100% 0;}to{background-position:0 0;}}
1041
+ @media (prefers-reduced-motion:reduce){:where([data-bd-chatbot] [data-slot="message-pending"]){animation:none;-webkit-text-fill-color:currentColor;}}
1042
+ @property --bd-chatbot-fade{syntax:"<length>";inherits:false;initial-value:0px;}
1043
+ @keyframes bd-chatbot-fade-b{from{--bd-chatbot-fade:24px;}to{--bd-chatbot-fade:0px;}}
1044
+ @supports (animation-timeline:scroll()){:where([data-bd-chatbot] [data-slot="message-list"]){-webkit-mask-image:linear-gradient(to bottom,#000 0,#000 calc(100% - var(--bd-chatbot-fade,0px)),transparent 100%);mask-image:linear-gradient(to bottom,#000 0,#000 calc(100% - var(--bd-chatbot-fade,0px)),transparent 100%);animation:bd-chatbot-fade-b 1ms linear both;animation-timeline:scroll(self y);animation-range:calc(100% - 96px) 100%;}}
1058
1045
  `;
1059
1046
 
1060
1047
  export type ChatbotInlineProps = {
@@ -1460,7 +1447,7 @@ export function ChatbotInline({
1460
1447
  className={
1461
1448
  [classNames.root, className].filter(Boolean).join(" ") || undefined
1462
1449
  }
1463
- data-biab-chatbot={unstyled ? undefined : ""}
1450
+ data-bd-chatbot={unstyled ? undefined : ""}
1464
1451
  style={{ ...inlineLayoutStyles.root, ...style }}
1465
1452
  >
1466
1453
  {title !== null ? (
@@ -2118,7 +2105,7 @@ export type ProgrammaticAuthOptions = {
2118
2105
  returnTo?: string;
2119
2106
  /** Pre-fill the email on the platform-hosted page (sign-in / sign-up only). */
2120
2107
  loginHint?: string;
2121
- /** Override the auth-handler basePath. Defaults to "/api/biab-auth". */
2108
+ /** Override the auth-handler basePath. Defaults to "/api/bd-auth". */
2122
2109
  basePath?: string;
2123
2110
  };
2124
2111
 
@@ -2141,7 +2128,7 @@ export function signUp(options: ProgrammaticAuthOptions = {}): void {
2141
2128
  }
2142
2129
 
2143
2130
  export type ProgrammaticSignOutOptions = {
2144
- /** Override the auth-handler basePath. Defaults to "/api/biab-auth". */
2131
+ /** Override the auth-handler basePath. Defaults to "/api/bd-auth". */
2145
2132
  basePath?: string;
2146
2133
  /**
2147
2134
  * Default `"redirect"` — navigates through `/sign-out` (matches the
@@ -2181,7 +2168,7 @@ export async function signOut(
2181
2168
 
2182
2169
  export type ProgrammaticPasswordResetInput = {
2183
2170
  email: string;
2184
- /** Override the auth-handler basePath. Defaults to "/api/biab-auth". */
2171
+ /** Override the auth-handler basePath. Defaults to "/api/bd-auth". */
2185
2172
  basePath?: string;
2186
2173
  /**
2187
2174
  * Optional return path the platform-hosted reset page sends the user to
@@ -2193,7 +2180,7 @@ export type ProgrammaticPasswordResetInput = {
2193
2180
 
2194
2181
  /**
2195
2182
  * Programmatic "forgot password" — POSTs to the auth handler's
2196
- * `/password-reset` route, which the SDK forwards to BIAB. The server
2183
+ * `/password-reset` route, which the SDK forwards to BD. The server
2197
2184
  * returns `{ ok: true }` regardless of whether an account matched
2198
2185
  * (intentional anti-leak), and emails the platform-hosted reset link
2199
2186
  * out-of-band when a match exists.
@@ -2297,7 +2284,7 @@ export function useFollowers(options: UseFollowersOptions) {
2297
2284
  const controller = useMemo(
2298
2285
  () =>
2299
2286
  createFollowersController({
2300
- client: createBiabClient({
2287
+ client: createBdClient({
2301
2288
  apiKey: token,
2302
2289
  siteId,
2303
2290
  baseUrl: baseUrl ?? DEFAULT_PACKAGE_BASE_URL,
@@ -2338,7 +2325,7 @@ export function useCheckout(options: { apiKey: string; baseUrl: string }) {
2338
2325
  const controller = useMemo(
2339
2326
  () =>
2340
2327
  createCheckoutController({
2341
- client: new BiabDevClient({ apiKey, baseUrl }),
2328
+ client: new BdApiClient({ apiKey, baseUrl }),
2342
2329
  }),
2343
2330
  [apiKey, baseUrl],
2344
2331
  );
@@ -2362,12 +2349,46 @@ export function useCheckout(options: { apiKey: string; baseUrl: string }) {
2362
2349
  }
2363
2350
 
2364
2351
  // ─── Marketing pages — list, lazy-load per page, preload ─────────────────────
2352
+ //
2353
+ // All three hooks are thin React shells over `createMarketingController`.
2354
+ // They used to reimplement the fetch, the status flag and the in-flight
2355
+ // guard here, which meant a fix in the core never reached React — and the
2356
+ // parity gate was satisfied by an unused import rather than by the code.
2357
+ //
2358
+ // What stays React's own business is the SYNCHRONOUS cache read. The core
2359
+ // deliberately does not duplicate the client's cache, and React needs the
2360
+ // first render to be able to paint a cached page without a loading flash.
2361
+
2362
+ /** Unchanged public shape — now an alias of the core's, so the two cannot
2363
+ * drift apart the way they did before. */
2364
+ type MarketingPageState = MarketingPageView;
2365
+
2366
+ /** One controller per hook instance. The cache they share lives a layer down
2367
+ * in the client, so this costs a subscriber set and nothing else. */
2368
+ function useMarketingController(
2369
+ apiKey: string,
2370
+ baseUrl: string,
2371
+ siteId: string,
2372
+ ) {
2373
+ const client = useMemo(
2374
+ () => new BdApiClient({ apiKey, baseUrl }),
2375
+ [apiKey, baseUrl],
2376
+ );
2377
+ const controller = useMemo(
2378
+ () => createMarketingController({ client: client.site(siteId) }),
2379
+ [client, siteId],
2380
+ );
2381
+ useEffect(() => () => controller.destroy(), [controller]);
2365
2382
 
2366
- type MarketingPageState =
2367
- | { status: "idle"; page: null }
2368
- | { status: "loading"; page: null }
2369
- | { status: "ready"; page: SiteMarketingPage | null }
2370
- | { status: "error"; page: null; error: Error };
2383
+ const subscribe = useCallback(
2384
+ (onStoreChange: () => void) => controller.subscribe(() => onStoreChange()),
2385
+ [controller],
2386
+ );
2387
+ const getSnapshot = useCallback(() => controller.snapshot(), [controller]);
2388
+ const snap = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
2389
+
2390
+ return { client, controller, snap };
2391
+ }
2371
2392
 
2372
2393
  /**
2373
2394
  * Lazy-fetch a single marketing page by `pageKey`. The hook reads from the
@@ -2389,61 +2410,30 @@ export function useMarketingPage(options: {
2389
2410
  refresh: () => Promise<void>;
2390
2411
  } {
2391
2412
  const { apiKey, baseUrl, siteId, pageKey, enabled = true } = options;
2392
- const client = useMemo(
2393
- () => new BiabDevClient({ apiKey, baseUrl }),
2394
- [apiKey, baseUrl],
2395
- );
2396
- const initial = client.site(siteId).marketingPages.getCached(pageKey);
2397
- const [state, setState] = useState<MarketingPageState>(
2398
- initial
2399
- ? { status: "ready", page: initial.page }
2400
- : { status: "idle", page: null },
2401
- );
2402
-
2403
- const fetchOnce = useCallback(
2404
- async (skipCache = false) => {
2405
- if (!enabled) return;
2406
- setState({ status: "loading", page: null });
2407
- try {
2408
- const response = await client
2409
- .site(siteId)
2410
- .marketingPages.get(pageKey, { skipCache });
2411
- setState({ status: "ready", page: response.page });
2412
- } catch (err) {
2413
- setState({
2414
- status: "error",
2415
- page: null,
2416
- error:
2417
- err instanceof Error
2418
- ? err
2419
- : new Error("Marketing page load failed"),
2420
- });
2421
- }
2422
- },
2423
- [client, siteId, pageKey, enabled],
2413
+ const { client, controller, snap } = useMarketingController(
2414
+ apiKey,
2415
+ baseUrl,
2416
+ siteId,
2424
2417
  );
2425
2418
 
2426
2419
  useEffect(() => {
2427
2420
  if (!enabled) return;
2428
- // Cache hit: render synchronously, no fetch.
2429
- const hit = client.site(siteId).marketingPages.getCached(pageKey);
2430
- if (hit) {
2431
- setState({ status: "ready", page: hit.page });
2432
- return;
2433
- }
2434
- void fetchOnce(false);
2435
- }, [client, siteId, pageKey, enabled, fetchOnce]);
2421
+ void controller.open(pageKey);
2422
+ }, [controller, pageKey, enabled]);
2436
2423
 
2437
- const refresh = useCallback(() => fetchOnce(true), [fetchOnce]);
2424
+ const refresh = useCallback(
2425
+ () => controller.open(pageKey, { skipCache: true }),
2426
+ [controller, pageKey],
2427
+ );
2438
2428
 
2439
- return { ...state, refresh };
2429
+ // The synchronous cache read is React's to do — the controller cannot,
2430
+ // because the cache lives in the client. Everything after it is the core's.
2431
+ const cached =
2432
+ client.site(siteId).marketingPages.getCached(pageKey)?.page ?? null;
2433
+ return { ...marketingPageView(snap, pageKey, cached), refresh };
2440
2434
  }
2441
2435
 
2442
- type MarketingPageListState =
2443
- | { status: "idle"; pages: null }
2444
- | { status: "loading"; pages: null }
2445
- | { status: "ready"; pages: SiteMarketingPageSummary[] }
2446
- | { status: "error"; pages: null; error: Error };
2436
+ type MarketingPageListState = MarketingListView;
2447
2437
 
2448
2438
  /**
2449
2439
  * Loads the payload-free directory of marketing pages for a site. Pair with
@@ -2458,38 +2448,18 @@ export function useMarketingPagesList(options: {
2458
2448
  refresh: () => Promise<void>;
2459
2449
  } {
2460
2450
  const { apiKey, baseUrl, siteId, enabled = true } = options;
2461
- const client = useMemo(
2462
- () => new BiabDevClient({ apiKey, baseUrl }),
2463
- [apiKey, baseUrl],
2464
- );
2465
- const [state, setState] = useState<MarketingPageListState>({
2466
- status: "idle",
2467
- pages: null,
2468
- });
2451
+ const { controller, snap } = useMarketingController(apiKey, baseUrl, siteId);
2469
2452
 
2470
- const fetchOnce = useCallback(async () => {
2471
- if (!enabled) return;
2472
- setState({ status: "loading", pages: null });
2473
- try {
2474
- const response: ListSiteMarketingPagesResponse = await client
2475
- .site(siteId)
2476
- .marketingPages.list();
2477
- setState({ status: "ready", pages: response.pages });
2478
- } catch (err) {
2479
- setState({
2480
- status: "error",
2481
- pages: null,
2482
- error:
2483
- err instanceof Error ? err : new Error("Marketing pages list failed"),
2484
- });
2485
- }
2486
- }, [client, siteId, enabled]);
2453
+ // This hook only ever calls `load()`, so the controller's single `status`
2454
+ // is describing the index and nothing else.
2455
+ const refresh = useCallback(() => controller.load(), [controller]);
2487
2456
 
2488
2457
  useEffect(() => {
2489
- void fetchOnce();
2490
- }, [fetchOnce]);
2458
+ if (!enabled) return;
2459
+ void controller.load();
2460
+ }, [controller, enabled]);
2491
2461
 
2492
- return { ...state, refresh: fetchOnce };
2462
+ return { ...marketingListView(snap), refresh };
2493
2463
  }
2494
2464
 
2495
2465
  /**
@@ -2504,19 +2474,21 @@ export function useMarketingPagePreloader(options: {
2504
2474
  siteId: string;
2505
2475
  }): (pageKey: string) => Promise<GetSiteMarketingPageResponse | null> {
2506
2476
  const { apiKey, baseUrl, siteId } = options;
2507
- const client = useMemo(
2508
- () => new BiabDevClient({ apiKey, baseUrl }),
2509
- [apiKey, baseUrl],
2477
+ const { client, controller } = useMarketingController(
2478
+ apiKey,
2479
+ baseUrl,
2480
+ siteId,
2510
2481
  );
2511
2482
  return useCallback(
2512
2483
  async (pageKey: string) => {
2513
- try {
2514
- return await client.site(siteId).marketingPages.prefetch(pageKey);
2515
- } catch {
2516
- return null;
2517
- }
2484
+ // The core swallows prefetch failures on purpose — a guess about
2485
+ // where the visitor is going must never surface as an error on the
2486
+ // page they are actually reading. Reading the cache afterwards is
2487
+ // how this keeps returning the response its signature promises.
2488
+ await controller.prefetch(pageKey);
2489
+ return client.site(siteId).marketingPages.getCached(pageKey) ?? null;
2518
2490
  },
2519
- [client, siteId],
2491
+ [client, controller, siteId],
2520
2492
  );
2521
2493
  }
2522
2494
 
@@ -2549,7 +2521,7 @@ export function VerifyEmailFlow(props: {
2549
2521
  }) {
2550
2522
  const { apiKey, baseUrl, sessionToken, currentEmail } = props;
2551
2523
  const client = useMemo(
2552
- () => new BiabDevClient({ apiKey, baseUrl }),
2524
+ () => new BdApiClient({ apiKey, baseUrl }),
2553
2525
  [apiKey, baseUrl],
2554
2526
  );
2555
2527
  const [email, setEmail] = useState(currentEmail ?? "");
@@ -2627,7 +2599,7 @@ export function VerifyEmailConfirm(props: {
2627
2599
  }) {
2628
2600
  const { apiKey, baseUrl, sessionToken, token, onVerified } = props;
2629
2601
  const client = useMemo(
2630
- () => new BiabDevClient({ apiKey, baseUrl }),
2602
+ () => new BdApiClient({ apiKey, baseUrl }),
2631
2603
  [apiKey, baseUrl],
2632
2604
  );
2633
2605
  const [state, setState] = useState<
@@ -2698,7 +2670,7 @@ export function VerifyPhoneFlow(props: {
2698
2670
  }) {
2699
2671
  const { apiKey, baseUrl, sessionToken, currentPhone, onVerified } = props;
2700
2672
  const client = useMemo(
2701
- () => new BiabDevClient({ apiKey, baseUrl }),
2673
+ () => new BdApiClient({ apiKey, baseUrl }),
2702
2674
  [apiKey, baseUrl],
2703
2675
  );
2704
2676
  const [phone, setPhone] = useState(currentPhone ?? "");
@@ -2818,7 +2790,7 @@ export function VerifyPhoneFlow(props: {
2818
2790
  * bundled: it's ~1,600 entries that change only when Lucide does. An author who
2819
2791
  * curated `allowedIcons` skips the fetch entirely.
2820
2792
  */
2821
- function BiabIconSelect({
2793
+ function BdIconSelect({
2822
2794
  field,
2823
2795
  value,
2824
2796
  set,
@@ -2851,19 +2823,19 @@ function BiabIconSelect({
2851
2823
  const results = useMemo(() => filterIconNames(all, query), [all, query]);
2852
2824
 
2853
2825
  return (
2854
- <div className="biab-iconselect">
2826
+ <div className="bd-iconselect">
2855
2827
  <input
2856
- className="biab-input biab-iconselect__search"
2828
+ className="bd-input bd-iconselect__search"
2857
2829
  onChange={(e) => setQuery(e.target.value)}
2858
2830
  placeholder={field.placeholder || "Search icons…"}
2859
2831
  type="text"
2860
2832
  value={query}
2861
2833
  />
2862
2834
  {selected && (
2863
- <span className="biab-iconselect__current">
2835
+ <span className="bd-iconselect__current">
2864
2836
  <img alt={selected} height={16} src={iconUrl(selected)} width={16} />
2865
2837
  <button
2866
- className="biab-iconselect__clear"
2838
+ className="bd-iconselect__clear"
2867
2839
  onClick={() => set("")}
2868
2840
  type="button"
2869
2841
  >
@@ -2871,12 +2843,12 @@ function BiabIconSelect({
2871
2843
  </button>
2872
2844
  </span>
2873
2845
  )}
2874
- <div className="biab-iconselect__grid">
2846
+ <div className="bd-iconselect__grid">
2875
2847
  {results.map((name) => (
2876
2848
  <button
2877
2849
  aria-label={name}
2878
2850
  aria-pressed={selected === name}
2879
- className={`biab-iconselect__item${
2851
+ className={`bd-iconselect__item${
2880
2852
  selected === name ? "is-selected" : ""
2881
2853
  }`}
2882
2854
  key={name}
@@ -2899,7 +2871,7 @@ function BiabIconSelect({
2899
2871
  * pending file and flushed by the controller at submit, exactly like
2900
2872
  * `file_upload`, then replaced by its stored URL.
2901
2873
  */
2902
- function BiabImageInput({
2874
+ function BdImageInput({
2903
2875
  field,
2904
2876
  value,
2905
2877
  set,
@@ -2928,15 +2900,13 @@ function BiabImageInput({
2928
2900
  const url = typeof value === "string" ? value : "";
2929
2901
 
2930
2902
  return (
2931
- <div className="biab-imageinput">
2903
+ <div className="bd-imageinput">
2932
2904
  {sources === "both" && (
2933
- <div className="biab-imageinput__modes">
2905
+ <div className="bd-imageinput__modes">
2934
2906
  {(["upload", "url"] as const).map((m) => (
2935
2907
  <button
2936
2908
  aria-pressed={mode === m}
2937
- className={`biab-imageinput__mode${
2938
- mode === m ? "is-active" : ""
2939
- }`}
2909
+ className={`bd-imageinput__mode${mode === m ? "is-active" : ""}`}
2940
2910
  key={m}
2941
2911
  onClick={() => setMode(m)}
2942
2912
  type="button"
@@ -2949,7 +2919,7 @@ function BiabImageInput({
2949
2919
  {mode === "upload" && sources !== "url" ? (
2950
2920
  <input
2951
2921
  accept={(cfg.allowedTypes ?? ["image/*"]).join(",")}
2952
- className="biab-input biab-imageinput__file"
2922
+ className="bd-input bd-imageinput__file"
2953
2923
  onChange={(e) => {
2954
2924
  const file = e.target.files?.[0];
2955
2925
  if (!file) return;
@@ -2966,7 +2936,7 @@ function BiabImageInput({
2966
2936
  />
2967
2937
  ) : (
2968
2938
  <input
2969
- className="biab-input biab-imageinput__url"
2939
+ className="bd-input bd-imageinput__url"
2970
2940
  onChange={(e) => set(e.target.value)}
2971
2941
  placeholder={field.placeholder || "https://example.com/image.png"}
2972
2942
  type="url"
@@ -2976,12 +2946,12 @@ function BiabImageInput({
2976
2946
  {cfg.showPreview !== false && (pending || url) ? (
2977
2947
  <img
2978
2948
  alt=""
2979
- className="biab-imageinput__preview"
2949
+ className="bd-imageinput__preview"
2980
2950
  src={pending ? "" : url}
2981
2951
  />
2982
2952
  ) : null}
2983
2953
  {pending && (
2984
- <span className="biab-imageinput__pending">
2954
+ <span className="bd-imageinput__pending">
2985
2955
  {pending.name} — uploads on submit.
2986
2956
  </span>
2987
2957
  )}
@@ -2989,13 +2959,13 @@ function BiabImageInput({
2989
2959
  );
2990
2960
  }
2991
2961
 
2992
- // ─── BiabForm (schema-driven drop-in) ──────────────────────────────────────
2962
+ // ─── BdForm (schema-driven drop-in) ──────────────────────────────────────
2993
2963
  //
2994
- // `<BiabForm slug="contact" client={biab} />` renders a complete, validated,
2995
- // multi-step-aware form from a BIAB form schema. All runtime behaviour lives in
2964
+ // `<BdForm slug="contact" client={bd} />` renders a complete, validated,
2965
+ // multi-step-aware form from a BD form schema. All runtime behaviour lives in
2996
2966
  // the framework-agnostic `createFormController` (from `@businessdash/sdk/forms`);
2997
2967
  // this binding is the React view layer: it subscribes via `useSyncExternalStore`
2998
- // and renders the controller's `snapshot()` with the `biab-*` light-DOM class
2968
+ // and renders the controller's `snapshot()` with the `bd-*` light-DOM class
2999
2969
  // convention (no shadow root) so consumer CSS cascades and overrides freely.
3000
2970
  //
3001
2971
  // It reproduces the dashboard FormPreview: per-type field rendering,
@@ -3003,14 +2973,14 @@ function BiabImageInput({
3003
2973
  // gating, flex_row layout, multi-step ("consecutive") navigation with a bar or
3004
2974
  // step progress indicator, entrance animations, and the org icon.
3005
2975
 
3006
- // `BiabFormsClient` is defined in `forms-core/controller.ts` — the layer that
2976
+ // `BdFormsClient` is defined in `forms-core/controller.ts` — the layer that
3007
2977
  // actually consumes it — and re-exported here so it stays importable from
3008
2978
  // `@businessdash/sdk/react`, where consumers have always found it.
3009
- export type { BiabFormsClient };
2979
+ export type { BdFormsClient };
3010
2980
 
3011
- // A `BiabFormProvider` lets a consumer supply the client once at the root and
3012
- // drop `<BiabForm slug="…" />` anywhere below without threading the client prop.
3013
- const BiabFormClientContext = createContext<BiabFormsClient | null>(null);
2981
+ // A `BdFormProvider` lets a consumer supply the client once at the root and
2982
+ // drop `<BdForm slug="…" />` anywhere below without threading the client prop.
2983
+ const BdFormClientContext = createContext<BdFormsClient | null>(null);
3014
2984
 
3015
2985
  /** Production package-API base URL — the default target for a publishable token
3016
2986
  * when no `baseUrl` is supplied. Override via the `baseUrl` prop for staging or
@@ -3019,11 +2989,11 @@ const BiabFormClientContext = createContext<BiabFormsClient | null>(null);
3019
2989
  * request), so a browser publishable-token client must target the apex. */
3020
2990
  export const DEFAULT_PACKAGE_BASE_URL = "https://biab.app/api/package/v1";
3021
2991
 
3022
- export type BiabFormProviderProps = {
2992
+ export type BdFormProviderProps = {
3023
2993
  /** A forms client (or same-origin proxy). Provide this OR a publishable `token`. */
3024
- client?: BiabFormsClient;
2994
+ client?: BdFormsClient;
3025
2995
  /**
3026
- * A publishable token (`pk_…`) — builds a browser client that calls BIAB
2996
+ * A publishable token (`pk_…`) — builds a browser client that calls BD
3027
2997
  * directly. Safe in browser JS: it's origin-locked (the browser sends Origin
3028
2998
  * automatically), rate-limited, and scoped to public read/submit actions
3029
2999
  * only. Requires `siteId`. Use instead of `client` for the zero-backend path.
@@ -3036,21 +3006,21 @@ export type BiabFormProviderProps = {
3036
3006
  children: ReactNode;
3037
3007
  };
3038
3008
 
3039
- /** Provide a BIAB forms client to every nested `<BiabForm>`. Pass a `client`, or
3009
+ /** Provide a BD forms client to every nested `<BdForm>`. Pass a `client`, or
3040
3010
  * a publishable `token` + `siteId` (+ optional `baseUrl`) to build a browser
3041
3011
  * client. */
3042
- export function BiabFormProvider({
3012
+ export function BdFormProvider({
3043
3013
  client,
3044
3014
  token,
3045
3015
  siteId,
3046
3016
  baseUrl,
3047
3017
  children,
3048
- }: BiabFormProviderProps) {
3049
- const resolved = useMemo<BiabFormsClient | null>(
3018
+ }: BdFormProviderProps) {
3019
+ const resolved = useMemo<BdFormsClient | null>(
3050
3020
  () =>
3051
3021
  client ??
3052
3022
  (token && siteId
3053
- ? createBiabClient({
3023
+ ? createBdClient({
3054
3024
  apiKey: token,
3055
3025
  siteId,
3056
3026
  baseUrl: baseUrl ?? DEFAULT_PACKAGE_BASE_URL,
@@ -3059,18 +3029,18 @@ export function BiabFormProvider({
3059
3029
  [client, token, siteId, baseUrl],
3060
3030
  );
3061
3031
  return (
3062
- <BiabFormClientContext.Provider value={resolved}>
3032
+ <BdFormClientContext.Provider value={resolved}>
3063
3033
  {children}
3064
- </BiabFormClientContext.Provider>
3034
+ </BdFormClientContext.Provider>
3065
3035
  );
3066
3036
  }
3067
3037
 
3068
- /** Read the client from a surrounding `<BiabFormProvider>`, if any. */
3069
- export function useBiabFormsClient(): BiabFormsClient | null {
3070
- return useContext(BiabFormClientContext);
3038
+ /** Read the client from a surrounding `<BdFormProvider>`, if any. */
3039
+ export function useBdFormsClient(): BdFormsClient | null {
3040
+ return useContext(BdFormClientContext);
3071
3041
  }
3072
3042
 
3073
- /** Fetch open booking slots for an auto-`schedule` field. BiabForm derives this
3043
+ /** Fetch open booking slots for an auto-`schedule` field. BdForm derives this
3074
3044
  * from its client + slug and provides it via context so the deep field renderer
3075
3045
  * needn't thread it. Undefined when the client can't fetch slots. */
3076
3046
  type FetchScheduleSlots = (input: {
@@ -3082,28 +3052,28 @@ const FetchScheduleSlotsContext = createContext<FetchScheduleSlots | undefined>(
3082
3052
  undefined,
3083
3053
  );
3084
3054
 
3085
- /** Autofill (`autocomplete`) config for the field tree, provided by `<BiabForm>`.
3055
+ /** Autofill (`autocomplete`) config for the field tree, provided by `<BdForm>`.
3086
3056
  * Default `{}` → inference on, no overrides. */
3087
3057
  const AutocompleteContext = createContext<AutocompleteConfig>({});
3088
3058
 
3089
- type BiabScheduleAutoValue = { slotStartAt?: string; timezone?: string };
3059
+ type BdScheduleAutoValue = { slotStartAt?: string; timezone?: string };
3090
3060
 
3091
- /** Auto-schedule (instant booking) picker for `<BiabForm>`: fetches the assigned
3061
+ /** Auto-schedule (instant booking) picker for `<BdForm>`: fetches the assigned
3092
3062
  * staff's open slots and lets the visitor pick one. Value `{ slotStartAt,
3093
3063
  * timezone }`; the server books the meeting on submit. Dependency-light (plain
3094
- * elements + `biab-field__schedule-*` classes). */
3095
- function BiabScheduleAuto({
3064
+ * elements + `bd-field__schedule-*` classes). */
3065
+ function BdScheduleAuto({
3096
3066
  field,
3097
3067
  value,
3098
3068
  onChange,
3099
3069
  }: {
3100
3070
  field: { id: string };
3101
3071
  value: unknown;
3102
- onChange: (v: BiabScheduleAutoValue) => void;
3072
+ onChange: (v: BdScheduleAutoValue) => void;
3103
3073
  }) {
3104
3074
  const fetchSlots = useContext(FetchScheduleSlotsContext);
3105
- const v: BiabScheduleAutoValue =
3106
- value && typeof value === "object" ? (value as BiabScheduleAutoValue) : {};
3075
+ const v: BdScheduleAutoValue =
3076
+ value && typeof value === "object" ? (value as BdScheduleAutoValue) : {};
3107
3077
  const tz =
3108
3078
  typeof Intl !== "undefined"
3109
3079
  ? (Intl.DateTimeFormat().resolvedOptions().timeZone ?? "UTC")
@@ -3135,24 +3105,20 @@ function BiabScheduleAuto({
3135
3105
 
3136
3106
  if (!fetchSlots) {
3137
3107
  return (
3138
- <p className="biab-field__schedule-hint">Booking isn't available here.</p>
3108
+ <p className="bd-field__schedule-hint">Booking isn't available here.</p>
3139
3109
  );
3140
3110
  }
3141
3111
  if (phase === "loading" || phase === "idle") {
3142
- return (
3143
- <p className="biab-field__schedule-hint">Loading available times…</p>
3144
- );
3112
+ return <p className="bd-field__schedule-hint">Loading available times…</p>;
3145
3113
  }
3146
3114
  if (phase === "error") {
3147
3115
  return (
3148
- <p className="biab-field__schedule-hint">
3149
- Couldn't load times right now.
3150
- </p>
3116
+ <p className="bd-field__schedule-hint">Couldn't load times right now.</p>
3151
3117
  );
3152
3118
  }
3153
3119
  if (slots.length === 0) {
3154
3120
  return (
3155
- <p className="biab-field__schedule-hint">
3121
+ <p className="bd-field__schedule-hint">
3156
3122
  No open times in the next few weeks.
3157
3123
  </p>
3158
3124
  );
@@ -3176,14 +3142,14 @@ function BiabScheduleAuto({
3176
3142
  }
3177
3143
 
3178
3144
  return (
3179
- <div className="biab-field__schedule-auto">
3145
+ <div className="bd-field__schedule-auto">
3180
3146
  {[...byDay.entries()].map(([day, times]) => (
3181
- <div className="biab-field__schedule-day" key={day}>
3182
- <p className="biab-field__schedule-day-label">{day}</p>
3183
- <div className="biab-field__schedule-times">
3147
+ <div className="bd-field__schedule-day" key={day}>
3148
+ <p className="bd-field__schedule-day-label">{day}</p>
3149
+ <div className="bd-field__schedule-times">
3184
3150
  {times.map((t) => (
3185
3151
  <button
3186
- className="biab-field__schedule-slot"
3152
+ className="bd-field__schedule-slot"
3187
3153
  data-selected={v.slotStartAt === t.startAt ? "true" : undefined}
3188
3154
  key={t.startAt}
3189
3155
  onClick={() =>
@@ -3216,18 +3182,18 @@ function useFormSnapshot(controller: FormController) {
3216
3182
  return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
3217
3183
  }
3218
3184
 
3219
- export type BiabFormProps = {
3185
+ export type BdFormProps = {
3220
3186
  /** Form slug to load via the client. Omit when passing a pre-fetched `schema`. */
3221
3187
  slug?: string;
3222
3188
  /**
3223
3189
  * SDK client (or same-origin proxy). Falls back to `token`, then to a
3224
- * surrounding `<BiabFormProvider>`. Required unless a pre-fetched `schema` is
3190
+ * surrounding `<BdFormProvider>`. Required unless a pre-fetched `schema` is
3225
3191
  * supplied AND no submit is needed (submit needs a client).
3226
3192
  */
3227
- client?: BiabFormsClient;
3193
+ client?: BdFormsClient;
3228
3194
  /**
3229
- * A publishable token (`pk_…`) — builds a browser client that calls BIAB
3230
- * directly, so `<BiabForm slug token siteId />` works with zero backend
3195
+ * A publishable token (`pk_…`) — builds a browser client that calls BD
3196
+ * directly, so `<BdForm slug token siteId />` works with zero backend
3231
3197
  * wiring. Safe in browser JS (origin-locked, rate-limited, public
3232
3198
  * read/submit only). Requires `siteId`.
3233
3199
  */
@@ -3270,14 +3236,14 @@ export type BiabFormProps = {
3270
3236
  * (off). Wins over `autoComplete` and any schema-level token.
3271
3237
  */
3272
3238
  fieldAutoComplete?: Record<string, string | boolean>;
3273
- /** Extra class on the root `.biab-form` element. `class` is accepted as an
3239
+ /** Extra class on the root `.bd-form` element. `class` is accepted as an
3274
3240
  * alias for non-JSX callers / parity with other framework bindings. */
3275
3241
  className?: string;
3276
3242
  class?: string;
3277
3243
  style?: CSSProperties;
3278
3244
  };
3279
3245
 
3280
- // ── biab-* class helpers (mirrors FormPreview's `fieldCssKey`) ───────────────
3246
+ // ── bd-* class helpers (mirrors FormPreview's `fieldCssKey`) ───────────────
3281
3247
 
3282
3248
  function cx(...parts: Array<string | false | null | undefined>): string {
3283
3249
  return parts.filter(Boolean).join(" ");
@@ -3360,7 +3326,7 @@ function safeHttpHref(raw: string | undefined): string | undefined {
3360
3326
  // ── Self-contained date-range picker (no external calendar dep) ─────────────
3361
3327
  // A popover trigger + a month grid with click-to-select range. Stored value is
3362
3328
  // `{ start, end }` as `yyyy-MM-dd` strings (identical to the app's contract).
3363
- // Styled via biab-* classes so consumers can theme it.
3329
+ // Styled via bd-* classes so consumers can theme it.
3364
3330
 
3365
3331
  function parseIsoDate(s: string | undefined): Date | null {
3366
3332
  if (!s) return null;
@@ -3375,7 +3341,7 @@ function toIsoDate(d: Date): string {
3375
3341
  return `${y}-${m}-${day}`;
3376
3342
  }
3377
3343
 
3378
- const BIAB_MONTHS = [
3344
+ const BD_MONTHS = [
3379
3345
  "January",
3380
3346
  "February",
3381
3347
  "March",
@@ -3389,9 +3355,9 @@ const BIAB_MONTHS = [
3389
3355
  "November",
3390
3356
  "December",
3391
3357
  ];
3392
- const BIAB_WEEKDAYS = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
3358
+ const BD_WEEKDAYS = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
3393
3359
 
3394
- function BiabDateRange({
3360
+ function BdDateRange({
3395
3361
  value,
3396
3362
  setValue,
3397
3363
  }: {
@@ -3458,12 +3424,12 @@ function BiabDateRange({
3458
3424
  : "Pick a date range";
3459
3425
 
3460
3426
  // Positioning is the only inline style (so the popover never overlaps content
3461
- // even before the CSS loads); everything visual lives in `biab-forms.css`,
3427
+ // even before the CSS loads); everything visual lives in `bd-forms.css`,
3462
3428
  // which mirrors the COSS/shadcn calendar (import it from the SDK).
3463
3429
  return (
3464
- <div className="biab-daterange" ref={ref} style={{ position: "relative" }}>
3430
+ <div className="bd-daterange" ref={ref} style={{ position: "relative" }}>
3465
3431
  <button
3466
- className="biab-input biab-field__input biab-daterange__trigger"
3432
+ className="bd-input bd-field__input bd-daterange__trigger"
3467
3433
  onClick={() => setOpen((o) => !o)}
3468
3434
  type="button"
3469
3435
  >
@@ -3471,13 +3437,13 @@ function BiabDateRange({
3471
3437
  </button>
3472
3438
  {open && (
3473
3439
  <div
3474
- className="biab-daterange__popover"
3440
+ className="bd-daterange__popover"
3475
3441
  style={{ position: "absolute", zIndex: 50 }}
3476
3442
  >
3477
- <div className="biab-daterange__nav">
3443
+ <div className="bd-daterange__nav">
3478
3444
  <button
3479
3445
  aria-label="Previous month"
3480
- className="biab-daterange__navbtn"
3446
+ className="bd-daterange__navbtn"
3481
3447
  onClick={() =>
3482
3448
  setView((s) =>
3483
3449
  s.m === 0 ? { y: s.y - 1, m: 11 } : { y: s.y, m: s.m - 1 },
@@ -3487,12 +3453,12 @@ function BiabDateRange({
3487
3453
  >
3488
3454
 
3489
3455
  </button>
3490
- <span className="biab-daterange__title">
3491
- {BIAB_MONTHS[view.m]} {view.y}
3456
+ <span className="bd-daterange__title">
3457
+ {BD_MONTHS[view.m]} {view.y}
3492
3458
  </span>
3493
3459
  <button
3494
3460
  aria-label="Next month"
3495
- className="biab-daterange__navbtn"
3461
+ className="bd-daterange__navbtn"
3496
3462
  onClick={() =>
3497
3463
  setView((s) =>
3498
3464
  s.m === 11 ? { y: s.y + 1, m: 0 } : { y: s.y, m: s.m + 1 },
@@ -3503,22 +3469,20 @@ function BiabDateRange({
3503
3469
 
3504
3470
  </button>
3505
3471
  </div>
3506
- <div className="biab-daterange__grid">
3507
- {BIAB_WEEKDAYS.map((w) => (
3508
- <span className="biab-daterange__dow" key={w}>
3472
+ <div className="bd-daterange__grid">
3473
+ {BD_WEEKDAYS.map((w) => (
3474
+ <span className="bd-daterange__dow" key={w}>
3509
3475
  {w}
3510
3476
  </span>
3511
3477
  ))}
3512
3478
  {cells.map((d, i) => {
3513
3479
  if (!d) {
3514
- return (
3515
- <span className="biab-daterange__blank" key={`b-${i}`} />
3516
- );
3480
+ return <span className="bd-daterange__blank" key={`b-${i}`} />;
3517
3481
  }
3518
3482
  const st = dayState(d);
3519
3483
  return (
3520
3484
  <button
3521
- className="biab-daterange__day"
3485
+ className="bd-daterange__day"
3522
3486
  data-range-end={st.end || undefined}
3523
3487
  data-range-middle={st.middle || undefined}
3524
3488
  data-range-start={st.start || undefined}
@@ -3539,9 +3503,9 @@ function BiabDateRange({
3539
3503
  );
3540
3504
  }
3541
3505
 
3542
- // ── Per-type input renderer (light DOM, biab-* classes, no shadcn) ──────────
3506
+ // ── Per-type input renderer (light DOM, bd-* classes, no shadcn) ──────────
3543
3507
 
3544
- function BiabFieldInput({
3508
+ function BdFieldInput({
3545
3509
  field,
3546
3510
  value,
3547
3511
  setValue,
@@ -3556,23 +3520,23 @@ function BiabFieldInput({
3556
3520
  const set = (v: unknown) => setValue(field.id, v);
3557
3521
  const ph = field.placeholder || "";
3558
3522
  // Resolved HTML autofill token for this field (or undefined to omit the
3559
- // attribute). Honors the global `<BiabForm autoComplete>` switch and per-field
3523
+ // attribute). Honors the global `<BdForm autoComplete>` switch and per-field
3560
3524
  // overrides; see forms-core/autocomplete.ts.
3561
3525
  const acConfig = useContext(AutocompleteContext);
3562
3526
  const autoComplete = resolveFieldAutocomplete(field, acConfig);
3563
3527
 
3564
3528
  switch (field.type) {
3565
3529
  case "title":
3566
- return <h2 className="biab-field__title">{field.label || "Untitled"}</h2>;
3530
+ return <h2 className="bd-field__title">{field.label || "Untitled"}</h2>;
3567
3531
  case "subtitle":
3568
- return <h3 className="biab-field__subtitle">{field.label || ""}</h3>;
3532
+ return <h3 className="bd-field__subtitle">{field.label || ""}</h3>;
3569
3533
  case "separator":
3570
- return <hr className="biab-field__separator" />;
3534
+ return <hr className="bd-field__separator" />;
3571
3535
  case "richtext":
3572
3536
  // `content` is server-sanitized HTML.
3573
3537
  return field.content ? (
3574
3538
  <div
3575
- className="biab-field__richtext"
3539
+ className="bd-field__richtext"
3576
3540
  // biome-ignore lint/security/noDangerouslySetInnerHtml: server-sanitized
3577
3541
  dangerouslySetInnerHTML={{ __html: field.content }}
3578
3542
  />
@@ -3580,7 +3544,7 @@ function BiabFieldInput({
3580
3544
  case "body_text":
3581
3545
  // Plain body paragraph (no HTML). Display only.
3582
3546
  return field.content ? (
3583
- <p className="biab-field__body">{field.content}</p>
3547
+ <p className="bd-field__body">{field.content}</p>
3584
3548
  ) : null;
3585
3549
  case "bullet_list": {
3586
3550
  const lc = field.listConfig ?? {};
@@ -3588,20 +3552,20 @@ function BiabFieldInput({
3588
3552
  const items = lc.items ?? [];
3589
3553
  if (variant === "details") {
3590
3554
  return (
3591
- <div className="biab-field__points">
3555
+ <div className="bd-field__points">
3592
3556
  {items.map((it, i) => (
3593
- <div className="biab-field__point" key={`${field.id}-li-${i}`}>
3557
+ <div className="bd-field__point" key={`${field.id}-li-${i}`}>
3594
3558
  {it.icon && (
3595
3559
  <span
3596
3560
  aria-hidden="true"
3597
- className="biab-field__point-icon"
3561
+ className="bd-field__point-icon"
3598
3562
  data-icon={it.icon}
3599
3563
  />
3600
3564
  )}
3601
- <span className="biab-field__point-body">
3602
- <span className="biab-field__point-title">{it.text}</span>
3565
+ <span className="bd-field__point-body">
3566
+ <span className="bd-field__point-title">{it.text}</span>
3603
3567
  {it.description && (
3604
- <span className="biab-field__point-desc">
3568
+ <span className="bd-field__point-desc">
3605
3569
  {it.description}
3606
3570
  </span>
3607
3571
  )}
@@ -3612,9 +3576,9 @@ function BiabFieldInput({
3612
3576
  );
3613
3577
  }
3614
3578
  return (
3615
- <div className="biab-field__bullets">
3579
+ <div className="bd-field__bullets">
3616
3580
  {lc.title && (
3617
- <div className="biab-field__bullets-title">{lc.title}</div>
3581
+ <div className="bd-field__bullets-title">{lc.title}</div>
3618
3582
  )}
3619
3583
  <ul>
3620
3584
  {items.map((it, i) => (
@@ -3630,7 +3594,7 @@ function BiabFieldInput({
3630
3594
  const rows = cfg.rows ?? [];
3631
3595
  if (columns.length === 0) return null;
3632
3596
  return (
3633
- <div className="biab-field__table">
3597
+ <div className="bd-field__table">
3634
3598
  <table>
3635
3599
  <thead>
3636
3600
  <tr>
@@ -3680,13 +3644,13 @@ function BiabFieldInput({
3680
3644
  const hasVal = value !== null && value !== undefined && value !== "";
3681
3645
  if (cfg?.sensitive && !hasVal) {
3682
3646
  return (
3683
- <output className="biab-field__calc biab-field__calc--pending">
3647
+ <output className="bd-field__calc bd-field__calc--pending">
3684
3648
  Calculated on submit.
3685
3649
  </output>
3686
3650
  );
3687
3651
  }
3688
3652
  return (
3689
- <output className="biab-field__calc">
3653
+ <output className="bd-field__calc">
3690
3654
  {hasVal ? formatCalcValue(value as FormulaValue, cfg?.display) : "—"}
3691
3655
  </output>
3692
3656
  );
@@ -3701,7 +3665,7 @@ function BiabFieldInput({
3701
3665
  return cfg.src ? (
3702
3666
  <img
3703
3667
  alt={cfg.alt || ""}
3704
- className="biab-field__image"
3668
+ className="bd-field__image"
3705
3669
  src={cfg.src}
3706
3670
  style={{
3707
3671
  width: cfg.width ? `${cfg.width}px` : "auto",
@@ -3719,7 +3683,7 @@ function BiabFieldInput({
3719
3683
  return (
3720
3684
  <textarea
3721
3685
  autoComplete={autoComplete}
3722
- className="biab-textarea biab-field__input"
3686
+ className="bd-textarea bd-field__input"
3723
3687
  onChange={(e) => set(e.target.value)}
3724
3688
  placeholder={ph}
3725
3689
  rows={cfg.rows ?? 4}
@@ -3744,11 +3708,11 @@ function BiabFieldInput({
3744
3708
  (field as { display?: string }).display,
3745
3709
  )
3746
3710
  ) {
3747
- const listId = `biab-list-${field.id}`;
3711
+ const listId = `bd-list-${field.id}`;
3748
3712
  return (
3749
3713
  <>
3750
3714
  <input
3751
- className="biab-input biab-combobox biab-field__input"
3715
+ className="bd-input bd-combobox bd-field__input"
3752
3716
  list={listId}
3753
3717
  onChange={(e) => set(e.target.value)}
3754
3718
  placeholder={ph || "Search…"}
@@ -3765,7 +3729,7 @@ function BiabFieldInput({
3765
3729
  }
3766
3730
  return (
3767
3731
  <select
3768
- className="biab-select biab-field__input"
3732
+ className="bd-select bd-field__input"
3769
3733
  onChange={(e) => set(e.target.value)}
3770
3734
  value={typeof value === "string" ? value : ""}
3771
3735
  >
@@ -3779,14 +3743,14 @@ function BiabFieldInput({
3779
3743
  );
3780
3744
  }
3781
3745
  case "icon_select":
3782
- return <BiabIconSelect field={field} set={set} value={value} />;
3746
+ return <BdIconSelect field={field} set={set} value={value} />;
3783
3747
  case "image_input":
3784
- return <BiabImageInput field={field} set={set} value={value} />;
3748
+ return <BdImageInput field={field} set={set} value={value} />;
3785
3749
  case "radio":
3786
3750
  return (
3787
- <div className="biab-field__radios">
3751
+ <div className="bd-field__radios">
3788
3752
  {(field.options ?? []).map((opt) => (
3789
- <label className="biab-field__radio" key={opt}>
3753
+ <label className="bd-field__radio" key={opt}>
3790
3754
  <input
3791
3755
  checked={value === opt}
3792
3756
  name={field.id}
@@ -3806,7 +3770,7 @@ function BiabFieldInput({
3806
3770
  if ((field as { display?: string }).display === "dropdown") {
3807
3771
  return (
3808
3772
  <select
3809
- className="biab-select biab-field__input"
3773
+ className="bd-select bd-field__input"
3810
3774
  multiple
3811
3775
  onChange={(e) =>
3812
3776
  set(Array.from(e.target.selectedOptions).map((o) => o.value))
@@ -3822,11 +3786,11 @@ function BiabFieldInput({
3822
3786
  );
3823
3787
  }
3824
3788
  return (
3825
- <div className="biab-field__checkboxes">
3789
+ <div className="bd-field__checkboxes">
3826
3790
  {(field.options ?? []).map((opt) => {
3827
3791
  const checked = arr.includes(opt);
3828
3792
  return (
3829
- <label className="biab-field__checkbox" key={opt}>
3793
+ <label className="bd-field__checkbox" key={opt}>
3830
3794
  <input
3831
3795
  checked={checked}
3832
3796
  onChange={(e) =>
@@ -3865,7 +3829,7 @@ function BiabFieldInput({
3865
3829
  };
3866
3830
  return (
3867
3831
  <div
3868
- className="biab-field__choice-cards"
3832
+ className="bd-field__choice-cards"
3869
3833
  role={ccMulti ? "group" : "radiogroup"}
3870
3834
  >
3871
3835
  {ccOptions.map((opt) => {
@@ -3875,7 +3839,7 @@ function BiabFieldInput({
3875
3839
  return (
3876
3840
  <button
3877
3841
  aria-pressed={active}
3878
- className="biab-field__choice-card"
3842
+ className="bd-field__choice-card"
3879
3843
  data-active={active ? "" : undefined}
3880
3844
  data-product-id={opt.productId || undefined}
3881
3845
  key={opt.label}
@@ -3886,35 +3850,35 @@ function BiabFieldInput({
3886
3850
  {opt.imageUrl ? (
3887
3851
  <img
3888
3852
  alt={opt.label}
3889
- className="biab-field__choice-card-image"
3853
+ className="bd-field__choice-card-image"
3890
3854
  src={opt.imageUrl}
3891
3855
  />
3892
3856
  ) : opt.icon ? (
3893
3857
  <span
3894
3858
  aria-hidden="true"
3895
- className="biab-field__choice-card-icon"
3859
+ className="bd-field__choice-card-icon"
3896
3860
  data-icon={opt.icon}
3897
3861
  />
3898
3862
  ) : null}
3899
- <span className="biab-field__choice-card-body">
3900
- <span className="biab-field__choice-card-header">
3901
- <span className="biab-field__choice-card-label">
3863
+ <span className="bd-field__choice-card-body">
3864
+ <span className="bd-field__choice-card-header">
3865
+ <span className="bd-field__choice-card-label">
3902
3866
  {opt.label}
3903
3867
  </span>
3904
3868
  {opt.price ? (
3905
- <span className="biab-field__choice-card-price">
3869
+ <span className="bd-field__choice-card-price">
3906
3870
  {opt.price}
3907
3871
  </span>
3908
3872
  ) : null}
3909
3873
  </span>
3910
3874
  {opt.description ? (
3911
- <span className="biab-field__choice-card-description">
3875
+ <span className="bd-field__choice-card-description">
3912
3876
  {opt.description}
3913
3877
  </span>
3914
3878
  ) : null}
3915
3879
  {safeHttpHref(opt.href) ? (
3916
3880
  <a
3917
- className="biab-field__choice-card-link"
3881
+ className="bd-field__choice-card-link"
3918
3882
  href={safeHttpHref(opt.href)}
3919
3883
  onClick={(e) => e.stopPropagation()}
3920
3884
  rel="noopener noreferrer nofollow"
@@ -3932,7 +3896,7 @@ function BiabFieldInput({
3932
3896
  }
3933
3897
  case "boolean":
3934
3898
  return (
3935
- <label className="biab-field__switch">
3899
+ <label className="bd-field__switch">
3936
3900
  <input
3937
3901
  checked={value === true}
3938
3902
  onChange={(e) => set(e.target.checked)}
@@ -3944,7 +3908,7 @@ function BiabFieldInput({
3944
3908
  case "date":
3945
3909
  return (
3946
3910
  <input
3947
- className="biab-input biab-field__input"
3911
+ className="bd-input bd-field__input"
3948
3912
  onChange={(e) => set(e.target.value)}
3949
3913
  type="date"
3950
3914
  value={typeof value === "string" ? value : ""}
@@ -3973,7 +3937,7 @@ function BiabFieldInput({
3973
3937
  };
3974
3938
  return (
3975
3939
  <select
3976
- className="biab-select biab-field__input"
3940
+ className="bd-select bd-field__input"
3977
3941
  onChange={(e) => set(e.target.value)}
3978
3942
  value={typeof value === "string" ? value : ""}
3979
3943
  >
@@ -3996,15 +3960,15 @@ function BiabFieldInput({
3996
3960
  : {};
3997
3961
  const inputType = (k: string) => (k === "date" ? "date" : "time");
3998
3962
  return (
3999
- <div className="biab-field__grid2">
3963
+ <div className="bd-field__grid2">
4000
3964
  {pair.map((k) => (
4001
- <label className="biab-field__sub" key={k}>
4002
- <span className="biab-field__sublabel">
3965
+ <label className="bd-field__sub" key={k}>
3966
+ <span className="bd-field__sublabel">
4003
3967
  {k[0]?.toUpperCase()}
4004
3968
  {k.slice(1)}
4005
3969
  </span>
4006
3970
  <input
4007
- className="biab-input"
3971
+ className="bd-input"
4008
3972
  onChange={(e) => set({ ...v, [k]: e.target.value })}
4009
3973
  type={inputType(k)}
4010
3974
  value={v[k] ?? ""}
@@ -4015,7 +3979,7 @@ function BiabFieldInput({
4015
3979
  );
4016
3980
  }
4017
3981
  case "date_range":
4018
- return <BiabDateRange setValue={(next) => set(next)} value={value} />;
3982
+ return <BdDateRange setValue={(next) => set(next)} value={value} />;
4019
3983
  case "slider": {
4020
3984
  const min = field.sliderConfig?.min ?? 0;
4021
3985
  const max = field.sliderConfig?.max ?? 100;
@@ -4025,10 +3989,10 @@ function BiabFieldInput({
4025
3989
  unit === "currency" ? `$${n}` : unit === "percent" ? `${n}%` : `${n}`;
4026
3990
  const v = typeof value === "number" ? value : min;
4027
3991
  return (
4028
- <div className="biab-field__slider">
4029
- <div className="biab-field__sliderscale">
3992
+ <div className="bd-field__slider">
3993
+ <div className="bd-field__sliderscale">
4030
3994
  <span>{fmt(min)}</span>
4031
- <span className="biab-field__slidervalue">{fmt(v)}</span>
3995
+ <span className="bd-field__slidervalue">{fmt(v)}</span>
4032
3996
  <span>{fmt(max)}</span>
4033
3997
  </div>
4034
3998
  <input
@@ -4046,14 +4010,11 @@ function BiabFieldInput({
4046
4010
  const max = field.starRatingConfig?.maxStars ?? 5;
4047
4011
  const v = typeof value === "number" ? value : 0;
4048
4012
  return (
4049
- <div className="biab-field__stars">
4013
+ <div className="bd-field__stars">
4050
4014
  {Array.from({ length: max }).map((_, i) => (
4051
4015
  <button
4052
4016
  aria-label={`${i + 1} star${i ? "s" : ""}`}
4053
- className={cx(
4054
- "biab-field__star",
4055
- i < v && "biab-field__star--on",
4056
- )}
4017
+ className={cx("bd-field__star", i < v && "bd-field__star--on")}
4057
4018
  key={`${field.id}-star-${i}`}
4058
4019
  onClick={() => set(i + 1)}
4059
4020
  type="button"
@@ -4067,12 +4028,12 @@ function BiabFieldInput({
4067
4028
  case "happiness_rating": {
4068
4029
  const v = typeof value === "number" ? value : -1;
4069
4030
  return (
4070
- <div className="biab-field__happiness">
4031
+ <div className="bd-field__happiness">
4071
4032
  {["😞", "😕", "😐", "🙂", "😄"].map((emoji, i) => (
4072
4033
  <button
4073
4034
  className={cx(
4074
- "biab-field__emoji",
4075
- i === v && "biab-field__emoji--on",
4035
+ "bd-field__emoji",
4036
+ i === v && "bd-field__emoji--on",
4076
4037
  )}
4077
4038
  key={emoji}
4078
4039
  onClick={() => set(i)}
@@ -4087,16 +4048,16 @@ function BiabFieldInput({
4087
4048
  case "stepper": {
4088
4049
  const v = typeof value === "number" ? value : 0;
4089
4050
  return (
4090
- <div className="biab-field__stepper">
4051
+ <div className="bd-field__stepper">
4091
4052
  <button
4092
- className="biab-btn biab-btn--ghost"
4053
+ className="bd-btn bd-btn--ghost"
4093
4054
  onClick={() => set(v - 1)}
4094
4055
  type="button"
4095
4056
  >
4096
4057
 
4097
4058
  </button>
4098
4059
  <input
4099
- className="biab-field__steppervalue"
4060
+ className="bd-field__steppervalue"
4100
4061
  inputMode="numeric"
4101
4062
  onChange={(e) => {
4102
4063
  const n = Number(e.target.value.replace(/[^\d-]/g, ""));
@@ -4105,7 +4066,7 @@ function BiabFieldInput({
4105
4066
  value={String(v)}
4106
4067
  />
4107
4068
  <button
4108
- className="biab-btn biab-btn--ghost"
4069
+ className="bd-btn bd-btn--ghost"
4109
4070
  onClick={() => set(v + 1)}
4110
4071
  type="button"
4111
4072
  >
@@ -4114,6 +4075,87 @@ function BiabFieldInput({
4114
4075
  </div>
4115
4076
  );
4116
4077
  }
4078
+ case "name": {
4079
+ const v = (value && typeof value === "object" ? value : {}) as {
4080
+ prefix?: string;
4081
+ first?: string;
4082
+ middle?: string;
4083
+ last?: string;
4084
+ suffix?: string;
4085
+ nicknames?: string[] | string;
4086
+ };
4087
+ const cfg = field.nameConfig ?? {};
4088
+ // Per-subfield autofill tokens (given/family/honorifics), suppressed when
4089
+ // autofill is off for this field (global switch or per-field override).
4090
+ const acOff = autoComplete === "off";
4091
+ const part = (
4092
+ k: "prefix" | "first" | "middle" | "last" | "suffix",
4093
+ label: string,
4094
+ ac: string,
4095
+ placeholder: string,
4096
+ ) => (
4097
+ <label className="bd-field__sub" key={k}>
4098
+ <span className="bd-field__sublabel">{label}</span>
4099
+ <input
4100
+ autoComplete={acOff ? "off" : ac}
4101
+ className="bd-input"
4102
+ onChange={(e) => set({ ...v, [k]: e.target.value })}
4103
+ placeholder={placeholder}
4104
+ value={v[k] ?? ""}
4105
+ />
4106
+ </label>
4107
+ );
4108
+ // The nicknames input keeps the raw comma-separated string while the
4109
+ // visitor types; submit normalizes it into a string[] (and the server
4110
+ // re-normalizes authoritatively).
4111
+ const nicknamesText = Array.isArray(v.nicknames)
4112
+ ? v.nicknames.join(", ")
4113
+ : typeof v.nicknames === "string"
4114
+ ? v.nicknames
4115
+ : "";
4116
+ return (
4117
+ <div className="bd-field__name">
4118
+ {cfg.prefix && part("prefix", "Title", "honorific-prefix", "Dr.")}
4119
+ {cfg.middle ? (
4120
+ <>
4121
+ <div className="bd-field__grid2">
4122
+ {part("first", "First name", "given-name", "Jane")}
4123
+ {part("middle", "Middle name", "additional-name", "Quinn")}
4124
+ </div>
4125
+ {cfg.suffix ? (
4126
+ <div className="bd-field__grid2">
4127
+ {part("last", "Last name", "family-name", "Doe")}
4128
+ {part("suffix", "Suffix", "honorific-suffix", "Jr.")}
4129
+ </div>
4130
+ ) : (
4131
+ part("last", "Last name", "family-name", "Doe")
4132
+ )}
4133
+ </>
4134
+ ) : (
4135
+ <>
4136
+ <div className="bd-field__grid2">
4137
+ {part("first", "First name", "given-name", "Jane")}
4138
+ {part("last", "Last name", "family-name", "Doe")}
4139
+ </div>
4140
+ {cfg.suffix &&
4141
+ part("suffix", "Suffix", "honorific-suffix", "Jr.")}
4142
+ </>
4143
+ )}
4144
+ {cfg.nicknames && (
4145
+ <label className="bd-field__sub" key="nicknames">
4146
+ <span className="bd-field__sublabel">Nicknames</span>
4147
+ <input
4148
+ autoComplete={acOff ? "off" : undefined}
4149
+ className="bd-input"
4150
+ onChange={(e) => set({ ...v, nicknames: e.target.value })}
4151
+ placeholder="Johnny, JM — separate with commas"
4152
+ value={nicknamesText}
4153
+ />
4154
+ </label>
4155
+ )}
4156
+ </div>
4157
+ );
4158
+ }
4117
4159
  case "address": {
4118
4160
  const v =
4119
4161
  value && typeof value === "object"
@@ -4128,23 +4170,23 @@ function BiabFieldInput({
4128
4170
  { k: "city", label: "City", ac: "address-level2" },
4129
4171
  ];
4130
4172
  return (
4131
- <div className="biab-field__address">
4173
+ <div className="bd-field__address">
4132
4174
  {rows.map(({ k, label, ac }) => (
4133
- <label className="biab-field__sub" key={k}>
4134
- <span className="biab-field__sublabel">{label}</span>
4175
+ <label className="bd-field__sub" key={k}>
4176
+ <span className="bd-field__sublabel">{label}</span>
4135
4177
  <input
4136
4178
  autoComplete={acOff ? "off" : ac}
4137
- className="biab-input"
4179
+ className="bd-input"
4138
4180
  onChange={(e) => set({ ...v, [k]: e.target.value })}
4139
4181
  value={v[k] ?? ""}
4140
4182
  />
4141
4183
  </label>
4142
4184
  ))}
4143
- <label className="biab-field__sub" key="state">
4144
- <span className="biab-field__sublabel">State</span>
4185
+ <label className="bd-field__sub" key="state">
4186
+ <span className="bd-field__sublabel">State</span>
4145
4187
  <select
4146
4188
  autoComplete={acOff ? "off" : "address-level1"}
4147
- className="biab-select"
4189
+ className="bd-select"
4148
4190
  onChange={(e) => set({ ...v, state: e.target.value })}
4149
4191
  value={v["state"] ?? ""}
4150
4192
  >
@@ -4156,11 +4198,11 @@ function BiabFieldInput({
4156
4198
  ))}
4157
4199
  </select>
4158
4200
  </label>
4159
- <label className="biab-field__sub" key="zip">
4160
- <span className="biab-field__sublabel">Zip</span>
4201
+ <label className="bd-field__sub" key="zip">
4202
+ <span className="bd-field__sublabel">Zip</span>
4161
4203
  <input
4162
4204
  autoComplete={acOff ? "off" : "postal-code"}
4163
- className="biab-input"
4205
+ className="bd-input"
4164
4206
  inputMode="numeric"
4165
4207
  onChange={(e) =>
4166
4208
  // Numbers only (allow a single hyphen for ZIP+4).
@@ -4178,12 +4220,12 @@ function BiabFieldInput({
4178
4220
  const presets = field.options ?? [];
4179
4221
  const isCustom = v !== "" && !presets.includes(v);
4180
4222
  return (
4181
- <div className="biab-field__colors">
4223
+ <div className="bd-field__colors">
4182
4224
  {presets.map((color) => (
4183
4225
  <button
4184
4226
  className={cx(
4185
- "biab-field__color",
4186
- v === color && "biab-field__color--on",
4227
+ "bd-field__color",
4228
+ v === color && "bd-field__color--on",
4187
4229
  )}
4188
4230
  key={color}
4189
4231
  onClick={() => set(color)}
@@ -4195,8 +4237,8 @@ function BiabFieldInput({
4195
4237
  {/* "Other" — native color input returning a single hex. */}
4196
4238
  <label
4197
4239
  className={cx(
4198
- "biab-field__color biab-field__color--custom",
4199
- isCustom && "biab-field__color--on",
4240
+ "bd-field__color bd-field__color--custom",
4241
+ isCustom && "bd-field__color--on",
4200
4242
  )}
4201
4243
  style={
4202
4244
  isCustom
@@ -4230,14 +4272,14 @@ function BiabFieldInput({
4230
4272
  const maxFiles = cfg.maxFiles ?? 1;
4231
4273
  const remaining = Math.max(maxFiles - files.length, 0);
4232
4274
  return (
4233
- <div className="biab-field__files">
4234
- {/* NOTE: no `biab-input` class — a consumer's base input styling
4275
+ <div className="bd-field__files">
4276
+ {/* NOTE: no `bd-input` class — a consumer's base input styling
4235
4277
  (e.g. `appearance: none`) breaks the native file control's
4236
4278
  rendering in Safari (glitched text + layout overlap). The file
4237
- field is styled solely via `biab-field__fileinput`. */}
4279
+ field is styled solely via `bd-field__fileinput`. */}
4238
4280
  <input
4239
4281
  accept={buildAcceptAttr(cfg.allowedTypes)}
4240
- className="biab-field__fileinput"
4282
+ className="bd-field__fileinput"
4241
4283
  disabled={remaining === 0}
4242
4284
  multiple={maxFiles > 1}
4243
4285
  onChange={(e) => {
@@ -4266,19 +4308,19 @@ function BiabFieldInput({
4266
4308
  type="file"
4267
4309
  />
4268
4310
  {files.length > 0 && (
4269
- <ul className="biab-field__filelist">
4311
+ <ul className="bd-field__filelist">
4270
4312
  {files.map((f, i) => (
4271
- <li className="biab-field__fileitem" key={`${f.name}-${i}`}>
4272
- <span className="biab-field__filename">{f.name}</span>
4313
+ <li className="bd-field__fileitem" key={`${f.name}-${i}`}>
4314
+ <span className="bd-field__filename">{f.name}</span>
4273
4315
  {isPendingFileValue(f) ? (
4274
- <span className="biab-field__filepending">
4316
+ <span className="bd-field__filepending">
4275
4317
  {" "}
4276
4318
  · uploads on submit
4277
4319
  </span>
4278
4320
  ) : null}
4279
4321
  <button
4280
4322
  aria-label="Remove file"
4281
- className="biab-field__fileremove"
4323
+ className="bd-field__fileremove"
4282
4324
  onClick={() => set(files.filter((_, idx) => idx !== i))}
4283
4325
  type="button"
4284
4326
  >
@@ -4308,24 +4350,24 @@ function BiabFieldInput({
4308
4350
  ? `${(pc.amountCents / 100).toFixed(2)} ${(pc.currency ?? "usd").toUpperCase()}`
4309
4351
  : null;
4310
4352
  return (
4311
- <div className="biab-field__card">
4312
- <div className="biab-field__card-row">
4313
- <span className="biab-field__card-brand">CARD</span>
4314
- <span className="biab-field__card-dots">•••• •••• •••• ••••</span>
4315
- <span className="biab-field__card-mode">
4353
+ <div className="bd-field__card">
4354
+ <div className="bd-field__card-row">
4355
+ <span className="bd-field__card-brand">CARD</span>
4356
+ <span className="bd-field__card-dots">•••• •••• •••• ••••</span>
4357
+ <span className="bd-field__card-mode">
4316
4358
  {deposit
4317
4359
  ? `Deposit${amount ? ` · ${amount}` : ""}`
4318
4360
  : "Card on file"}
4319
4361
  </span>
4320
4362
  </div>
4321
4363
  {pc?.description ? (
4322
- <p className="biab-field__card-desc">{pc.description}</p>
4364
+ <p className="bd-field__card-desc">{pc.description}</p>
4323
4365
  ) : null}
4324
- <p className="biab-field__card-note">
4366
+ <p className="bd-field__card-note">
4325
4367
  Secure card entry via Stripe. Initialize Elements with{" "}
4326
- <code>biab.forms.paymentIntent(slug, "{field.id}")</code> (returns
4327
- the clientSecret + the org's connected account), confirm, then store
4328
- the intent id on this field before submit.
4368
+ <code>bd.forms.paymentIntent(slug, "{field.id}")</code> (returns the
4369
+ clientSecret + the org's connected account), confirm, then store the
4370
+ intent id on this field before submit.
4329
4371
  </p>
4330
4372
  </div>
4331
4373
  );
@@ -4335,7 +4377,7 @@ function BiabFieldInput({
4335
4377
  (field as { scheduleConfig?: { mode?: string } }).scheduleConfig
4336
4378
  ?.mode === "auto"
4337
4379
  ) {
4338
- return <BiabScheduleAuto field={field} onChange={set} value={value} />;
4380
+ return <BdScheduleAuto field={field} onChange={set} value={value} />;
4339
4381
  }
4340
4382
  const sc = (
4341
4383
  field as {
@@ -4399,9 +4441,9 @@ function BiabFieldInput({
4399
4441
  });
4400
4442
  };
4401
4443
  return (
4402
- <div className="biab-field__schedule">
4444
+ <div className="bd-field__schedule">
4403
4445
  {sc?.allowAsap !== false && (
4404
- <label className="biab-field__schedule-asap">
4446
+ <label className="bd-field__schedule-asap">
4405
4447
  <input
4406
4448
  checked={sv.asap === true}
4407
4449
  onChange={(e) => set({ ...sv, asap: e.target.checked })}
@@ -4411,14 +4453,14 @@ function BiabFieldInput({
4411
4453
  </label>
4412
4454
  )}
4413
4455
  {/* Availability is additive: day/time choices show alongside ASAP. */}
4414
- <div className="biab-field__schedule-group">
4415
- <span className="biab-field__schedule-label">Preferred days</span>
4416
- <div className="biab-field__schedule-chips">
4456
+ <div className="bd-field__schedule-group">
4457
+ <span className="bd-field__schedule-label">Preferred days</span>
4458
+ <div className="bd-field__schedule-chips">
4417
4459
  {days.map((d) => (
4418
4460
  <button
4419
4461
  className={cx(
4420
- "biab-field__chip",
4421
- sv.days?.includes(d) && "biab-field__chip--on",
4462
+ "bd-field__chip",
4463
+ sv.days?.includes(d) && "bd-field__chip--on",
4422
4464
  )}
4423
4465
  key={d}
4424
4466
  onClick={() => toggle("days", d)}
@@ -4429,14 +4471,14 @@ function BiabFieldInput({
4429
4471
  ))}
4430
4472
  </div>
4431
4473
  </div>
4432
- <div className="biab-field__schedule-group">
4433
- <span className="biab-field__schedule-label">Preferred times</span>
4434
- <div className="biab-field__schedule-chips">
4474
+ <div className="bd-field__schedule-group">
4475
+ <span className="bd-field__schedule-label">Preferred times</span>
4476
+ <div className="bd-field__schedule-chips">
4435
4477
  {windows.map((w) => (
4436
4478
  <button
4437
4479
  className={cx(
4438
- "biab-field__chip",
4439
- sv.times?.includes(w) && "biab-field__chip--on",
4480
+ "bd-field__chip",
4481
+ sv.times?.includes(w) && "bd-field__chip--on",
4440
4482
  )}
4441
4483
  key={w}
4442
4484
  onClick={() => toggle("times", w)}
@@ -4449,7 +4491,7 @@ function BiabFieldInput({
4449
4491
  </div>
4450
4492
  {sc?.allowNotes !== false && (
4451
4493
  <textarea
4452
- className="biab-input biab-field__schedule-notes"
4494
+ className="bd-input bd-field__schedule-notes"
4453
4495
  onChange={(e) => set({ ...sv, notes: e.target.value })}
4454
4496
  placeholder="Anything we should know about timing?"
4455
4497
  value={sv.notes ?? ""}
@@ -4465,15 +4507,15 @@ function BiabFieldInput({
4465
4507
  phoneIntl,
4466
4508
  );
4467
4509
  return (
4468
- <div className="biab-field__phone">
4510
+ <div className="bd-field__phone">
4469
4511
  {field.phoneConfig?.showFlags && (
4470
- <span aria-hidden className="biab-field__flag">
4512
+ <span aria-hidden className="bd-field__flag">
4471
4513
  🇺🇸
4472
4514
  </span>
4473
4515
  )}
4474
4516
  <input
4475
4517
  autoComplete={autoComplete}
4476
- className="biab-input biab-field__input"
4518
+ className="bd-input bd-field__input"
4477
4519
  inputMode="tel"
4478
4520
  maxLength={phoneIntl ? 20 : 14}
4479
4521
  onChange={(e) => set(formatPhoneLocal(e.target.value, phoneIntl))}
@@ -4490,7 +4532,7 @@ function BiabFieldInput({
4490
4532
  return (
4491
4533
  <input
4492
4534
  autoComplete={autoComplete}
4493
- className="biab-input biab-field__input"
4535
+ className="bd-input bd-field__input"
4494
4536
  onChange={(e) => set(e.target.value)}
4495
4537
  placeholder={ph || "you@example.com"}
4496
4538
  type="email"
@@ -4506,7 +4548,7 @@ function BiabFieldInput({
4506
4548
  return (
4507
4549
  <input
4508
4550
  autoComplete={autoComplete}
4509
- className="biab-input biab-field__input"
4551
+ className="bd-input bd-field__input"
4510
4552
  inputMode="url"
4511
4553
  onChange={(e) => set({ ...uv, url: e.target.value })}
4512
4554
  placeholder={ph || "https://example.com"}
@@ -4521,7 +4563,7 @@ function BiabFieldInput({
4521
4563
  (field as { allowNegative?: boolean }).allowNegative === false;
4522
4564
  return (
4523
4565
  <input
4524
- className="biab-input biab-field__input"
4566
+ className="bd-input bd-field__input"
4525
4567
  inputMode={allowDecimal ? "decimal" : "numeric"}
4526
4568
  min={noNegative ? 0 : undefined}
4527
4569
  onChange={(e) => {
@@ -4548,12 +4590,12 @@ function BiabFieldInput({
4548
4590
  const allowNeg = field.currencyConfig?.allowNegative === true;
4549
4591
  const allowDec = field.currencyConfig?.allowDecimal !== false;
4550
4592
  return (
4551
- <div className="biab-field__currency">
4552
- <span aria-hidden="true" className="biab-field__currency-symbol">
4593
+ <div className="bd-field__currency">
4594
+ <span aria-hidden="true" className="bd-field__currency-symbol">
4553
4595
  {symbol}
4554
4596
  </span>
4555
4597
  <input
4556
- className="biab-input biab-field__input"
4598
+ className="bd-input bd-field__input"
4557
4599
  inputMode="decimal"
4558
4600
  min={allowNeg ? undefined : 0}
4559
4601
  onChange={(e) => {
@@ -4605,9 +4647,9 @@ function BiabFieldInput({
4605
4647
  const hint =
4606
4648
  typeof num === "number" ? formatMeasurement(num, unit, category) : "";
4607
4649
  return (
4608
- <div className="biab-field__measurement">
4650
+ <div className="bd-field__measurement">
4609
4651
  <input
4610
- className="biab-input biab-field__input"
4652
+ className="bd-input bd-field__input"
4611
4653
  inputMode="decimal"
4612
4654
  onChange={(e) => {
4613
4655
  const raw = e.target.value;
@@ -4621,12 +4663,12 @@ function BiabFieldInput({
4621
4663
  value={num}
4622
4664
  />
4623
4665
  {mode === "fixed" ? (
4624
- <span aria-hidden="true" className="biab-field__unit">
4666
+ <span aria-hidden="true" className="bd-field__unit">
4625
4667
  {unit}
4626
4668
  </span>
4627
4669
  ) : (
4628
4670
  <select
4629
- className="biab-select biab-field__unit-select"
4671
+ className="bd-select bd-field__unit-select"
4630
4672
  onChange={(e) => emit(num, e.target.value)}
4631
4673
  value={unit}
4632
4674
  >
@@ -4638,7 +4680,7 @@ function BiabFieldInput({
4638
4680
  </select>
4639
4681
  )}
4640
4682
  {hint ? (
4641
- <span className="biab-field__measurement-hint">≈ {hint}</span>
4683
+ <span className="bd-field__measurement-hint">≈ {hint}</span>
4642
4684
  ) : null}
4643
4685
  </div>
4644
4686
  );
@@ -4647,7 +4689,7 @@ function BiabFieldInput({
4647
4689
  return (
4648
4690
  <input
4649
4691
  autoComplete={autoComplete}
4650
- className="biab-input biab-field__input"
4692
+ className="bd-input bd-field__input"
4651
4693
  onChange={(e) => set(e.target.value)}
4652
4694
  placeholder={ph}
4653
4695
  value={typeof value === "string" ? value : ""}
@@ -4666,7 +4708,7 @@ const NO_LABEL_TYPES = new Set([
4666
4708
  "table",
4667
4709
  ]);
4668
4710
 
4669
- function BiabFieldRow({
4711
+ function BdFieldRow({
4670
4712
  field,
4671
4713
  value,
4672
4714
  error,
@@ -4683,39 +4725,39 @@ function BiabFieldRow({
4683
4725
  return (
4684
4726
  <div
4685
4727
  className={cx(
4686
- "biab-field",
4687
- `biab-field--${field.type}`,
4688
- field.required && "biab-field--required",
4689
- error && "biab-field--error",
4728
+ "bd-field",
4729
+ `bd-field--${field.type}`,
4730
+ field.required && "bd-field--required",
4731
+ error && "bd-field--error",
4690
4732
  )}
4691
- data-biab-field={fieldCssKey(field)}
4692
- data-biab-type={field.type}
4733
+ data-bd-field={fieldCssKey(field)}
4734
+ data-bd-type={field.type}
4693
4735
  data-field-id={field.id}
4694
4736
  >
4695
4737
  {showLabel && (
4696
4738
  // A visual field heading, not a control-associated <label> — the field's
4697
- // inputs render in BiabFieldInput below and carry no shared id to point a
4739
+ // inputs render in BdFieldInput below and carry no shared id to point a
4698
4740
  // `htmlFor` at, so a <label> here would be an orphan.
4699
- <span className="biab-field__label biab-label">
4741
+ <span className="bd-field__label bd-label">
4700
4742
  {field.label || ""}
4701
- {field.required && <span className="biab-field__required">*</span>}
4743
+ {field.required && <span className="bd-field__required">*</span>}
4702
4744
  </span>
4703
4745
  )}
4704
- <BiabFieldInput
4746
+ <BdFieldInput
4705
4747
  field={field}
4706
4748
  setValue={setValue}
4707
4749
  upload={upload}
4708
4750
  value={value}
4709
4751
  />
4710
- {error && <p className="biab-field__error">{error.message}</p>}
4711
- {field.helpText && <p className="biab-field__help">{field.helpText}</p>}
4752
+ {error && <p className="bd-field__error">{error.message}</p>}
4753
+ {field.helpText && <p className="bd-field__help">{field.helpText}</p>}
4712
4754
  </div>
4713
4755
  );
4714
4756
  }
4715
4757
 
4716
4758
  /** Recursively render a field subtree, mirroring FormPreview's `RenderTree`:
4717
4759
  * input_group (+ concurrent reveal), condition_block, or_condition, flex_row. */
4718
- function BiabRenderTree({
4760
+ function BdRenderTree({
4719
4761
  fields,
4720
4762
  values,
4721
4763
  errors,
@@ -4731,7 +4773,7 @@ function BiabRenderTree({
4731
4773
  flat?: boolean | undefined;
4732
4774
  }) {
4733
4775
  return (
4734
- <div className="biab-tree">
4776
+ <div className="bd-tree">
4735
4777
  {fields.map((f) => {
4736
4778
  if (f.type === "multi_populator") {
4737
4779
  const cfg = f.populatorConfig ?? {};
@@ -4746,8 +4788,8 @@ function BiabRenderTree({
4746
4788
  setValue(f.id, next);
4747
4789
  return (
4748
4790
  <div
4749
- className="biab-populator"
4750
- data-biab-field={fieldCssKey(f)}
4791
+ className="bd-populator"
4792
+ data-bd-field={fieldCssKey(f)}
4751
4793
  data-field-id={f.id}
4752
4794
  key={f.id}
4753
4795
  >
@@ -4759,17 +4801,17 @@ function BiabRenderTree({
4759
4801
  };
4760
4802
  return (
4761
4803
  <div
4762
- className="biab-populator__item"
4804
+ className="bd-populator__item"
4763
4805
  key={`${f.id}-inst-${idx}`}
4764
4806
  >
4765
- <div className="biab-populator__item-head">
4766
- <span className="biab-populator__item-label">
4807
+ <div className="bd-populator__item-head">
4808
+ <span className="bd-populator__item-label">
4767
4809
  {cfg.itemLabel || f.label || "Entry"} {idx + 1}
4768
4810
  </span>
4769
4811
  {instances.length > 1 && (
4770
4812
  <button
4771
4813
  aria-label="Remove entry"
4772
- className="biab-populator__remove"
4814
+ className="bd-populator__remove"
4773
4815
  onClick={() =>
4774
4816
  writeInstances(
4775
4817
  instances.filter((_, i) => i !== idx),
@@ -4781,7 +4823,7 @@ function BiabRenderTree({
4781
4823
  </button>
4782
4824
  )}
4783
4825
  </div>
4784
- <BiabRenderTree
4826
+ <BdRenderTree
4785
4827
  errors={errors}
4786
4828
  fields={children}
4787
4829
  setValue={scopedSetValue}
@@ -4793,7 +4835,7 @@ function BiabRenderTree({
4793
4835
  })}
4794
4836
  {instances.length < max && (
4795
4837
  <button
4796
- className="biab-populator__add"
4838
+ className="bd-populator__add"
4797
4839
  onClick={() => writeInstances([...instances, {}])}
4798
4840
  type="button"
4799
4841
  >
@@ -4813,14 +4855,14 @@ function BiabRenderTree({
4813
4855
  const shown = concurrent ? children.slice(0, revealCount) : children;
4814
4856
  return (
4815
4857
  <div
4816
- className={cx("biab-group", flat && "biab-group--flat")}
4817
- data-biab-group={fieldCssKey(f)}
4858
+ className={cx("bd-group", flat && "bd-group--flat")}
4859
+ data-bd-group={fieldCssKey(f)}
4818
4860
  key={f.id}
4819
4861
  >
4820
4862
  {f.label && showGroupLabel && (
4821
- <div className="biab-group__label">{f.label}</div>
4863
+ <div className="bd-group__label">{f.label}</div>
4822
4864
  )}
4823
- <BiabRenderTree
4865
+ <BdRenderTree
4824
4866
  errors={errors}
4825
4867
  fields={shown}
4826
4868
  setValue={setValue}
@@ -4833,7 +4875,7 @@ function BiabRenderTree({
4833
4875
  if (f.type === "condition_block") {
4834
4876
  if (!evalCondition(f.conditionRule, values)) return null;
4835
4877
  return (
4836
- <BiabRenderTree
4878
+ <BdRenderTree
4837
4879
  errors={errors}
4838
4880
  fields={(f.children ?? []) as FormCoreField[]}
4839
4881
  key={f.id}
@@ -4853,22 +4895,19 @@ function BiabRenderTree({
4853
4895
  ["Option A", "Option B", "Option C"][i] ||
4854
4896
  `Option ${i + 1}`;
4855
4897
  return (
4856
- <div
4857
- className={cx("biab-or", errored && "biab-or--error")}
4858
- key={f.id}
4859
- >
4898
+ <div className={cx("bd-or", errored && "bd-or--error")} key={f.id}>
4860
4899
  {f.label && (
4861
- <div className="biab-or__label">
4900
+ <div className="bd-or__label">
4862
4901
  {f.label}
4863
- <span className="biab-or__hint">Fill at least one side</span>
4902
+ <span className="bd-or__hint">Fill at least one side</span>
4864
4903
  </div>
4865
4904
  )}
4866
- <div className="biab-or__sides">
4905
+ <div className="bd-or__sides">
4867
4906
  {branches.flatMap((child, i) => {
4868
4907
  const side = (
4869
- <div className="biab-or__side" key={child.id}>
4870
- <div className="biab-or__sidelabel">{branchLabel(i)}</div>
4871
- <BiabRenderTree
4908
+ <div className="bd-or__side" key={child.id}>
4909
+ <div className="bd-or__sidelabel">{branchLabel(i)}</div>
4910
+ <BdRenderTree
4872
4911
  errors={errors}
4873
4912
  fields={[child]}
4874
4913
  setValue={setValue}
@@ -4879,7 +4918,7 @@ function BiabRenderTree({
4879
4918
  );
4880
4919
  if (i === 0) return [side];
4881
4920
  return [
4882
- <div className="biab-or__divider" key={`or-${child.id}`}>
4921
+ <div className="bd-or__divider" key={`or-${child.id}`}>
4883
4922
  OR
4884
4923
  </div>,
4885
4924
  side,
@@ -4887,7 +4926,7 @@ function BiabRenderTree({
4887
4926
  })}
4888
4927
  </div>
4889
4928
  {errored && (
4890
- <p className="biab-field__error">
4929
+ <p className="bd-field__error">
4891
4930
  Fill in at least one side to continue.
4892
4931
  </p>
4893
4932
  )}
@@ -4902,7 +4941,7 @@ function BiabRenderTree({
4902
4941
  };
4903
4942
  return (
4904
4943
  <div
4905
- className="biab-flexrow"
4944
+ className="bd-flexrow"
4906
4945
  key={f.id}
4907
4946
  style={{
4908
4947
  display: "flex",
@@ -4927,8 +4966,8 @@ function BiabRenderTree({
4927
4966
  }}
4928
4967
  >
4929
4968
  {((f.children ?? []) as FormCoreField[]).map((child) => (
4930
- <div className="biab-flexrow__cell" key={child.id}>
4931
- <BiabRenderTree
4969
+ <div className="bd-flexrow__cell" key={child.id}>
4970
+ <BdRenderTree
4932
4971
  errors={errors}
4933
4972
  fields={[child]}
4934
4973
  setValue={setValue}
@@ -4941,7 +4980,7 @@ function BiabRenderTree({
4941
4980
  );
4942
4981
  }
4943
4982
  return (
4944
- <BiabFieldRow
4983
+ <BdFieldRow
4945
4984
  error={errors[f.id]}
4946
4985
  field={f}
4947
4986
  key={f.id}
@@ -4955,15 +4994,15 @@ function BiabRenderTree({
4955
4994
  );
4956
4995
  }
4957
4996
 
4958
- export function BiabForm(props: BiabFormProps) {
4959
- const providerClient = useBiabFormsClient();
4960
- // A publishable token (`pk_…`) builds a browser client that calls BIAB
4997
+ export function BdForm(props: BdFormProps) {
4998
+ const providerClient = useBdFormsClient();
4999
+ // A publishable token (`pk_…`) builds a browser client that calls BD
4961
5000
  // directly — the zero-backend path. Explicit `client` still wins, then the
4962
5001
  // token client, then a surrounding provider.
4963
- const tokenClient = useMemo<BiabFormsClient | null>(
5002
+ const tokenClient = useMemo<BdFormsClient | null>(
4964
5003
  () =>
4965
5004
  props.token && props.siteId
4966
- ? createBiabClient({
5005
+ ? createBdClient({
4967
5006
  apiKey: props.token,
4968
5007
  siteId: props.siteId,
4969
5008
  baseUrl: props.baseUrl ?? DEFAULT_PACKAGE_BASE_URL,
@@ -5035,8 +5074,10 @@ export function BiabForm(props: BiabFormProps) {
5035
5074
  // Autofill config provided to the field tree. Keyed by the serialized
5036
5075
  // overrides so a fresh-but-equal `fieldAutoComplete` literal doesn't churn.
5037
5076
  const fieldAutoCompleteKey = JSON.stringify(props.fieldAutoComplete ?? null);
5077
+ // The directive has to sit immediately above the flagged `useMemo`; inside
5078
+ // the call it attaches to the callback and does nothing.
5079
+ // biome-ignore lint/correctness/useExhaustiveDependencies: keyed by fieldAutoCompleteKey, so a fresh-but-equal literal does not churn
5038
5080
  const autocompleteConfig = useMemo<AutocompleteConfig>(
5039
- // biome-ignore lint/correctness/useExhaustiveDependencies: keyed by fieldAutoCompleteKey
5040
5081
  () => ({
5041
5082
  ...(props.autoComplete !== undefined
5042
5083
  ? { enabled: props.autoComplete }
@@ -5072,13 +5113,13 @@ export function BiabForm(props: BiabFormProps) {
5072
5113
  return () => window.removeEventListener("beforeunload", onBeforeUnload);
5073
5114
  }, [exitConfirm?.enabled, exitConfirm?.message, snap.submitted, exitDirty]);
5074
5115
 
5075
- const rootClass = cx("biab-form", props.className, props.class);
5116
+ const rootClass = cx("bd-form", props.className, props.class);
5076
5117
 
5077
5118
  if (snap.status === "loading" || snap.status === "idle") {
5078
5119
  return (
5079
5120
  <div className={rootClass} style={props.style}>
5080
5121
  {props.loadingFallback ?? (
5081
- <div className="biab-form__loading">Loading…</div>
5122
+ <div className="bd-form__loading">Loading…</div>
5082
5123
  )}
5083
5124
  </div>
5084
5125
  );
@@ -5090,7 +5131,7 @@ export function BiabForm(props: BiabFormProps) {
5090
5131
  {props.errorFallback ? (
5091
5132
  props.errorFallback(message)
5092
5133
  ) : (
5093
- <div className="biab-form__error">{message}</div>
5134
+ <div className="bd-form__error">{message}</div>
5094
5135
  )}
5095
5136
  </div>
5096
5137
  );
@@ -5102,7 +5143,7 @@ export function BiabForm(props: BiabFormProps) {
5102
5143
  {props.successFallback ? (
5103
5144
  props.successFallback(ok)
5104
5145
  ) : (
5105
- <div className="biab-form__success">
5146
+ <div className="bd-form__success">
5106
5147
  Thanks — your response was received.
5107
5148
  </div>
5108
5149
  )}
@@ -5114,15 +5155,15 @@ export function BiabForm(props: BiabFormProps) {
5114
5155
  const consecutive = settings.isConsecutive && snap.stepCount > 1;
5115
5156
  const animClass =
5116
5157
  settings.animation === "fade"
5117
- ? "biab-anim--fade"
5158
+ ? "bd-anim--fade"
5118
5159
  : settings.animation === "scale"
5119
- ? "biab-anim--scale"
5160
+ ? "bd-anim--scale"
5120
5161
  : settings.animation === "slide"
5121
5162
  ? snap.forward
5122
- ? "biab-anim--slide-fwd"
5123
- : "biab-anim--slide-back"
5163
+ ? "bd-anim--slide-fwd"
5164
+ : "bd-anim--slide-back"
5124
5165
  : settings.animation === "scroll"
5125
- ? "biab-anim--scroll"
5166
+ ? "bd-anim--scroll"
5126
5167
  : "";
5127
5168
 
5128
5169
  const handleSubmit = async () => {
@@ -5160,14 +5201,14 @@ export function BiabForm(props: BiabFormProps) {
5160
5201
  <AutocompleteContext.Provider value={autocompleteConfig}>
5161
5202
  <div className={rootClass} style={props.style}>
5162
5203
  {snap.orgIcon && (
5163
- <img alt="" className="biab-form__orgicon" src={snap.orgIcon} />
5204
+ <img alt="" className="bd-form__orgicon" src={snap.orgIcon} />
5164
5205
  )}
5165
5206
 
5166
5207
  {(snap.title || snap.description) && (
5167
- <div className="biab-form__head">
5168
- {snap.title && <h2 className="biab-form__title">{snap.title}</h2>}
5208
+ <div className="bd-form__head">
5209
+ {snap.title && <h2 className="bd-form__title">{snap.title}</h2>}
5169
5210
  {snap.description && (
5170
- <p className="biab-form__desc">{snap.description}</p>
5211
+ <p className="bd-form__desc">{snap.description}</p>
5171
5212
  )}
5172
5213
  </div>
5173
5214
  )}
@@ -5176,7 +5217,7 @@ export function BiabForm(props: BiabFormProps) {
5176
5217
  {consecutive &&
5177
5218
  settings.showProgress &&
5178
5219
  settings.progressStyle !== "bar" && (
5179
- <div className="biab-progress biab-progress--steps">
5220
+ <div className="bd-progress bd-progress--steps">
5180
5221
  {snap.steps.map((s, i) => {
5181
5222
  const f = s[0];
5182
5223
  const title = f?.stepTitle || f?.label || `Step ${i + 1}`;
@@ -5187,24 +5228,24 @@ export function BiabForm(props: BiabFormProps) {
5187
5228
  {i > 0 && (
5188
5229
  <span
5189
5230
  className={cx(
5190
- "biab-progress__line",
5191
- i <= snap.step && "biab-progress__line--done",
5231
+ "bd-progress__line",
5232
+ i <= snap.step && "bd-progress__line--done",
5192
5233
  )}
5193
5234
  />
5194
5235
  )}
5195
5236
  <button
5196
5237
  className={cx(
5197
- "biab-progress__step",
5198
- current && "biab-progress__step--current",
5199
- done && "biab-progress__step--done",
5238
+ "bd-progress__step",
5239
+ current && "bd-progress__step--current",
5240
+ done && "bd-progress__step--done",
5200
5241
  )}
5201
5242
  onClick={() => controller.goToStep(i)}
5202
5243
  type="button"
5203
5244
  >
5204
- <span className="biab-progress__num">
5245
+ <span className="bd-progress__num">
5205
5246
  {done ? "✓" : i + 1}
5206
5247
  </span>
5207
- <span className="biab-progress__title">{title}</span>
5248
+ <span className="bd-progress__title">{title}</span>
5208
5249
  </button>
5209
5250
  </Fragment>
5210
5251
  );
@@ -5216,13 +5257,13 @@ export function BiabForm(props: BiabFormProps) {
5216
5257
  {consecutive &&
5217
5258
  settings.showProgress &&
5218
5259
  settings.progressStyle === "bar" && (
5219
- <div className="biab-progress biab-progress--bar">
5220
- <span className="biab-progress__count">
5260
+ <div className="bd-progress bd-progress--bar">
5261
+ <span className="bd-progress__count">
5221
5262
  {snap.step + 1} / {snap.stepCount}
5222
5263
  </span>
5223
- <div className="biab-progress__track">
5264
+ <div className="bd-progress__track">
5224
5265
  <div
5225
- className="biab-progress__fill"
5266
+ className="bd-progress__fill"
5226
5267
  style={{ width: `${snap.progressPercent}%` }}
5227
5268
  />
5228
5269
  </div>
@@ -5231,12 +5272,12 @@ export function BiabForm(props: BiabFormProps) {
5231
5272
 
5232
5273
  {/* Aggregate required-field notice */}
5233
5274
  {snap.errorList.length > 0 && (
5234
- <div className="biab-form__notice" role="alert">
5235
- <div className="biab-form__notice-title">
5275
+ <div className="bd-form__notice" role="alert">
5276
+ <div className="bd-form__notice-title">
5236
5277
  Please complete the highlighted field
5237
5278
  {snap.errorList.length === 1 ? "" : "s"}.
5238
5279
  </div>
5239
- <div className="biab-form__notice-list">
5280
+ <div className="bd-form__notice-list">
5240
5281
  {snap.errorList.map((e) => e.label).join(", ")}
5241
5282
  </div>
5242
5283
  </div>
@@ -5244,10 +5285,10 @@ export function BiabForm(props: BiabFormProps) {
5244
5285
 
5245
5286
  {/* Field tree for the active step (single-page = all fields) */}
5246
5287
  <div
5247
- className={cx("biab-form__body", animClass)}
5248
- key={`biab-step-${snap.step}`}
5288
+ className={cx("bd-form__body", animClass)}
5289
+ key={`bd-step-${snap.step}`}
5249
5290
  >
5250
- <BiabRenderTree
5291
+ <BdRenderTree
5251
5292
  errors={snap.errors}
5252
5293
  fields={snap.visibleFields}
5253
5294
  flat={consecutive}
@@ -5271,10 +5312,10 @@ export function BiabForm(props: BiabFormProps) {
5271
5312
  ? a.coupon.notice.replace(/\{code\}/g, code)
5272
5313
  : `Coupon ${code} will be applied to this submission.`;
5273
5314
  return (
5274
- <div className="biab-coupon-notice" key={`coupon-${i}`}>
5275
- <p className="biab-coupon-notice__text">{text}</p>
5315
+ <div className="bd-coupon-notice" key={`coupon-${i}`}>
5316
+ <p className="bd-coupon-notice__text">{text}</p>
5276
5317
  {a.coupon?.finePrint && (
5277
- <p className="biab-coupon-notice__fine">
5318
+ <p className="bd-coupon-notice__fine">
5278
5319
  {a.coupon.finePrint}
5279
5320
  </p>
5280
5321
  )}
@@ -5284,9 +5325,9 @@ export function BiabForm(props: BiabFormProps) {
5284
5325
 
5285
5326
  {/* Navigation */}
5286
5327
  {consecutive ? (
5287
- <div className="biab-nav">
5328
+ <div className="bd-nav">
5288
5329
  <button
5289
- className="biab-btn biab-btn--back biab-btn--ghost"
5330
+ className="bd-btn bd-btn--back bd-btn--ghost"
5290
5331
  disabled={snap.isFirstStep}
5291
5332
  onClick={() => controller.back()}
5292
5333
  type="button"
@@ -5295,7 +5336,7 @@ export function BiabForm(props: BiabFormProps) {
5295
5336
  </button>
5296
5337
  {!snap.isLastStep ? (
5297
5338
  <button
5298
- className="biab-btn biab-btn--next"
5339
+ className="bd-btn bd-btn--next"
5299
5340
  disabled={!snap.canAdvance}
5300
5341
  onClick={() => controller.next()}
5301
5342
  type="button"
@@ -5304,7 +5345,7 @@ export function BiabForm(props: BiabFormProps) {
5304
5345
  </button>
5305
5346
  ) : (
5306
5347
  <button
5307
- className="biab-btn biab-btn--submit"
5348
+ className="bd-btn bd-btn--submit"
5308
5349
  disabled={snap.submitting || snap.uploading}
5309
5350
  onClick={() => void handleSubmit()}
5310
5351
  type="button"
@@ -5314,9 +5355,9 @@ export function BiabForm(props: BiabFormProps) {
5314
5355
  )}
5315
5356
  </div>
5316
5357
  ) : (
5317
- <div className="biab-nav">
5358
+ <div className="bd-nav">
5318
5359
  <button
5319
- className="biab-btn biab-btn--submit"
5360
+ className="bd-btn bd-btn--submit"
5320
5361
  disabled={snap.submitting || snap.uploading}
5321
5362
  onClick={() => void handleSubmit()}
5322
5363
  type="button"
@@ -5328,7 +5369,7 @@ export function BiabForm(props: BiabFormProps) {
5328
5369
 
5329
5370
  {/* Inline failure message (non-validation, e.g. plan gate / network) */}
5330
5371
  {snap.result && !snap.result.ok && snap.errorList.length === 0 && (
5331
- <div className="biab-form__error" role="alert">
5372
+ <div className="bd-form__error" role="alert">
5332
5373
  {snap.result.message}
5333
5374
  </div>
5334
5375
  )}
@@ -5397,7 +5438,7 @@ export type SocialLinksProps = {
5397
5438
 
5398
5439
  /**
5399
5440
  * Render every social profile on the company as an icon + link, in canonical
5400
- * order, resolving BIAB's exact keys (`meta`, `twitterX`, …) for you. Branded
5441
+ * order, resolving BD's exact keys (`meta`, `twitterX`, …) for you. Branded
5401
5442
  * glyphs come from the simple-icons CDN; platforms without one fall back to a
5402
5443
  * globe. Returns `null` when no socials are set.
5403
5444
  */
@@ -5458,7 +5499,7 @@ export function useBlog(options: {
5458
5499
  const controller = useMemo(
5459
5500
  () =>
5460
5501
  createBlogController({
5461
- client: new BiabDevClient({ apiKey, baseUrl }),
5502
+ client: new BdApiClient({ apiKey, baseUrl }),
5462
5503
  ...(pageSize ? { pageSize } : {}),
5463
5504
  ...(categoryId ? { categoryId } : {}),
5464
5505
  }),
@@ -5506,7 +5547,7 @@ export function useVerify(
5506
5547
  const controller = useMemo(
5507
5548
  () =>
5508
5549
  createVerifyController({
5509
- client: new BiabDevClient({ apiKey, baseUrl }),
5550
+ client: new BdApiClient({ apiKey, baseUrl }),
5510
5551
  kind,
5511
5552
  ...(resendCooldownSeconds ? { resendCooldownSeconds } : {}),
5512
5553
  }),
@@ -5556,7 +5597,7 @@ export function usePortal(options: {
5556
5597
  const controller = useMemo(
5557
5598
  () =>
5558
5599
  createPortalController({
5559
- client: new BiabDevClient({ apiKey, baseUrl }),
5600
+ client: new BdApiClient({ apiKey, baseUrl }),
5560
5601
  sessionToken,
5561
5602
  }),
5562
5603
  [apiKey, baseUrl, sessionToken],