@burdenoff/website-sdk 0.0.0-dev → 2026.511.1

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 (91) hide show
  1. package/dist/analytics/index.js +0 -1
  2. package/dist/analytics/index.js.map +1 -1
  3. package/dist/analytics/index.mjs +0 -1
  4. package/dist/analytics/index.mjs.map +1 -1
  5. package/dist/client/index.d.mts +2 -5
  6. package/dist/client/index.d.ts +2 -5
  7. package/dist/client/index.js +2 -11
  8. package/dist/client/index.js.map +1 -1
  9. package/dist/client/index.mjs +2 -11
  10. package/dist/client/index.mjs.map +1 -1
  11. package/dist/components/contact.d.mts +2 -77
  12. package/dist/components/contact.d.ts +2 -77
  13. package/dist/components/contact.js +9 -112
  14. package/dist/components/contact.js.map +1 -1
  15. package/dist/components/contact.mjs +10 -112
  16. package/dist/components/contact.mjs.map +1 -1
  17. package/dist/components/newsletter.js +6 -17
  18. package/dist/components/newsletter.js.map +1 -1
  19. package/dist/components/newsletter.mjs +6 -17
  20. package/dist/components/newsletter.mjs.map +1 -1
  21. package/dist/components/pricing.d.mts +0 -38
  22. package/dist/components/pricing.d.ts +0 -38
  23. package/dist/components/pricing.js +84 -267
  24. package/dist/components/pricing.js.map +1 -1
  25. package/dist/components/pricing.mjs +84 -267
  26. package/dist/components/pricing.mjs.map +1 -1
  27. package/dist/components/waitlist.js +2 -6
  28. package/dist/components/waitlist.js.map +1 -1
  29. package/dist/components/waitlist.mjs +2 -6
  30. package/dist/components/waitlist.mjs.map +1 -1
  31. package/dist/content/index.d.mts +9 -181
  32. package/dist/content/index.d.ts +9 -181
  33. package/dist/content/index.js +16 -2177
  34. package/dist/content/index.js.map +1 -1
  35. package/dist/content/index.mjs +6 -2141
  36. package/dist/content/index.mjs.map +1 -1
  37. package/dist/index.d.mts +5 -43
  38. package/dist/index.d.ts +5 -43
  39. package/dist/index.js +373 -3919
  40. package/dist/index.js.map +1 -1
  41. package/dist/index.mjs +378 -3905
  42. package/dist/index.mjs.map +1 -1
  43. package/dist/product/index.d.mts +0 -8
  44. package/dist/product/index.d.ts +0 -8
  45. package/dist/product/index.js +4 -24
  46. package/dist/product/index.js.map +1 -1
  47. package/dist/product/index.mjs +4 -24
  48. package/dist/product/index.mjs.map +1 -1
  49. package/package.json +3 -5
  50. package/dist/components/electron-download.d.mts +0 -32
  51. package/dist/components/electron-download.d.ts +0 -32
  52. package/dist/components/electron-download.js +0 -131
  53. package/dist/components/electron-download.js.map +0 -1
  54. package/dist/components/electron-download.mjs +0 -108
  55. package/dist/components/electron-download.mjs.map +0 -1
  56. package/dist/components/launch-countdown.d.mts +0 -15
  57. package/dist/components/launch-countdown.d.ts +0 -15
  58. package/dist/components/launch-countdown.js +0 -89
  59. package/dist/components/launch-countdown.js.map +0 -1
  60. package/dist/components/launch-countdown.mjs +0 -87
  61. package/dist/components/launch-countdown.mjs.map +0 -1
  62. package/dist/components/partners.d.mts +0 -88
  63. package/dist/components/partners.d.ts +0 -88
  64. package/dist/components/partners.js +0 -1169
  65. package/dist/components/partners.js.map +0 -1
  66. package/dist/components/partners.mjs +0 -1143
  67. package/dist/components/partners.mjs.map +0 -1
  68. package/dist/components/problems-solved.d.mts +0 -37
  69. package/dist/components/problems-solved.d.ts +0 -37
  70. package/dist/components/problems-solved.js +0 -157
  71. package/dist/components/problems-solved.js.map +0 -1
  72. package/dist/components/problems-solved.mjs +0 -135
  73. package/dist/components/problems-solved.mjs.map +0 -1
  74. package/dist/components/resource-links.d.mts +0 -28
  75. package/dist/components/resource-links.d.ts +0 -28
  76. package/dist/components/resource-links.js +0 -229
  77. package/dist/components/resource-links.js.map +0 -1
  78. package/dist/components/resource-links.mjs +0 -227
  79. package/dist/components/resource-links.mjs.map +0 -1
  80. package/dist/components/social-links.d.mts +0 -37
  81. package/dist/components/social-links.d.ts +0 -37
  82. package/dist/components/social-links.js +0 -268
  83. package/dist/components/social-links.js.map +0 -1
  84. package/dist/components/social-links.mjs +0 -266
  85. package/dist/components/social-links.mjs.map +0 -1
  86. package/dist/components/website-switcher.d.mts +0 -27
  87. package/dist/components/website-switcher.d.ts +0 -27
  88. package/dist/components/website-switcher.js +0 -168
  89. package/dist/components/website-switcher.js.map +0 -1
  90. package/dist/components/website-switcher.mjs +0 -146
  91. package/dist/components/website-switcher.mjs.map +0 -1
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@
3
3
  var React = require('react');
4
4
  var jsxRuntime = require('react/jsx-runtime');
5
5
  var lucideReact = require('lucide-react');
6
- var ReCAPTCHA3 = require('react-google-recaptcha');
6
+ var ReCAPTCHA2 = require('react-google-recaptcha');
7
7
  var sonner = require('sonner');
8
8
  var reactHelmetAsync = require('react-helmet-async');
9
9
  var reactSlot = require('@radix-ui/react-slot');
@@ -14,7 +14,6 @@ var LabelPrimitive = require('@radix-ui/react-label');
14
14
  var zod$1 = require('@hookform/resolvers/zod');
15
15
  var reactHookForm = require('react-hook-form');
16
16
  var zod = require('zod');
17
- var DropdownMenu = require('@radix-ui/react-dropdown-menu');
18
17
 
19
18
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
20
19
 
@@ -37,9 +36,8 @@ function _interopNamespace(e) {
37
36
  }
38
37
 
39
38
  var React__namespace = /*#__PURE__*/_interopNamespace(React);
40
- var ReCAPTCHA3__default = /*#__PURE__*/_interopDefault(ReCAPTCHA3);
39
+ var ReCAPTCHA2__default = /*#__PURE__*/_interopDefault(ReCAPTCHA2);
41
40
  var LabelPrimitive__namespace = /*#__PURE__*/_interopNamespace(LabelPrimitive);
42
- var DropdownMenu__namespace = /*#__PURE__*/_interopNamespace(DropdownMenu);
43
41
 
44
42
  var __defProp = Object.defineProperty;
45
43
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
@@ -194,7 +192,6 @@ function getLocalFallback() {
194
192
  }
195
193
  var PRODUCT_DOMAINS = [
196
194
  "burdenoff.com",
197
- "algoshred.com",
198
195
  "vibecontrols.com",
199
196
  "fluidgrids.com",
200
197
  "botlit.ai",
@@ -239,25 +236,17 @@ function isAlphaHost(hostname) {
239
236
  );
240
237
  }
241
238
  function resolveGatewayUrl(explicitUrl) {
242
- if (explicitUrl && isAbsoluteUrl(explicitUrl) && !isLocalhostUrl(explicitUrl)) {
239
+ if (explicitUrl && !isLocalhostUrl(explicitUrl)) {
243
240
  return explicitUrl;
244
241
  }
245
242
  if (typeof window === "undefined") {
246
- return explicitUrl && isAbsoluteUrl(explicitUrl) ? explicitUrl : getLocalFallback();
243
+ return explicitUrl || getLocalFallback();
247
244
  }
248
245
  const hostname = window.location.hostname.toLowerCase();
249
246
  if (isProductionHost(hostname)) return PROD_GATEWAY;
250
247
  if (isAlphaHost(hostname)) return ALPHA_GATEWAY;
251
248
  return getLocalFallback();
252
249
  }
253
- function isAbsoluteUrl(url) {
254
- try {
255
- new URL(url);
256
- return true;
257
- } catch {
258
- return false;
259
- }
260
- }
261
250
  function isLocalhostUrl(url) {
262
251
  try {
263
252
  const parsed = new URL(url);
@@ -289,125 +278,6 @@ function useWebSDKConfig() {
289
278
  const client = useWebSDK();
290
279
  return client.getConfig();
291
280
  }
292
- var PRODUCT_FIELDS_BASE = `
293
- id
294
- name
295
- slug
296
- description
297
- status
298
- logoUrl
299
- website
300
- appUrl
301
- docsUrl
302
- contactEmail
303
- supportEmail
304
- contactPhone
305
- contactAddress
306
- seoTitle
307
- seoDescription
308
- seoKeywords
309
- ogImage
310
- socialLinks
311
- recaptchaSiteKey
312
- rybbitSiteId
313
- notificationEmail
314
- metadata
315
- `;
316
- var PUBLIC_PRODUCT_QUERY = `
317
- query PublicProduct($slug: String!) {
318
- publicProduct(slug: $slug) {
319
- ${PRODUCT_FIELDS_BASE}
320
- resourceLinks
321
- }
322
- }
323
- `;
324
- var PUBLIC_PRODUCT_QUERY_FALLBACK = `
325
- query PublicProductFallback($slug: String!) {
326
- publicProduct(slug: $slug) {
327
- ${PRODUCT_FIELDS_BASE}
328
- }
329
- }
330
- `;
331
- var ProductContext = React.createContext({
332
- product: null,
333
- loading: true,
334
- error: null,
335
- hasProvider: false,
336
- refetch: () => {
337
- }
338
- });
339
- function ProductProvider({ children, fallback }) {
340
- const client = useWebSDK();
341
- const config = useWebSDKConfig();
342
- const [product, setProduct] = React.useState(
343
- fallback ? fallback : null
344
- );
345
- const [loading, setLoading] = React.useState(true);
346
- const [error, setError] = React.useState(null);
347
- const fetchProduct = React.useCallback(async () => {
348
- if (!config.productSlug) {
349
- setLoading(false);
350
- return;
351
- }
352
- setLoading(true);
353
- setError(null);
354
- try {
355
- let result = await client.query(
356
- PUBLIC_PRODUCT_QUERY,
357
- { slug: config.productSlug }
358
- );
359
- const schemaMissingResourceLinks = result.errors?.some(
360
- (e) => /Cannot query field ["']?resourceLinks["']?/i.test(e?.message ?? "")
361
- );
362
- if (schemaMissingResourceLinks) {
363
- result = await client.query(
364
- PUBLIC_PRODUCT_QUERY_FALLBACK,
365
- { slug: config.productSlug }
366
- );
367
- }
368
- if (result.errors?.length) {
369
- setError(result.errors[0]?.message ?? "Unknown error");
370
- } else if (result.data?.publicProduct) {
371
- setProduct(result.data.publicProduct);
372
- }
373
- } catch (err) {
374
- setError(err instanceof Error ? err.message : "Failed to fetch product");
375
- } finally {
376
- setLoading(false);
377
- }
378
- }, [client, config.productSlug]);
379
- React.useLayoutEffect(() => {
380
- fetchProduct();
381
- }, [fetchProduct]);
382
- const value = React.useMemo(
383
- () => ({
384
- product,
385
- loading,
386
- error,
387
- hasProvider: true,
388
- refetch: fetchProduct
389
- }),
390
- [product, loading, error, fetchProduct]
391
- );
392
- return /* @__PURE__ */ jsxRuntime.jsx(ProductContext.Provider, { value, children });
393
- }
394
- function useProduct() {
395
- return React.useContext(ProductContext);
396
- }
397
- function useProductConfig() {
398
- const { product } = useProduct();
399
- const config = useWebSDKConfig();
400
- return {
401
- // Prefer product-level config, fall back to SDK config
402
- recaptchaSiteKey: product?.recaptchaSiteKey ?? config.recaptchaSiteKey ?? null,
403
- rybbitSiteId: product?.rybbitSiteId ?? config.rybbitSiteId ?? null,
404
- contactEmail: product?.contactEmail ?? null,
405
- supportEmail: product?.supportEmail ?? null,
406
- notificationEmail: product?.notificationEmail ?? product?.contactEmail ?? null,
407
- productName: product?.name ?? null,
408
- logoUrl: product?.logoUrl ?? null
409
- };
410
- }
411
281
  function PageHead({
412
282
  title,
413
283
  description,
@@ -531,28 +401,6 @@ var Label = React__namespace.forwardRef(({ className, ...props }, ref) => /* @__
531
401
  }
532
402
  ));
533
403
  Label.displayName = LabelPrimitive__namespace.Root.displayName;
534
- var Select = React__namespace.forwardRef(({ className, children, ...props }, ref) => {
535
- return /* @__PURE__ */ jsxRuntime.jsx(
536
- "select",
537
- {
538
- className: cn(
539
- "flex h-9 w-full appearance-none rounded-md border border-input bg-transparent bg-[length:1rem_1rem] bg-[right_0.75rem_center] bg-no-repeat px-3 py-1 pr-8 text-base shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
540
- // Inline SVG chevron: SVG inside a `background-image: url(data:…)`
541
- // does NOT inherit `currentColor` from the host element (the SVG is
542
- // its own document with no parent CSS context), so the stroke is
543
- // hardcoded to a muted slate-400 (#94a3b8). Matches the muted
544
- // foreground token visually in both light and dark themes well
545
- // enough for a passive chevron — addresses #9 review feedback.
546
- `bg-[url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>")]`,
547
- className
548
- ),
549
- ref,
550
- ...props,
551
- children
552
- }
553
- );
554
- });
555
- Select.displayName = "Select";
556
404
  var Textarea = React__namespace.forwardRef(({ className, ...props }, ref) => {
557
405
  return /* @__PURE__ */ jsxRuntime.jsx(
558
406
  "textarea",
@@ -567,15 +415,6 @@ var Textarea = React__namespace.forwardRef(({ className, ...props }, ref) => {
567
415
  );
568
416
  });
569
417
  Textarea.displayName = "Textarea";
570
- var DEFAULT_CONTACT_CATEGORIES = [
571
- { value: "general", label: "General enquiry" },
572
- { value: "sales", label: "Sales" },
573
- { value: "support", label: "Support" },
574
- { value: "partnership", label: "Partnership" },
575
- { value: "press", label: "Press / media" },
576
- { value: "careers", label: "Careers" },
577
- { value: "security", label: "Security disclosure" }
578
- ];
579
418
  var SUBMIT_CONTACT_INQUIRY_MUTATION = (
580
419
  /* GraphQL */
581
420
  `
@@ -597,12 +436,6 @@ async function submitContactForm(client, data, productId) {
597
436
  subject: data.subject,
598
437
  message: data.message,
599
438
  productId,
600
- // Send `category` only when the user actually picked one. Older
601
- // backend deployments without the `category` field in the
602
- // `SubmitContactInquiryInput` schema would reject any unknown
603
- // field; spreading conditionally keeps the SDK forward-
604
- // compatible.
605
- ...data.category ? { category: data.category } : {},
606
439
  ...data.recaptchaToken ? { recaptchaToken: data.recaptchaToken } : {}
607
440
  }
608
441
  });
@@ -641,17 +474,6 @@ function ContactPage({
641
474
  responseTime = "We typically respond within 24 hours",
642
475
  heroTitle = "Get in Touch",
643
476
  heroDescription = "Have questions about {productName}? We're here to help. Reach out to our team and we'll get back to you as soon as possible.",
644
- headingLevel = 1,
645
- // Backward-compatibility: default to `[]` so existing consumers
646
- // (fluidgrids/burdenoff/algoshred websites) that don't pass
647
- // `categories` continue to render with NO dropdown — same behavior
648
- // they had before this SDK version. To opt in, pass an explicit
649
- // `categories={…}` array (or the exported `DEFAULT_CONTACT_CATEGORIES`
650
- // constant for the sane Burdenoff-generic vocabulary).
651
- categories = [],
652
- categoryLabel = "Category",
653
- categoryPlaceholder = "Select a category",
654
- categoryRequired = true,
655
477
  additionalOptions = [
656
478
  {
657
479
  title: "Sales Inquiries",
@@ -676,10 +498,8 @@ function ContactPage({
676
498
  }) {
677
499
  const client = useWebSDK();
678
500
  const config = useWebSDKConfig();
679
- const { product } = useProduct();
680
- const resolvedProductId = product?.id || config.productId;
681
- const displayName = productName ?? product?.name ?? config.productId;
682
- const effectiveRecaptchaSiteKey = recaptchaSiteKey ?? product?.recaptchaSiteKey ?? config.recaptchaSiteKey ?? "";
501
+ const displayName = productName ?? config.productId;
502
+ const effectiveRecaptchaSiteKey = recaptchaSiteKey ?? config.recaptchaSiteKey ?? "";
683
503
  const recaptchaRef = React.useRef(null);
684
504
  const [isSubmitting, setIsSubmitting] = React.useState(false);
685
505
  const [submitStatus, setSubmitStatus] = React.useState("idle");
@@ -695,19 +515,11 @@ function ContactPage({
695
515
  const form = e.currentTarget;
696
516
  try {
697
517
  const formData = new FormData(form);
698
- const showCategory = categories.length > 0;
699
- const rawCategory = showCategory ? (formData.get("category") ?? "").trim() : "";
700
- if (showCategory && categoryRequired && !rawCategory) {
701
- sonner.toast.error("Please select a category");
702
- setIsSubmitting(false);
703
- return;
704
- }
705
518
  const contactData = {
706
519
  firstName: formData.get("firstName"),
707
520
  lastName: formData.get("lastName"),
708
521
  email: formData.get("email"),
709
522
  company: formData.get("company"),
710
- category: rawCategory || void 0,
711
523
  subject: formData.get("subject"),
712
524
  message: formData.get("message"),
713
525
  recaptchaToken
@@ -715,7 +527,7 @@ function ContactPage({
715
527
  const result = await submitContactForm(
716
528
  client,
717
529
  contactData,
718
- resolvedProductId
530
+ config.productId
719
531
  );
720
532
  if (result.success) {
721
533
  setSubmitStatus("success");
@@ -754,16 +566,10 @@ function ContactPage({
754
566
  url: seo.url
755
567
  }
756
568
  ),
757
- /* @__PURE__ */ jsxRuntime.jsx(
758
- "section",
759
- {
760
- className: headingLevel === 2 ? "py-12 md:py-16" : "bg-gradient-to-b from-background to-muted/20 py-12 sm:py-16 md:py-20 lg:py-24",
761
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center max-w-3xl mx-auto", children: [
762
- headingLevel === 2 ? /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl md:text-3xl font-bold mb-3", children: heroTitle }) : /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-3xl sm:text-4xl md:text-5xl font-bold mb-4 sm:mb-6", children: heroTitle }),
763
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-lg sm:text-xl text-muted-foreground leading-relaxed", children: replacePlaceholders(heroDescription, displayName, contactEmail) })
764
- ] }) })
765
- }
766
- ),
569
+ /* @__PURE__ */ jsxRuntime.jsx("section", { className: "bg-gradient-to-b from-background to-muted/20 py-12 sm:py-16 md:py-20 lg:py-24", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center max-w-3xl mx-auto", children: [
570
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-3xl sm:text-4xl md:text-5xl font-bold mb-4 sm:mb-6", children: heroTitle }),
571
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-lg sm:text-xl text-muted-foreground leading-relaxed", children: replacePlaceholders(heroDescription, displayName, contactEmail) })
572
+ ] }) }) }),
767
573
  /* @__PURE__ */ jsxRuntime.jsx("section", { className: "py-12 sm:py-16 md:py-20 lg:py-24", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid sm:grid-cols-1 lg:grid-cols-3 gap-8 sm:gap-10 lg:gap-12", children: [
768
574
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lg:col-span-1 order-2 lg:order-1", children: [
769
575
  /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-xl sm:text-2xl font-bold mb-4 sm:mb-6", children: "Contact Information" }),
@@ -775,7 +581,7 @@ function ContactPage({
775
581
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground text-sm sm:text-base break-all", children: contactEmail })
776
582
  ] })
777
583
  ] }),
778
- contactPhone && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start space-x-3 sm:space-x-4", children: [
584
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start space-x-3 sm:space-x-4", children: [
779
585
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-8 h-8 sm:w-10 sm:h-10 bg-primary/10 rounded-lg flex items-center justify-center flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Phone, { className: "h-4 w-4 sm:h-5 sm:w-5 text-primary" }) }),
780
586
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
781
587
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "font-semibold mb-1 text-sm sm:text-base", children: "Phone" }),
@@ -882,33 +688,6 @@ function ContactPage({
882
688
  }
883
689
  )
884
690
  ] }),
885
- categories.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
886
- /* @__PURE__ */ jsxRuntime.jsxs(
887
- Label,
888
- {
889
- htmlFor: "category",
890
- className: "text-sm sm:text-base",
891
- children: [
892
- categoryLabel,
893
- categoryRequired ? " *" : ""
894
- ]
895
- }
896
- ),
897
- /* @__PURE__ */ jsxRuntime.jsxs(
898
- Select,
899
- {
900
- id: "category",
901
- name: "category",
902
- required: categoryRequired,
903
- defaultValue: "",
904
- className: "text-sm sm:text-base",
905
- children: [
906
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: categoryRequired, children: categoryPlaceholder }),
907
- categories.map((c) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: c.value, children: c.label }, c.value))
908
- ]
909
- }
910
- )
911
- ] }),
912
691
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
913
692
  /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "subject", className: "text-sm sm:text-base", children: "Subject *" }),
914
693
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -938,7 +717,7 @@ function ContactPage({
938
717
  ] }),
939
718
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
940
719
  effectiveRecaptchaSiteKey && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center sm:justify-start", children: /* @__PURE__ */ jsxRuntime.jsx(
941
- ReCAPTCHA3__default.default,
720
+ ReCAPTCHA2__default.default,
942
721
  {
943
722
  ref: recaptchaRef,
944
723
  sitekey: effectiveRecaptchaSiteKey
@@ -979,7 +758,7 @@ function ContactPage({
979
758
  )
980
759
  ] }) })
981
760
  ] }) }) }),
982
- additionalOptions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("section", { className: "py-12 sm:py-16 md:py-20 lg:py-24 bg-muted/30", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: [
761
+ /* @__PURE__ */ jsxRuntime.jsx("section", { className: "py-12 sm:py-16 md:py-20 lg:py-24 bg-muted/30", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: [
983
762
  /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl sm:text-3xl font-bold text-center mb-8 sm:mb-12", children: "More Ways to Connect" }),
984
763
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid sm:grid-cols-2 lg:grid-cols-3 gap-6 sm:gap-8 max-w-5xl mx-auto", children: additionalOptions.map((option, index) => /* @__PURE__ */ jsxRuntime.jsxs(
985
764
  "div",
@@ -1008,999 +787,187 @@ function ContactPage({
1008
787
  ] }) })
1009
788
  ] });
1010
789
  }
1011
- var PUBLIC_CONTENT_PAGE_QUERY = `
1012
- query PublicContentPage($slug: String!, $productId: String) {
1013
- publicContentPage(slug: $slug, productId: $productId) {
1014
- id
1015
- title
1016
- slug
1017
- content
1018
- contentFormat
1019
- seoTitle
1020
- seoDescription
1021
- seoKeywords
1022
- lastUpdated
1023
- version
1024
- excerpt
1025
- thumbnail
1026
- author
1027
- publishedAt
1028
- readingTime
1029
- featured
1030
- category
1031
- }
1032
- }
1033
- `;
1034
- var PUBLIC_RESOLVED_CONTENT_PAGE_QUERY = `
1035
- query PublicResolvedContentPage($slug: String!, $productId: String!) {
1036
- publicResolvedContentPage(slug: $slug, productId: $productId) {
790
+ var PUBLIC_PRODUCT_QUERY = `
791
+ query PublicProduct($slug: String!) {
792
+ publicProduct(slug: $slug) {
1037
793
  id
1038
- title
794
+ name
1039
795
  slug
1040
- content
1041
- contentFormat
796
+ description
797
+ status
798
+ logoUrl
799
+ website
800
+ appUrl
801
+ docsUrl
802
+ contactEmail
803
+ supportEmail
804
+ contactPhone
805
+ contactAddress
1042
806
  seoTitle
1043
807
  seoDescription
1044
808
  seoKeywords
1045
- lastUpdated
1046
- version
1047
- excerpt
1048
- thumbnail
1049
- author
1050
- publishedAt
1051
- readingTime
1052
- featured
1053
- category
809
+ ogImage
810
+ socialLinks
811
+ recaptchaSiteKey
812
+ rybbitSiteId
813
+ notificationEmail
814
+ metadata
1054
815
  }
1055
816
  }
1056
817
  `;
1057
- var PUBLIC_CONTENT_PAGES_QUERY = `
1058
- query PublicContentPages($productId: String!, $limit: Int, $offset: Int) {
1059
- publicContentPages(productId: $productId, limit: $limit, offset: $offset) {
1060
- items {
1061
- id
1062
- title
1063
- slug
1064
- lastUpdated
1065
- tags
1066
- seoDescription
1067
- excerpt
1068
- thumbnail
1069
- author
1070
- publishedAt
1071
- readingTime
1072
- featured
1073
- category
1074
- }
1075
- total
1076
- hasMore
1077
- }
818
+ var ProductContext = React.createContext({
819
+ product: null,
820
+ loading: true,
821
+ error: null,
822
+ hasProvider: false,
823
+ refetch: () => {
1078
824
  }
1079
- `;
1080
- function useContentPage(slug, options) {
825
+ });
826
+ function ProductProvider({ children, fallback }) {
1081
827
  const client = useWebSDK();
1082
828
  const config = useWebSDKConfig();
1083
- const { product } = useProduct();
1084
- const resolvedProductId = product?.id || config.productId;
1085
- const [data, setData] = React.useState(null);
829
+ const [product, setProduct] = React.useState(
830
+ fallback ? fallback : null
831
+ );
1086
832
  const [loading, setLoading] = React.useState(true);
1087
833
  const [error, setError] = React.useState(null);
1088
- const fetchPage = React.useCallback(async () => {
1089
- if (!resolvedProductId) return;
834
+ const fetchProduct = React.useCallback(async () => {
835
+ if (!config.productSlug) {
836
+ setLoading(false);
837
+ return;
838
+ }
1090
839
  setLoading(true);
1091
840
  setError(null);
1092
841
  try {
1093
- const query = options?.resolve ? PUBLIC_RESOLVED_CONTENT_PAGE_QUERY : PUBLIC_CONTENT_PAGE_QUERY;
1094
- const queryName = options?.resolve ? "publicResolvedContentPage" : "publicContentPage";
1095
842
  const result = await client.query(
1096
- query,
1097
- {
1098
- slug,
1099
- productId: resolvedProductId
1100
- }
843
+ PUBLIC_PRODUCT_QUERY,
844
+ { slug: config.productSlug }
1101
845
  );
1102
846
  if (result.errors?.length) {
1103
847
  setError(result.errors[0]?.message ?? "Unknown error");
1104
- setData(null);
1105
- } else {
1106
- setData(result.data?.[queryName] ?? null);
848
+ } else if (result.data?.publicProduct) {
849
+ setProduct(result.data.publicProduct);
1107
850
  }
1108
851
  } catch (err) {
1109
- setError(err instanceof Error ? err.message : "Failed to fetch content");
1110
- setData(null);
852
+ setError(err instanceof Error ? err.message : "Failed to fetch product");
1111
853
  } finally {
1112
854
  setLoading(false);
1113
855
  }
1114
- }, [client, resolvedProductId, slug, options?.resolve]);
1115
- React.useEffect(() => {
1116
- fetchPage();
1117
- }, [fetchPage]);
1118
- return { data, loading, error, refetch: fetchPage };
1119
- }
1120
- function useContentPages(options) {
1121
- const client = useWebSDK();
1122
- const config = useWebSDKConfig();
1123
- const [data, setData] = React.useState(null);
1124
- const [loading, setLoading] = React.useState(true);
1125
- const [error, setError] = React.useState(null);
1126
- const fetchPages = React.useCallback(async () => {
1127
- setLoading(true);
1128
- setError(null);
1129
- try {
1130
- const result = await client.query(PUBLIC_CONTENT_PAGES_QUERY, {
1131
- productId: config.productId,
1132
- limit: options?.limit ?? 50,
1133
- offset: options?.offset ?? 0
1134
- });
1135
- if (result.errors?.length) {
1136
- setError(result.errors[0]?.message ?? "Unknown error");
1137
- setData(null);
1138
- } else {
1139
- setData(result.data?.publicContentPages ?? null);
1140
- }
1141
- } catch (err) {
1142
- setError(
1143
- err instanceof Error ? err.message : "Failed to fetch content pages"
1144
- );
1145
- setData(null);
1146
- } finally {
1147
- setLoading(false);
1148
- }
1149
- }, [client, config.productId, options?.limit, options?.offset]);
1150
- React.useEffect(() => {
1151
- fetchPages();
1152
- }, [fetchPages]);
1153
- return { data, loading, error, refetch: fetchPages };
1154
- }
1155
- function renderMarkdown(md) {
1156
- let html = md;
1157
- html = html.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1158
- html = html.replace(/```(\w*)\n([\s\S]*?)```/g, (_m, _lang, code) => {
1159
- return `<pre><code>${code.trim()}</code></pre>`;
1160
- });
1161
- html = html.replace(/`([^`]+)`/g, "<code>$1</code>");
1162
- html = html.replace(/^###### (.+)$/gm, "<h6>$1</h6>");
1163
- html = html.replace(/^##### (.+)$/gm, "<h5>$1</h5>");
1164
- html = html.replace(/^#### (.+)$/gm, "<h4>$1</h4>");
1165
- html = html.replace(/^### (.+)$/gm, "<h3>$1</h3>");
1166
- html = html.replace(/^## (.+)$/gm, "<h2>$1</h2>");
1167
- html = html.replace(/^# (.+)$/gm, "<h1>$1</h1>");
1168
- html = html.replace(/\*\*\*(.+?)\*\*\*/g, "<strong><em>$1</em></strong>");
1169
- html = html.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>");
1170
- html = html.replace(/\*(.+?)\*/g, "<em>$1</em>");
1171
- html = html.replace(
1172
- /\[([^\]]+)\]\(([^)]+)\)/g,
1173
- '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>'
856
+ }, [client, config.productSlug]);
857
+ React.useLayoutEffect(() => {
858
+ fetchProduct();
859
+ }, [fetchProduct]);
860
+ const value = React.useMemo(
861
+ () => ({
862
+ product,
863
+ loading,
864
+ error,
865
+ hasProvider: true,
866
+ refetch: fetchProduct
867
+ }),
868
+ [product, loading, error, fetchProduct]
1174
869
  );
1175
- html = html.replace(/^&gt; (.+)$/gm, "<blockquote>$1</blockquote>");
1176
- html = html.replace(/(^- .+$(\n^- .+$)*)/gm, (block) => {
1177
- const items = block.split("\n").map((line) => `<li>${line.replace(/^- /, "")}</li>`).join("");
1178
- return `<ul>${items}</ul>`;
1179
- });
1180
- html = html.replace(/(^\d+\. .+$(\n^\d+\. .+$)*)/gm, (block) => {
1181
- const items = block.split("\n").map((line) => `<li>${line.replace(/^\d+\. /, "")}</li>`).join("");
1182
- return `<ol>${items}</ol>`;
1183
- });
1184
- html = html.replace(/^---$/gm, "<hr />");
1185
- html = html.split(/\n\n+/).map((block) => {
1186
- const trimmed = block.trim();
1187
- if (!trimmed) return "";
1188
- if (/^<(h[1-6]|ul|ol|pre|blockquote|hr)/.test(trimmed)) return trimmed;
1189
- return `<p>${trimmed.replace(/\n/g, "<br />")}</p>`;
1190
- }).join("\n");
1191
- return html;
1192
- }
1193
- function ContentRenderer({
1194
- content,
1195
- format = "markdown",
1196
- className
1197
- }) {
1198
- if (format === "markdown") {
1199
- return /* @__PURE__ */ jsxRuntime.jsx(
1200
- "div",
1201
- {
1202
- className,
1203
- dangerouslySetInnerHTML: { __html: renderMarkdown(content) }
1204
- }
1205
- );
1206
- }
1207
- if (format === "html") {
1208
- return /* @__PURE__ */ jsxRuntime.jsx(
1209
- "div",
1210
- {
1211
- className,
1212
- dangerouslySetInnerHTML: { __html: content }
1213
- }
1214
- );
1215
- }
1216
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className, children: /* @__PURE__ */ jsxRuntime.jsx("pre", { style: { whiteSpace: "pre-wrap", fontFamily: "inherit" }, children: content }) });
1217
- }
1218
- var PARTNER_CONTENT_QUERY = `
1219
- query PartnerContent($productId: String!, $limit: Int) {
1220
- publicContentPages(productId: $productId, limit: $limit, offset: 0) {
1221
- items {
1222
- id
1223
- title
1224
- slug
1225
- excerpt
1226
- content
1227
- contentFormat
1228
- thumbnail
1229
- tags
1230
- publishedAt
1231
- lastUpdated
1232
- }
1233
- }
1234
- }
1235
- `;
1236
- var PARTNER_DOCUMENTS_QUERY = `
1237
- query PartnerDocuments($productId: String!, $limit: Int) {
1238
- publicPressKitAssets(productId: $productId, limit: $limit, offset: 0) {
1239
- items {
1240
- id
1241
- category
1242
- title
1243
- description
1244
- fileUrl
1245
- fileName
1246
- fileSize
1247
- mimeType
1248
- version
1249
- tags
1250
- sortOrder
1251
- updatedAt
1252
- }
1253
- }
1254
- }
1255
- `;
1256
- function formatFileSize(bytes) {
1257
- if (!bytes || bytes <= 0) return "";
1258
- const units = ["B", "KB", "MB", "GB"];
1259
- let i = 0;
1260
- let n = bytes;
1261
- while (n >= 1024 && i < units.length - 1) {
1262
- n /= 1024;
1263
- i++;
1264
- }
1265
- return `${n.toFixed(n >= 10 || i === 0 ? 0 : 1)} ${units[i]}`;
1266
- }
1267
- async function downloadDocument(e, doc) {
1268
- e.preventDefault();
1269
- const name = doc.fileName || doc.fileUrl.split("/").pop()?.split("?")[0] || doc.title || "partner-document";
1270
- const openInTab = () => window.open(doc.fileUrl, "_blank", "noopener,noreferrer");
1271
- try {
1272
- const res = await fetch(doc.fileUrl, { mode: "cors" });
1273
- if (!res.ok) {
1274
- openInTab();
1275
- return;
1276
- }
1277
- const blob = await res.blob();
1278
- const objUrl = URL.createObjectURL(blob);
1279
- const a = document.createElement("a");
1280
- a.href = objUrl;
1281
- a.download = name;
1282
- document.body.appendChild(a);
1283
- a.click();
1284
- a.remove();
1285
- URL.revokeObjectURL(objUrl);
1286
- } catch {
1287
- openInTab();
1288
- }
1289
- }
1290
- function PartnerHero({
1291
- title,
1292
- subtitle
1293
- }) {
1294
- return /* @__PURE__ */ jsxRuntime.jsx("section", { className: "bg-gradient-to-b from-background to-muted/20 py-16 md:py-24", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "container mx-auto px-4 max-w-4xl text-center", children: [
1295
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-4xl md:text-5xl font-bold tracking-tight text-foreground mb-4", children: title }),
1296
- subtitle && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-lg md:text-xl text-muted-foreground max-w-2xl mx-auto", children: subtitle })
1297
- ] }) });
1298
- }
1299
- function stripLeadingHeading(content, format) {
1300
- if (format === "html") {
1301
- return content.replace(/^\s*<h1\b[^>]*>[\s\S]*?<\/h1>\s*/i, "");
1302
- }
1303
- return content.replace(/^\s*#\s+.*(?:\r?\n)*/, "");
1304
- }
1305
- function PartnerContentSection({
1306
- page
1307
- }) {
1308
- return /* @__PURE__ */ jsxRuntime.jsx("section", { className: "py-12 md:py-16", children: /* @__PURE__ */ jsxRuntime.jsx(
1309
- "div",
1310
- {
1311
- className: [
1312
- "mx-auto max-w-3xl px-4",
1313
- // Headings
1314
- "[&_h1]:text-3xl [&_h1]:font-bold [&_h1]:tracking-tight [&_h1]:mt-0 [&_h1]:mb-6",
1315
- "[&_h2]:text-2xl [&_h2]:font-bold [&_h2]:tracking-tight [&_h2]:mt-10 [&_h2]:mb-4",
1316
- "[&_h3]:text-xl [&_h3]:font-semibold [&_h3]:mt-7 [&_h3]:mb-3",
1317
- "[&_h4]:text-lg [&_h4]:font-semibold [&_h4]:mt-6 [&_h4]:mb-2",
1318
- // Block elements
1319
- "[&_p]:my-4 [&_p]:leading-relaxed [&_p]:text-base",
1320
- "[&_ul]:my-4 [&_ul]:pl-6 [&_ul]:list-disc [&_ul]:space-y-1",
1321
- "[&_ol]:my-4 [&_ol]:pl-6 [&_ol]:list-decimal [&_ol]:space-y-1",
1322
- "[&_li]:leading-relaxed",
1323
- "[&_li>p]:my-1",
1324
- "[&_blockquote]:border-l-4 [&_blockquote]:border-border [&_blockquote]:pl-4 [&_blockquote]:italic [&_blockquote]:my-4 [&_blockquote]:text-muted-foreground",
1325
- "[&_hr]:my-8 [&_hr]:border-border",
1326
- // Inline elements
1327
- "[&_strong]:font-semibold [&_strong]:text-foreground",
1328
- "[&_em]:italic",
1329
- "[&_a]:underline [&_a]:underline-offset-2 [&_a]:text-accent hover:[&_a]:text-accent/80 [&_a]:transition-colors",
1330
- "[&_code]:rounded [&_code]:bg-muted [&_code]:px-1.5 [&_code]:py-0.5 [&_code]:text-sm [&_code]:font-mono",
1331
- "[&_pre]:my-4 [&_pre]:overflow-x-auto [&_pre]:rounded-lg [&_pre]:bg-muted [&_pre]:p-4 [&_pre]:text-sm",
1332
- "[&_pre>code]:bg-transparent [&_pre>code]:p-0"
1333
- ].join(" "),
1334
- children: page.content && /* @__PURE__ */ jsxRuntime.jsx(
1335
- ContentRenderer,
1336
- {
1337
- content: stripLeadingHeading(page.content, page.contentFormat),
1338
- format: (
1339
- // Validate before passing; unknown formats fall through to
1340
- // ContentRenderer's default behavior (markdown rendering)
1341
- // rather than getting silently force-cast to an
1342
- // unsupported variant.
1343
- page.contentFormat === "markdown" || page.contentFormat === "html" ? page.contentFormat : void 0
1344
- )
1345
- }
1346
- )
1347
- }
1348
- ) });
1349
- }
1350
- function PartnerDocumentCard({ doc }) {
1351
- const sizeLabel = formatFileSize(doc.fileSize);
1352
- const ext = doc.fileName?.split(".").pop()?.toUpperCase();
1353
- return /* @__PURE__ */ jsxRuntime.jsxs("article", { className: "flex flex-col gap-3 rounded-xl border border-border bg-card p-5 shadow-sm hover:shadow-md transition-shadow", children: [
1354
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-3", children: [
1355
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-lg bg-muted p-2.5 shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileText, { size: 20, className: "text-muted-foreground" }) }),
1356
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
1357
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "font-semibold text-foreground truncate", children: doc.title }),
1358
- doc.version && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground mt-0.5", children: [
1359
- "Version ",
1360
- doc.version
1361
- ] })
1362
- ] })
1363
- ] }),
1364
- doc.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground line-clamp-3", children: doc.description }),
1365
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-3 mt-auto pt-2 border-t border-border/60", children: [
1366
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-muted-foreground flex items-center gap-2", children: [
1367
- ext && /* @__PURE__ */ jsxRuntime.jsx("span", { children: ext }),
1368
- sizeLabel && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1369
- /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, children: "\xB7" }),
1370
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: sizeLabel })
1371
- ] })
1372
- ] }),
1373
- /* @__PURE__ */ jsxRuntime.jsxs(
1374
- "a",
1375
- {
1376
- href: doc.fileUrl,
1377
- download: doc.fileName ?? void 0,
1378
- onClick: (e) => void downloadDocument(e, doc),
1379
- className: "inline-flex items-center gap-1.5 rounded-md border border-border bg-background px-3 py-1.5 text-sm font-medium text-foreground hover:bg-muted transition-colors",
1380
- children: [
1381
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Download, { size: 14 }),
1382
- "Download"
1383
- ]
1384
- }
1385
- )
1386
- ] })
1387
- ] });
870
+ return /* @__PURE__ */ jsxRuntime.jsx(ProductContext.Provider, { value, children });
1388
871
  }
1389
- function PartnerDocumentsSection({
1390
- title,
1391
- subtitle,
1392
- docs
1393
- }) {
1394
- return /* @__PURE__ */ jsxRuntime.jsx("section", { className: "py-12 md:py-16 bg-muted/20", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "container mx-auto px-4 max-w-5xl", children: [
1395
- /* @__PURE__ */ jsxRuntime.jsxs("header", { className: "mb-8 md:mb-10 text-center", children: [
1396
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl md:text-3xl font-bold text-foreground mb-2", children: title }),
1397
- subtitle && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground max-w-2xl mx-auto", children: subtitle })
1398
- ] }),
1399
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4", children: docs.map((doc) => /* @__PURE__ */ jsxRuntime.jsx(PartnerDocumentCard, { doc }, doc.id)) })
1400
- ] }) });
872
+ function useProduct() {
873
+ return React.useContext(ProductContext);
1401
874
  }
1402
- var DEFAULT_PARTNER_CATEGORY = {
1403
- value: "partnership",
1404
- label: "Partner program"
1405
- };
1406
- function PartnersPage(props) {
1407
- const {
1408
- productName: productNameProp,
1409
- heroTitle = "Partner with us",
1410
- heroSubtitle,
1411
- contentTag = "partner",
1412
- contentSlug,
1413
- documentsTag = "partner",
1414
- documentsTitle = "Partner Documents",
1415
- documentsSubtitle,
1416
- showContactForm = true,
1417
- contactCategory,
1418
- formTitle = "Become a partner",
1419
- formSubtitle,
1420
- contactEmail,
1421
- contactPhone,
1422
- officeAddress,
1423
- responseTime,
1424
- recaptchaSiteKey,
1425
- additionalOptions,
1426
- seoTitle,
1427
- seoDescription,
1428
- seoKeywords,
1429
- className
1430
- } = props;
1431
- const client = useWebSDK();
1432
- const config = useWebSDKConfig();
875
+ function useProductConfig() {
1433
876
  const { product } = useProduct();
1434
- const resolvedProductId = product?.id ?? config?.productId ?? productNameProp ?? "";
1435
- const resolvedProductName = productNameProp ?? product?.name ?? config?.productId ?? "our team";
1436
- const [contentPage, setContentPage] = React.useState(null);
1437
- const [docs, setDocs] = React.useState([]);
1438
- const [loading, setLoading] = React.useState(true);
1439
- React.useEffect(() => {
1440
- if (!client || !resolvedProductId) {
1441
- setContentPage(null);
1442
- setDocs([]);
1443
- setLoading(false);
1444
- return;
1445
- }
1446
- let cancelled = false;
1447
- setLoading(true);
1448
- void Promise.allSettled([
1449
- client.query(PARTNER_CONTENT_QUERY, { productId: resolvedProductId, limit: 50 }),
1450
- client.query(
1451
- PARTNER_DOCUMENTS_QUERY,
1452
- { productId: resolvedProductId, limit: 50 }
1453
- )
1454
- ]).then(([contentResult, docsResult]) => {
1455
- if (cancelled) return;
1456
- if (contentResult.status === "rejected") {
1457
- setContentPage(null);
1458
- } else if (contentResult.status === "fulfilled") {
1459
- const items = contentResult.value.data?.publicContentPages?.items ?? [];
1460
- const match = contentSlug ? items.find((p) => p.slug === contentSlug) : items.find((p) => p.tags?.includes(contentTag));
1461
- setContentPage(match ?? null);
1462
- }
1463
- if (docsResult.status === "rejected") {
1464
- setDocs([]);
1465
- } else if (docsResult.status === "fulfilled") {
1466
- const items = docsResult.value.data?.publicPressKitAssets?.items ?? [];
1467
- const filtered = items.filter(
1468
- (d) => d.category === "DOCUMENT" && d.tags?.includes(documentsTag) && !!d.fileUrl
1469
- ).sort(
1470
- (a, b) => a.sortOrder - b.sortOrder
1471
- );
1472
- setDocs(filtered);
1473
- }
1474
- setLoading(false);
1475
- });
1476
- return () => {
1477
- cancelled = true;
1478
- };
1479
- }, [client, resolvedProductId, contentSlug, contentTag, documentsTag]);
1480
- let partnerCategories;
1481
- if (contactCategory === false) {
1482
- partnerCategories = [];
1483
- } else if (Array.isArray(contactCategory)) {
1484
- partnerCategories = contactCategory.length > 0 ? contactCategory : [DEFAULT_PARTNER_CATEGORY];
1485
- } else {
1486
- partnerCategories = [contactCategory ?? DEFAULT_PARTNER_CATEGORY];
1487
- }
1488
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, children: [
1489
- /* @__PURE__ */ jsxRuntime.jsx(
1490
- PageHead,
1491
- {
1492
- title: seoTitle ?? `${heroTitle} \u2014 ${resolvedProductName}`,
1493
- description: seoDescription ?? heroSubtitle ?? `Join the ${resolvedProductName} partner programme.`,
1494
- keywords: seoKeywords
1495
- }
1496
- ),
1497
- /* @__PURE__ */ jsxRuntime.jsx(PartnerHero, { title: heroTitle, subtitle: heroSubtitle }),
1498
- !loading && contentPage && /* @__PURE__ */ jsxRuntime.jsx(PartnerContentSection, { page: contentPage }),
1499
- !loading && docs.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
1500
- PartnerDocumentsSection,
1501
- {
1502
- title: documentsTitle,
1503
- subtitle: documentsSubtitle,
1504
- docs
1505
- }
1506
- ),
1507
- showContactForm && /* @__PURE__ */ jsxRuntime.jsx(
1508
- ContactPage,
1509
- {
1510
- productName: resolvedProductName,
1511
- heroTitle: formTitle,
1512
- headingLevel: 2,
1513
- heroDescription: formSubtitle ?? `Tell us about your organisation and a partner manager from ${resolvedProductName} will be in touch.`,
1514
- contactEmail,
1515
- contactPhone,
1516
- officeAddress,
1517
- responseTime,
1518
- recaptchaSiteKey,
1519
- additionalOptions,
1520
- categories: partnerCategories,
1521
- categoryLabel: "Partnership type",
1522
- categoryRequired: partnerCategories.length > 0
1523
- }
1524
- )
1525
- ] });
1526
- }
1527
- var ALLOWED_RESUME_TYPES = {
1528
- "application/pdf": "PDF",
1529
- "application/msword": "DOC",
1530
- "application/vnd.openxmlformats-officedocument.wordprocessingml.document": "DOCX"
1531
- };
1532
- var EXT_TO_MIME = {
1533
- pdf: "application/pdf",
1534
- doc: "application/msword",
1535
- docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
1536
- };
1537
- var DEFAULT_MAX_RESUME_BYTES = 10 * 1024 * 1024;
1538
- function resolveResumeType(file) {
1539
- if (ALLOWED_RESUME_TYPES[file.type]) return file.type;
1540
- const ext = file.name.split(".").pop()?.toLowerCase();
1541
- return ext && EXT_TO_MIME[ext] ? EXT_TO_MIME[ext] : null;
877
+ const config = useWebSDKConfig();
878
+ return {
879
+ // Prefer product-level config, fall back to SDK config
880
+ recaptchaSiteKey: product?.recaptchaSiteKey ?? config.recaptchaSiteKey ?? null,
881
+ rybbitSiteId: product?.rybbitSiteId ?? config.rybbitSiteId ?? null,
882
+ contactEmail: product?.contactEmail ?? null,
883
+ supportEmail: product?.supportEmail ?? null,
884
+ notificationEmail: product?.notificationEmail ?? product?.contactEmail ?? null,
885
+ productName: product?.name ?? null,
886
+ logoUrl: product?.logoUrl ?? null
887
+ };
1542
888
  }
1543
- var SUBMIT_JOB_APPLICATION_MUTATION = (
889
+ var newsletterSchema = zod.z.object({
890
+ email: zod.z.string().email("Please enter a valid email address")
891
+ });
892
+ var SUBSCRIBE_MUTATION = (
1544
893
  /* GraphQL */
1545
894
  `
1546
- mutation SubmitJobApplication($input: SubmitJobApplicationInput!) {
1547
- submitJobApplication(input: $input) {
1548
- success
1549
- message
895
+ mutation Subscribe($input: SubscribeInput!) {
896
+ subscribe(input: $input) {
897
+ id
898
+ email
899
+ alreadySubscribed
900
+ status
901
+ subscribedAt
1550
902
  }
1551
903
  }
1552
904
  `
1553
905
  );
1554
- function fileToBase64(file) {
1555
- return new Promise((resolve, reject) => {
1556
- const reader = new FileReader();
1557
- reader.onload = () => {
1558
- const result = typeof reader.result === "string" ? reader.result : "";
1559
- const comma = result.indexOf(",");
1560
- resolve(comma >= 0 ? result.slice(comma + 1) : result);
1561
- };
1562
- reader.onerror = () => reject(new Error("Failed to read the resume file"));
1563
- reader.readAsDataURL(file);
1564
- });
1565
- }
1566
- async function submitApplication(client, data, productId) {
906
+ async function subscribeNewsletter(client, email, productId, recaptchaToken) {
1567
907
  try {
1568
- const result = await client.mutate(SUBMIT_JOB_APPLICATION_MUTATION, {
908
+ const result = await client.mutate(SUBSCRIBE_MUTATION, {
1569
909
  input: {
1570
- firstName: data.firstName,
1571
- lastName: data.lastName,
1572
- email: data.email.trim().toLowerCase(),
1573
- position: data.position,
1574
- resumeBase64: data.resumeBase64,
1575
- resumeFileName: data.resumeFileName,
1576
- resumeContentType: data.resumeContentType,
1577
- productId,
1578
- ...data.phone ? { phone: data.phone } : {},
1579
- ...data.linkedinUrl ? { linkedinUrl: data.linkedinUrl } : {},
1580
- ...data.portfolioUrl ? { portfolioUrl: data.portfolioUrl } : {},
1581
- ...data.coverLetter ? { coverLetter: data.coverLetter } : {},
1582
- ...data.recaptchaToken ? { recaptchaToken: data.recaptchaToken } : {}
910
+ email: email.trim().toLowerCase(),
911
+ platformProductId: productId,
912
+ metadata: {
913
+ source: "website",
914
+ referrer: typeof window !== "undefined" ? window.location.href : "",
915
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
916
+ ...recaptchaToken ? { recaptchaToken } : {}
917
+ }
1583
918
  }
1584
919
  });
1585
920
  if (result.errors?.length) {
921
+ const errorMsg = result.errors[0]?.message ?? "Subscription failed";
922
+ const code = result.errors[0]?.extensions?.code;
923
+ if (code === "DUPLICATE_SUBSCRIBER" || errorMsg.toLowerCase().includes("already subscribed")) {
924
+ return {
925
+ success: true,
926
+ message: "You are already part of this newsletter."
927
+ };
928
+ }
929
+ return { success: false, message: errorMsg };
930
+ }
931
+ if (result.data?.subscribe?.alreadySubscribed) {
1586
932
  return {
1587
- success: false,
1588
- message: result.errors[0]?.message ?? "Failed to submit application"
933
+ success: true,
934
+ message: "You are already part of this newsletter."
1589
935
  };
1590
936
  }
1591
- const response = result.data?.submitJobApplication;
1592
- return response?.success ? { success: true, message: response.message || "Application submitted!" } : {
1593
- success: false,
1594
- message: response?.message || "Failed to submit application"
1595
- };
937
+ return { success: true };
1596
938
  } catch (error) {
1597
- console.error("Job application submission error:", error);
939
+ console.error("Newsletter subscription error:", error);
1598
940
  return { success: false, message: "Network error. Please try again." };
1599
941
  }
1600
942
  }
1601
- function CareersApplyForm({
1602
- productName,
1603
- recaptchaSiteKey,
1604
- positions = [],
1605
- heroTitle = "Become a part of the team",
1606
- heroDescription = "Tell us about yourself and upload your resume \u2014 we'll be in touch.",
1607
- headingLevel = 1,
1608
- maxResumeBytes = DEFAULT_MAX_RESUME_BYTES,
1609
- className,
1610
- seo
1611
- }) {
1612
- const client = useWebSDK();
1613
- const config = useWebSDKConfig();
1614
- const { product } = useProduct();
1615
- const resolvedProductId = product?.id || config.productId;
1616
- const displayName = productName ?? product?.name ?? config.productId;
1617
- const effectiveRecaptchaSiteKey = recaptchaSiteKey ?? product?.recaptchaSiteKey ?? config.recaptchaSiteKey ?? "";
1618
- const recaptchaRef = React.useRef(null);
1619
- const [isSubmitting, setIsSubmitting] = React.useState(false);
1620
- const [submitted, setSubmitted] = React.useState(false);
1621
- const [resumeFile, setResumeFile] = React.useState(null);
1622
- const [resumeType, setResumeType] = React.useState(null);
1623
- const [resumeError, setResumeError] = React.useState(null);
1624
- const maxMb = Math.floor(maxResumeBytes / (1024 * 1024));
1625
- const clearResume = () => {
1626
- setResumeFile(null);
1627
- setResumeType(null);
1628
- };
1629
- const handleFileChange = (e) => {
1630
- setResumeError(null);
1631
- const file = e.target.files?.[0] ?? null;
1632
- if (!file) {
1633
- clearResume();
1634
- return;
943
+ function useRateLimit(maxAttempts, windowMinutes) {
944
+ const rateLimitRef = React.useRef(null);
945
+ const [isLimited, setIsLimited] = React.useState(false);
946
+ const [resetTime, setResetTime] = React.useState(null);
947
+ React.useEffect(() => {
948
+ const state = rateLimitRef.current;
949
+ if (state && Date.now() > state.resetTime) {
950
+ rateLimitRef.current = null;
951
+ setIsLimited(false);
952
+ setResetTime(null);
953
+ } else if (state && state.attempts >= maxAttempts) {
954
+ setIsLimited(true);
955
+ setResetTime(new Date(state.resetTime));
1635
956
  }
1636
- const type = resolveResumeType(file);
1637
- if (!type) {
1638
- clearResume();
1639
- setResumeError("Resume must be a PDF, DOC, or DOCX file.");
1640
- return;
957
+ }, [maxAttempts]);
958
+ function recordAttempt() {
959
+ const now = Date.now();
960
+ const state = rateLimitRef.current;
961
+ if (state && now > state.resetTime) {
962
+ rateLimitRef.current = null;
1641
963
  }
1642
- if (file.size > maxResumeBytes) {
1643
- clearResume();
1644
- setResumeError(`Resume must be ${maxMb}MB or smaller.`);
1645
- return;
1646
- }
1647
- setResumeFile(file);
1648
- setResumeType(type);
1649
- };
1650
- const handleSubmit = async (e) => {
1651
- e.preventDefault();
1652
- const recaptchaToken = recaptchaRef.current?.getValue() ?? "";
1653
- if (effectiveRecaptchaSiteKey && !recaptchaToken) {
1654
- sonner.toast.error("Please complete the reCAPTCHA verification");
1655
- return;
1656
- }
1657
- if (!resumeFile) {
1658
- setResumeError("Please attach your resume (PDF, DOC, or DOCX).");
1659
- return;
1660
- }
1661
- if (!resolvedProductId) {
1662
- console.warn(
1663
- "[CareersApplyForm] No productId resolved \u2014 submission will not be attributed to a product."
1664
- );
1665
- }
1666
- const form = e.currentTarget;
1667
- setIsSubmitting(true);
1668
- try {
1669
- const formData = new FormData(form);
1670
- const resumeBase64 = await fileToBase64(resumeFile);
1671
- const result = await submitApplication(
1672
- client,
1673
- {
1674
- firstName: formData.get("firstName") ?? "",
1675
- lastName: formData.get("lastName") ?? "",
1676
- email: formData.get("email") ?? "",
1677
- phone: formData.get("phone") || void 0,
1678
- position: formData.get("position") ?? "",
1679
- linkedinUrl: formData.get("linkedinUrl") || void 0,
1680
- portfolioUrl: formData.get("portfolioUrl") || void 0,
1681
- coverLetter: formData.get("coverLetter") || void 0,
1682
- resumeBase64,
1683
- resumeFileName: resumeFile.name,
1684
- // Use the resolved type (handles browsers that report an empty
1685
- // file.type for .doc/.docx).
1686
- resumeContentType: resumeType ?? resumeFile.type,
1687
- recaptchaToken
1688
- },
1689
- resolvedProductId
1690
- );
1691
- if (result.success) {
1692
- setSubmitted(true);
1693
- sonner.toast.success(result.message ?? "Application submitted!");
1694
- } else {
1695
- sonner.toast.error(result.message ?? "Failed to submit application");
1696
- recaptchaRef.current?.reset();
1697
- }
1698
- } catch (err) {
1699
- console.error("Careers form error:", err);
1700
- sonner.toast.error("Something went wrong. Please try again.");
1701
- recaptchaRef.current?.reset();
1702
- } finally {
1703
- setIsSubmitting(false);
1704
- }
1705
- };
1706
- const Heading = headingLevel === 2 ? "h2" : "h1";
1707
- const headingClass = headingLevel === 2 ? "text-2xl md:text-3xl font-bold mb-3" : "text-3xl sm:text-4xl md:text-5xl font-bold mb-4 sm:mb-6";
1708
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, children: [
1709
- seo && /* @__PURE__ */ jsxRuntime.jsx(
1710
- PageHead,
1711
- {
1712
- title: seo.title ?? `Careers \u2014 ${displayName}`,
1713
- description: seo.description ?? heroDescription,
1714
- keywords: seo.keywords
1715
- }
1716
- ),
1717
- /* @__PURE__ */ jsxRuntime.jsx(
1718
- "section",
1719
- {
1720
- className: headingLevel === 2 ? "py-12 md:py-16" : "bg-gradient-to-b from-background to-muted/20 py-12 sm:py-16 md:py-20 lg:py-24",
1721
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-3xl mx-auto px-4 sm:px-6 lg:px-8", children: [
1722
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center mb-8 sm:mb-10", children: [
1723
- /* @__PURE__ */ jsxRuntime.jsx(Heading, { className: headingClass, children: heroTitle }),
1724
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-lg text-muted-foreground leading-relaxed", children: heroDescription })
1725
- ] }),
1726
- submitted ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center rounded-xl border border-border bg-card p-10", children: [
1727
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle, { className: "mx-auto mb-4 h-12 w-12 text-accent" }),
1728
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-xl font-semibold text-foreground mb-2", children: "Application received" }),
1729
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-muted-foreground", children: [
1730
- "Thanks for applying to ",
1731
- displayName,
1732
- ". We've emailed you a confirmation and our team will be in touch."
1733
- ] })
1734
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, className: "space-y-5", children: [
1735
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-5", children: [
1736
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1737
- /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "firstName", children: "First name *" }),
1738
- /* @__PURE__ */ jsxRuntime.jsx(
1739
- Input,
1740
- {
1741
- id: "firstName",
1742
- name: "firstName",
1743
- required: true,
1744
- placeholder: "Ada"
1745
- }
1746
- )
1747
- ] }),
1748
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1749
- /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "lastName", children: "Last name *" }),
1750
- /* @__PURE__ */ jsxRuntime.jsx(
1751
- Input,
1752
- {
1753
- id: "lastName",
1754
- name: "lastName",
1755
- required: true,
1756
- placeholder: "Lovelace"
1757
- }
1758
- )
1759
- ] })
1760
- ] }),
1761
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-5", children: [
1762
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1763
- /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "email", children: "Email *" }),
1764
- /* @__PURE__ */ jsxRuntime.jsx(
1765
- Input,
1766
- {
1767
- id: "email",
1768
- name: "email",
1769
- type: "email",
1770
- required: true,
1771
- placeholder: "ada@example.com"
1772
- }
1773
- )
1774
- ] }),
1775
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1776
- /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "phone", children: "Phone" }),
1777
- /* @__PURE__ */ jsxRuntime.jsx(
1778
- Input,
1779
- {
1780
- id: "phone",
1781
- name: "phone",
1782
- placeholder: "+1 555 010 0100"
1783
- }
1784
- )
1785
- ] })
1786
- ] }),
1787
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1788
- /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "position", children: "Position *" }),
1789
- positions.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(
1790
- "select",
1791
- {
1792
- id: "position",
1793
- name: "position",
1794
- required: true,
1795
- defaultValue: "",
1796
- className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring",
1797
- children: [
1798
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: true, children: "Select a role" }),
1799
- positions.map((p) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: p, children: p }, p))
1800
- ]
1801
- }
1802
- ) : /* @__PURE__ */ jsxRuntime.jsx(
1803
- Input,
1804
- {
1805
- id: "position",
1806
- name: "position",
1807
- required: true,
1808
- placeholder: "Role you're applying for"
1809
- }
1810
- )
1811
- ] }),
1812
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-5", children: [
1813
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1814
- /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "linkedinUrl", children: "LinkedIn" }),
1815
- /* @__PURE__ */ jsxRuntime.jsx(
1816
- Input,
1817
- {
1818
- id: "linkedinUrl",
1819
- name: "linkedinUrl",
1820
- type: "url",
1821
- placeholder: "https://linkedin.com/in/\u2026"
1822
- }
1823
- )
1824
- ] }),
1825
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1826
- /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "portfolioUrl", children: "Portfolio / GitHub" }),
1827
- /* @__PURE__ */ jsxRuntime.jsx(
1828
- Input,
1829
- {
1830
- id: "portfolioUrl",
1831
- name: "portfolioUrl",
1832
- type: "url",
1833
- placeholder: "https://\u2026"
1834
- }
1835
- )
1836
- ] })
1837
- ] }),
1838
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1839
- /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "coverLetter", children: "Cover letter" }),
1840
- /* @__PURE__ */ jsxRuntime.jsx(
1841
- Textarea,
1842
- {
1843
- id: "coverLetter",
1844
- name: "coverLetter",
1845
- rows: 5,
1846
- placeholder: "Tell us why you'd be a great fit\u2026"
1847
- }
1848
- )
1849
- ] }),
1850
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1851
- /* @__PURE__ */ jsxRuntime.jsxs(Label, { htmlFor: "resume", children: [
1852
- "Resume * (PDF, DOC, or DOCX \u2014 max ",
1853
- maxMb,
1854
- "MB)"
1855
- ] }),
1856
- /* @__PURE__ */ jsxRuntime.jsxs(
1857
- "label",
1858
- {
1859
- htmlFor: "resume",
1860
- className: "mt-1 flex cursor-pointer items-center gap-3 rounded-md border border-dashed border-input bg-background px-4 py-3 text-sm hover:bg-muted/40 transition-colors",
1861
- children: [
1862
- /* @__PURE__ */ jsxRuntime.jsx(
1863
- lucideReact.Upload,
1864
- {
1865
- className: "h-5 w-5 text-muted-foreground",
1866
- "aria-hidden": "true"
1867
- }
1868
- ),
1869
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-foreground", children: resumeFile ? resumeFile.name : "Choose a file\u2026" }),
1870
- /* @__PURE__ */ jsxRuntime.jsx(
1871
- "input",
1872
- {
1873
- id: "resume",
1874
- name: "resume",
1875
- type: "file",
1876
- accept: ".pdf,.doc,.docx,application/pdf,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document",
1877
- className: "sr-only",
1878
- "aria-describedby": resumeError ? "resume-error" : void 0,
1879
- "aria-invalid": resumeError ? true : void 0,
1880
- onChange: handleFileChange
1881
- }
1882
- )
1883
- ]
1884
- }
1885
- ),
1886
- resumeError && /* @__PURE__ */ jsxRuntime.jsx(
1887
- "p",
1888
- {
1889
- id: "resume-error",
1890
- role: "alert",
1891
- className: "mt-1 text-sm text-destructive",
1892
- children: resumeError
1893
- }
1894
- )
1895
- ] }),
1896
- effectiveRecaptchaSiteKey && /* @__PURE__ */ jsxRuntime.jsx(
1897
- ReCAPTCHA3__default.default,
1898
- {
1899
- ref: recaptchaRef,
1900
- sitekey: effectiveRecaptchaSiteKey
1901
- }
1902
- ),
1903
- /* @__PURE__ */ jsxRuntime.jsx(
1904
- Button,
1905
- {
1906
- type: "submit",
1907
- size: "lg",
1908
- disabled: isSubmitting,
1909
- className: "w-full sm:w-auto",
1910
- children: isSubmitting ? "Submitting\u2026" : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1911
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Send, { className: "mr-2 h-4 w-4" }),
1912
- " Submit application"
1913
- ] })
1914
- }
1915
- )
1916
- ] })
1917
- ] })
1918
- }
1919
- )
1920
- ] });
1921
- }
1922
- var newsletterSchema = zod.z.object({
1923
- email: zod.z.string().email("Please enter a valid email address")
1924
- });
1925
- var SUBSCRIBE_MUTATION = (
1926
- /* GraphQL */
1927
- `
1928
- mutation Subscribe($input: SubscribeInput!) {
1929
- subscribe(input: $input) {
1930
- id
1931
- email
1932
- alreadySubscribed
1933
- status
1934
- subscribedAt
1935
- }
1936
- }
1937
- `
1938
- );
1939
- async function subscribeNewsletter(client, email, productId, recaptchaToken) {
1940
- try {
1941
- const result = await client.mutate(SUBSCRIBE_MUTATION, {
1942
- input: {
1943
- email: email.trim().toLowerCase(),
1944
- platformProductId: productId,
1945
- metadata: {
1946
- source: "website",
1947
- referrer: typeof window !== "undefined" ? window.location.href : "",
1948
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1949
- ...recaptchaToken ? { recaptchaToken } : {}
1950
- }
1951
- }
1952
- });
1953
- if (result.errors?.length) {
1954
- const errorMsg = result.errors[0]?.message ?? "Subscription failed";
1955
- const code = result.errors[0]?.extensions?.code;
1956
- if (code === "DUPLICATE_SUBSCRIBER" || errorMsg.toLowerCase().includes("already subscribed")) {
1957
- return {
1958
- success: true,
1959
- message: "You are already part of this newsletter."
1960
- };
1961
- }
1962
- return { success: false, message: errorMsg };
1963
- }
1964
- if (result.data?.subscribe?.alreadySubscribed) {
1965
- return {
1966
- success: true,
1967
- message: "You are already part of this newsletter."
1968
- };
1969
- }
1970
- return { success: true };
1971
- } catch (error) {
1972
- console.error("Newsletter subscription error:", error);
1973
- return { success: false, message: "Network error. Please try again." };
1974
- }
1975
- }
1976
- function useRateLimit(maxAttempts, windowMinutes) {
1977
- const rateLimitRef = React.useRef(null);
1978
- const [isLimited, setIsLimited] = React.useState(false);
1979
- const [resetTime, setResetTime] = React.useState(null);
1980
- React.useEffect(() => {
1981
- const state = rateLimitRef.current;
1982
- if (state && Date.now() > state.resetTime) {
1983
- rateLimitRef.current = null;
1984
- setIsLimited(false);
1985
- setResetTime(null);
1986
- } else if (state && state.attempts >= maxAttempts) {
1987
- setIsLimited(true);
1988
- setResetTime(new Date(state.resetTime));
1989
- }
1990
- }, [maxAttempts]);
1991
- function recordAttempt() {
1992
- const now = Date.now();
1993
- const state = rateLimitRef.current;
1994
- if (state && now > state.resetTime) {
1995
- rateLimitRef.current = null;
1996
- }
1997
- const current = rateLimitRef.current;
1998
- if (!current) {
1999
- rateLimitRef.current = {
2000
- attempts: 1,
2001
- resetTime: now + windowMinutes * 60 * 1e3
2002
- };
2003
- return false;
964
+ const current = rateLimitRef.current;
965
+ if (!current) {
966
+ rateLimitRef.current = {
967
+ attempts: 1,
968
+ resetTime: now + windowMinutes * 60 * 1e3
969
+ };
970
+ return false;
2004
971
  }
2005
972
  current.attempts += 1;
2006
973
  if (current.attempts >= maxAttempts) {
@@ -2208,7 +1175,7 @@ function NewsletterPage({
2208
1175
  errors.email && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-destructive", children: errors.email.message })
2209
1176
  ] }),
2210
1177
  effectiveRecaptchaSiteKey && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(
2211
- ReCAPTCHA3__default.default,
1178
+ ReCAPTCHA2__default.default,
2212
1179
  {
2213
1180
  ref: recaptchaRef,
2214
1181
  sitekey: effectiveRecaptchaSiteKey
@@ -2260,7 +1227,7 @@ function NewsletterPage({
2260
1227
  errors.email && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-destructive", children: errors.email.message })
2261
1228
  ] }),
2262
1229
  effectiveRecaptchaSiteKey && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center sm:justify-start", children: /* @__PURE__ */ jsxRuntime.jsx(
2263
- ReCAPTCHA3__default.default,
1230
+ ReCAPTCHA2__default.default,
2264
1231
  {
2265
1232
  ref: recaptchaRef,
2266
1233
  sitekey: effectiveRecaptchaSiteKey
@@ -2428,25 +1395,14 @@ function NewsletterForm({
2428
1395
  background: rgba(0,0,0,0.3) !important;
2429
1396
  }
2430
1397
  ` }),
2431
- /* @__PURE__ */ jsxRuntime.jsx(
2432
- "div",
1398
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "relative", zIndex: 50 }, children: /* @__PURE__ */ jsxRuntime.jsx(
1399
+ ReCAPTCHA2__default.default,
2433
1400
  {
2434
- style: {
2435
- position: "relative",
2436
- zIndex: 50,
2437
- display: "flex",
2438
- justifyContent: "flex-start"
2439
- },
2440
- children: /* @__PURE__ */ jsxRuntime.jsx(
2441
- ReCAPTCHA3__default.default,
2442
- {
2443
- ref: recaptchaRef,
2444
- sitekey: effectiveRecaptchaSiteKey,
2445
- size: "normal"
2446
- }
2447
- )
1401
+ ref: recaptchaRef,
1402
+ sitekey: effectiveRecaptchaSiteKey,
1403
+ size: "compact"
2448
1404
  }
2449
- )
1405
+ ) })
2450
1406
  ] }),
2451
1407
  effectiveRecaptchaSiteKey && /* @__PURE__ */ jsxRuntime.jsx(Button, { type: "submit", disabled: isLoading, className: "w-full", children: isLoading ? "Subscribing..." : buttonText })
2452
1408
  ]
@@ -2454,9 +1410,9 @@ function NewsletterForm({
2454
1410
  )
2455
1411
  ] });
2456
1412
  }
2457
- var GET_PLANS_QUERY = `
2458
- query GetPublicPlans($productId: String!) {
2459
- publicPlans(productId: $productId) {
1413
+ var GET_ALL_PLANS_QUERY = `
1414
+ query GetAllPlans {
1415
+ getAllPlans {
2460
1416
  id
2461
1417
  name
2462
1418
  price
@@ -2501,15 +1457,11 @@ function formatInterval(duration, billingIntervals) {
2501
1457
  const intervals = billingIntervals || defaultIntervals;
2502
1458
  return intervals[duration] || `/${duration}`;
2503
1459
  }
2504
- function isEnterprisePlan(planName) {
2505
- const name = planName.toLowerCase();
2506
- return name.includes("team") || name.includes("business") || name.includes("enterprise");
2507
- }
2508
1460
  function detectPlanTier(planName) {
2509
1461
  const name = planName.toLowerCase();
2510
- if (name.includes("free") || name.includes("basic") || name.includes("starter") || name.includes("hobby")) {
1462
+ if (name.includes("basic") || name.includes("starter")) {
2511
1463
  return "basic";
2512
- } else if (name.includes("pro") || name.includes("professional") || name.includes("premium") || name.includes("team")) {
1464
+ } else if (name.includes("pro") || name.includes("professional") || name.includes("premium")) {
2513
1465
  return "professional";
2514
1466
  } else if (name.includes("enterprise") || name.includes("business")) {
2515
1467
  return "enterprise";
@@ -2529,8 +1481,7 @@ function getMetadataForPlan(planName, planMetadata) {
2529
1481
  defaultDescription: meta.defaultDescription || "Choose this plan",
2530
1482
  popular: meta.popular ?? false,
2531
1483
  ctaText: meta.ctaText || "Get Started",
2532
- ctaType: meta.ctaType || "signup",
2533
- features: meta.features
1484
+ ctaType: meta.ctaType || "signup"
2534
1485
  };
2535
1486
  }
2536
1487
  const tier = detectPlanTier(planName);
@@ -2553,54 +1504,37 @@ function getMetadataForPlan(planName, planMetadata) {
2553
1504
  ctaType: "signup"
2554
1505
  };
2555
1506
  }
2556
- function humanizeQuotaName(name) {
2557
- const parts = name.split(".");
2558
- const resource = parts.length >= 3 ? parts[parts.length - 2] : parts[parts.length - 1];
2559
- return resource.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
2560
- }
2561
1507
  function transformQuotaToFeature(quota) {
2562
1508
  const { name, limits } = quota;
2563
1509
  if (!limits || typeof limits !== "object") {
2564
- return humanizeQuotaName(name);
1510
+ return name;
2565
1511
  }
2566
1512
  const limitsObj = limits;
2567
1513
  const value = limitsObj.value;
2568
1514
  const unit = limitsObj.unit;
2569
1515
  const type = limitsObj.type;
2570
1516
  const description = limitsObj.description;
2571
- if (typeof description === "string" && description) {
2572
- if (type === "number" && typeof value === "number") {
2573
- return `${value.toLocaleString()} ${description}`;
2574
- }
2575
- return description;
2576
- }
2577
- if (typeof unit === "string" && unit) {
2578
- if (typeof value === "number") {
2579
- return `${value.toLocaleString()} ${unit}`;
2580
- }
2581
- return `${unit}`;
2582
- }
2583
- const label = humanizeQuotaName(name);
2584
- if (type === "boolean") {
2585
- return label;
1517
+ if (type === "number" && typeof value === "number") {
1518
+ const formattedValue = value.toLocaleString();
1519
+ return `${formattedValue} ${unit || ""}${description ? " - " + description : ""}`.trim();
1520
+ } else if (type === "boolean") {
1521
+ return typeof description === "string" && description || name;
2586
1522
  }
2587
1523
  if (typeof value === "number") {
2588
- return `${value.toLocaleString()} ${label}`;
1524
+ const formattedValue = value.toLocaleString();
1525
+ return `${name}: ${formattedValue} ${unit || ""}`.trim();
2589
1526
  }
2590
- return label;
1527
+ return name;
2591
1528
  }
2592
1529
  function extractDisplayFeatures(features, planName, fallbackFeatures) {
2593
- const tier = detectPlanTier(planName);
2594
- if (tier) {
2595
- const tierFeatures = getFallbackFeaturesForTier(tier, fallbackFeatures);
2596
- if (tierFeatures.length > 0) {
2597
- return tierFeatures;
2598
- }
2599
- }
2600
1530
  const quotaFeatures = features.filter((f) => f.quota).map((f) => transformQuotaToFeature(f.quota));
2601
1531
  if (quotaFeatures.length > 0) {
2602
1532
  return quotaFeatures;
2603
1533
  }
1534
+ const tier = detectPlanTier(planName);
1535
+ if (tier) {
1536
+ return getFallbackFeaturesForTier(tier, fallbackFeatures);
1537
+ }
2604
1538
  return [];
2605
1539
  }
2606
1540
  function transformToDisplayCard(plan, props) {
@@ -2611,7 +1545,7 @@ function transformToDisplayCard(plan, props) {
2611
1545
  props.currencies
2612
1546
  );
2613
1547
  const interval = formatInterval(plan.duration, props.billingIntervals);
2614
- const features = metadata.features && metadata.features.length > 0 ? metadata.features : extractDisplayFeatures(
1548
+ const features = extractDisplayFeatures(
2615
1549
  plan.features,
2616
1550
  plan.name,
2617
1551
  props.fallbackFeatures
@@ -2649,25 +1583,22 @@ function PricingSection(props) {
2649
1583
  const [billingCycle, setBillingCycle] = React.useState(
2650
1584
  "monthly"
2651
1585
  );
2652
- const [audience, setAudience] = React.useState(
2653
- "individual"
2654
- );
2655
1586
  React.useEffect(() => {
2656
1587
  let cancelled = false;
2657
1588
  async function loadPlans() {
2658
1589
  try {
2659
1590
  setLoading(true);
2660
- const response = await client.query(GET_PLANS_QUERY, {
2661
- productId: resolvedProductId
2662
- });
1591
+ const response = await client.query(GET_ALL_PLANS_QUERY);
2663
1592
  if (cancelled) return;
2664
1593
  if (response.errors && response.errors.length > 0) {
2665
1594
  throw new Error(response.errors[0].message);
2666
1595
  }
2667
- if (!response.data?.publicPlans) {
1596
+ if (!response.data?.getAllPlans) {
2668
1597
  throw new Error("No plans data received");
2669
1598
  }
2670
- const cards = response.data.publicPlans.filter((plan) => plan.isActive).map((plan) => transformToDisplayCard(plan, props)).sort((a, b) => a.priceRaw - b.priceRaw);
1599
+ const cards = response.data.getAllPlans.filter(
1600
+ (plan) => plan.isActive && (!resolvedProductId || plan.productID === resolvedProductId)
1601
+ ).map((plan) => transformToDisplayCard(plan, props)).sort((a, b) => a.priceRaw - b.priceRaw);
2671
1602
  setAllPlans(cards);
2672
1603
  setError(null);
2673
1604
  } catch (err) {
@@ -2685,56 +1616,13 @@ function PricingSection(props) {
2685
1616
  cancelled = true;
2686
1617
  };
2687
1618
  }, [client, resolvedProductId]);
2688
- const individualAllPlans = allPlans.filter((p) => !isEnterprisePlan(p.name));
2689
- const enterpriseApiPlans = allPlans.filter((p) => isEnterprisePlan(p.name));
2690
- const individualPlans = individualAllPlans.filter(
1619
+ const plans = allPlans.filter(
2691
1620
  (plan) => billingCycle === "monthly" ? plan.duration === "monthly" : plan.duration === "yearly"
2692
1621
  );
2693
- const enterpriseCards = enterpriseApiPlans.map((p) => {
2694
- if (p.priceRaw === 0 && p.name.toLowerCase().includes("enterprise")) {
2695
- return {
2696
- ...p,
2697
- price: "Custom",
2698
- interval: "",
2699
- ctaText: "Contact Sales",
2700
- ctaLink: props.contactUrl || "/contact"
2701
- };
2702
- }
2703
- return p;
2704
- });
2705
- const hasApiEnterprisePlans = enterpriseCards.length > 0;
2706
- const defaultEnterpriseCard = {
2707
- name: "Enterprise",
2708
- subtitle: "For organizations that need more",
2709
- description: "For organizations that need more",
2710
- price: "Custom",
2711
- period: "",
2712
- interval: "",
2713
- button: "Contact Sales",
2714
- buttonLink: props.contactUrl || "/contact",
2715
- ctaText: "Contact Sales",
2716
- ctaLink: props.contactUrl || "/contact",
2717
- popular: false,
2718
- features: props.fallbackFeatures?.enterprise || [
2719
- "Unlimited everything",
2720
- "SSO & SAML",
2721
- "Audit logs",
2722
- "Dedicated support",
2723
- "Self-hosted option"
2724
- ]
2725
- };
2726
- const resolvedEnterprisePlans = hasApiEnterprisePlans ? enterpriseCards : props.enterprisePlans ? props.enterprisePlans : [defaultEnterpriseCard];
2727
- const plans = props.showAudienceTabs && audience === "enterprise" ? [] : individualPlans;
2728
1622
  if (loading) {
2729
1623
  return /* @__PURE__ */ jsxRuntime.jsx("section", { id, className: `py-12 sm:py-16 md:py-20 ${className}`, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "container mx-auto px-4 sm:px-6", children: [
2730
1624
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center mb-8 sm:mb-12", children: [
2731
- sectionHeading ? /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl sm:text-3xl md:text-4xl font-bold mb-3 sm:mb-4", children: sectionHeading }) : (
2732
- // Visually hidden but DOM-present heading so axe's heading-order
2733
- // rule still sees an h2 between the consumer's hero h1 and the
2734
- // h3 plan-card titles. See `getMetadataForPlan` for why a falsy
2735
- // sectionHeading is the canonical "no visible heading" signal.
2736
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "sr-only", children: "Pricing" })
2737
- ),
1625
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl sm:text-3xl md:text-4xl font-bold mb-3 sm:mb-4", children: sectionHeading }),
2738
1626
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground text-sm sm:text-base md:text-lg", children: "Loading pricing information..." })
2739
1627
  ] }),
2740
1628
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid md:grid-cols-3 gap-6 sm:gap-8", children: [1, 2, 3].map((i) => /* @__PURE__ */ jsxRuntime.jsxs(
@@ -2755,205 +1643,90 @@ function PricingSection(props) {
2755
1643
  }
2756
1644
  if (error && plans.length === 0) {
2757
1645
  return /* @__PURE__ */ jsxRuntime.jsx("section", { id, className: `py-12 sm:py-16 md:py-20 ${className}`, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "container mx-auto px-4 sm:px-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
2758
- sectionHeading ? /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl sm:text-3xl md:text-4xl font-bold mb-4", children: sectionHeading }) : /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "sr-only", children: "Pricing" }),
1646
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl sm:text-3xl md:text-4xl font-bold mb-4", children: sectionHeading }),
2759
1647
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground mb-8", children: error }),
2760
1648
  /* @__PURE__ */ jsxRuntime.jsx(Button, { onClick: () => window.location.reload(), children: "Retry" })
2761
1649
  ] }) }) });
2762
1650
  }
2763
1651
  return /* @__PURE__ */ jsxRuntime.jsx("section", { id, className: `py-12 sm:py-16 md:py-20 ${className}`, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "container mx-auto px-4 sm:px-6", children: [
2764
1652
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center mb-8 sm:mb-12", children: [
2765
- sectionHeading ? /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl sm:text-3xl md:text-4xl font-bold mb-3 sm:mb-4", children: sectionHeading }) : /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "sr-only", children: "Pricing" }),
1653
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl sm:text-3xl md:text-4xl font-bold mb-3 sm:mb-4", children: sectionHeading }),
2766
1654
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground text-sm sm:text-base md:text-lg mb-6", children: sectionSubtitle }),
2767
- props.showAudienceTabs && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center mb-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center bg-muted p-1.5 rounded-xl", children: [
1655
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center gap-3 bg-muted p-1.5 rounded-lg", children: [
2768
1656
  /* @__PURE__ */ jsxRuntime.jsx(
2769
1657
  "button",
2770
1658
  {
2771
- type: "button",
2772
- onClick: () => setAudience("individual"),
2773
- className: `px-6 sm:px-8 py-2.5 rounded-lg text-sm font-semibold transition-all ${audience === "individual" ? "bg-background shadow-sm text-foreground" : "text-muted-foreground hover:text-foreground"}`,
2774
- children: "Individual"
2775
- }
2776
- ),
2777
- /* @__PURE__ */ jsxRuntime.jsx(
2778
- "button",
2779
- {
2780
- type: "button",
2781
- onClick: () => setAudience("enterprise"),
2782
- className: `px-6 sm:px-8 py-2.5 rounded-lg text-sm font-semibold transition-all ${audience === "enterprise" ? "bg-background shadow-sm text-foreground" : "text-muted-foreground hover:text-foreground"}`,
2783
- children: "Enterprise"
2784
- }
2785
- )
2786
- ] }) }),
2787
- (audience === "individual" || !props.showAudienceTabs) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center bg-muted/60 p-1.5 rounded-xl", children: [
2788
- /* @__PURE__ */ jsxRuntime.jsx(
2789
- "button",
2790
- {
2791
- type: "button",
2792
1659
  onClick: () => setBillingCycle("monthly"),
2793
- className: `px-5 sm:px-6 py-2 rounded-lg text-sm font-medium transition-all ${billingCycle === "monthly" ? "bg-background shadow-sm text-foreground" : "text-muted-foreground hover:text-foreground"}`,
1660
+ className: `px-6 py-2 rounded-md text-sm font-medium transition-all ${billingCycle === "monthly" ? "bg-background shadow-sm text-foreground" : "text-muted-foreground hover:text-foreground"}`,
2794
1661
  children: "Monthly"
2795
1662
  }
2796
1663
  ),
2797
1664
  /* @__PURE__ */ jsxRuntime.jsx(
2798
1665
  "button",
2799
1666
  {
2800
- type: "button",
2801
1667
  onClick: () => setBillingCycle("yearly"),
2802
- className: `px-5 sm:px-6 py-2 rounded-lg text-sm font-medium transition-all ${billingCycle === "yearly" ? "bg-background shadow-sm text-foreground" : "text-muted-foreground hover:text-foreground"}`,
1668
+ className: `px-6 py-2 rounded-md text-sm font-medium transition-all ${billingCycle === "yearly" ? "bg-background shadow-sm text-foreground" : "text-muted-foreground hover:text-foreground"}`,
2803
1669
  children: "Yearly"
2804
1670
  }
2805
1671
  )
2806
- ] }) })
2807
- ] }),
2808
- props.showAudienceTabs && audience === "enterprise" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto max-w-3xl text-center mb-8", children: [
2809
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-xl sm:text-2xl font-bold text-foreground", children: props.enterpriseHeading || "Enterprise Solutions" }),
2810
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-3 text-muted-foreground", children: props.enterpriseSubtitle || "Built for teams, agencies, and organizations that need advanced collaboration, security, support, and scale." })
1672
+ ] })
2811
1673
  ] }),
2812
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid md:grid-cols-3 gap-6 sm:gap-8 max-w-6xl mx-auto items-stretch", children: props.showAudienceTabs && audience === "enterprise" ? (
2813
- /* Enterprise cards — from API, static prop, or built-in default */
2814
- resolvedEnterprisePlans.map((ep) => {
2815
- const subtitle = ep.subtitle || ep.description || "";
2816
- const btnText = ep.button || ep.ctaText || "Get Started";
2817
- const btnLink = ep.buttonLink || ep.ctaLink || props.appLoginUrl;
2818
- const period = ep.period || ep.interval || "";
2819
- const note = ep.note || "";
2820
- const badge = ep.badge || "";
2821
- const isPopular = ep.popular;
2822
- const features = ep.features || [];
2823
- const price = String(ep.price);
2824
- return /* @__PURE__ */ jsxRuntime.jsxs(
2825
- "div",
2826
- {
2827
- className: `bg-card rounded-xl p-6 sm:p-8 transition-all duration-300 flex flex-col ${isPopular ? "border-2 border-primary shadow-xl ring-1 ring-primary/20" : "border hover:shadow-lg hover:border-primary/50"}`,
2828
- children: [
2829
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center mb-6", children: [
2830
- isPopular && badge && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block bg-primary text-primary-foreground px-4 py-1 rounded-full text-xs font-semibold mb-3", children: badge }),
2831
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-xl sm:text-2xl font-bold mb-2", children: ep.name }),
2832
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground text-sm", children: subtitle })
2833
- ] }),
2834
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center mb-2", children: [
2835
- price === "Custom" ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-3xl sm:text-4xl font-bold", children: "Custom" }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline justify-center gap-1", children: [
2836
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-4xl sm:text-5xl font-extrabold", children: price }),
2837
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground text-lg", children: period })
2838
- ] }),
2839
- note && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-sm font-medium text-primary", children: note })
2840
- ] }),
2841
- /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "space-y-3 mb-8 mt-6 flex-1", children: features.map((feature) => /* @__PURE__ */ jsxRuntime.jsxs(
2842
- "li",
2843
- {
2844
- className: "text-sm flex items-start gap-2",
2845
- children: [
2846
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-primary mt-0.5 flex-shrink-0", children: "\u2713" }),
2847
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: feature })
2848
- ]
2849
- },
2850
- feature
2851
- )) }),
2852
- btnLink.startsWith("/") ? /* @__PURE__ */ jsxRuntime.jsx(
2853
- Button,
1674
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid md:grid-cols-3 gap-6 sm:gap-8 max-w-6xl mx-auto items-stretch", children: plans.map((plan) => /* @__PURE__ */ jsxRuntime.jsxs(
1675
+ "div",
1676
+ {
1677
+ className: `bg-card rounded-xl p-6 sm:p-8 transition-all duration-300 flex flex-col ${plan.popular ? "border-2 border-primary shadow-xl ring-1 ring-primary/20" : "border hover:shadow-lg hover:border-primary/50"}`,
1678
+ children: [
1679
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center mb-6", children: [
1680
+ plan.popular && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block bg-primary text-primary-foreground px-4 py-1 rounded-full text-xs font-semibold mb-3", children: "Most Popular" }),
1681
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-xl sm:text-2xl font-bold mb-2", children: plan.name }),
1682
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground text-sm", children: plan.description })
1683
+ ] }),
1684
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center mb-8", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline justify-center gap-1", children: [
1685
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-4xl sm:text-5xl font-bold", children: plan.price }),
1686
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground text-lg", children: plan.interval })
1687
+ ] }) }),
1688
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "space-y-3 mb-8 flex-1", children: plan.features.map((feature, index) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "text-sm flex items-start gap-2", children: [
1689
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-primary mt-0.5 flex-shrink-0", children: "\u2713" }),
1690
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: feature })
1691
+ ] }, index)) }),
1692
+ plan.ctaLink.startsWith("/") ? /* @__PURE__ */ jsxRuntime.jsx(
1693
+ Button,
1694
+ {
1695
+ className: "w-full h-11",
1696
+ variant: plan.popular ? "default" : "outline",
1697
+ size: "lg",
1698
+ asChild: true,
1699
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1700
+ "a",
2854
1701
  {
2855
- className: "w-full h-11",
2856
- variant: isPopular ? "default" : "outline",
2857
- size: "lg",
2858
- asChild: true,
2859
- children: /* @__PURE__ */ jsxRuntime.jsx(
2860
- "a",
2861
- {
2862
- href: btnLink,
2863
- onClick: () => window.scrollTo({ top: 0, behavior: "smooth" }),
2864
- children: btnText
2865
- }
2866
- )
1702
+ href: plan.ctaLink,
1703
+ onClick: () => window.scrollTo({ top: 0, behavior: "smooth" }),
1704
+ children: plan.ctaText
2867
1705
  }
2868
- ) : /* @__PURE__ */ jsxRuntime.jsx(
2869
- Button,
1706
+ )
1707
+ }
1708
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
1709
+ Button,
1710
+ {
1711
+ className: "w-full h-11",
1712
+ variant: plan.popular ? "default" : "outline",
1713
+ size: "lg",
1714
+ asChild: true,
1715
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1716
+ "a",
2870
1717
  {
2871
- className: "w-full h-11",
2872
- variant: isPopular ? "default" : "outline",
2873
- size: "lg",
2874
- asChild: true,
2875
- children: /* @__PURE__ */ jsxRuntime.jsx(
2876
- "a",
2877
- {
2878
- href: btnLink,
2879
- target: "_blank",
2880
- rel: "noopener noreferrer",
2881
- children: btnText
2882
- }
2883
- )
1718
+ href: plan.ctaLink,
1719
+ target: "_blank",
1720
+ rel: "noopener noreferrer",
1721
+ children: plan.ctaText
2884
1722
  }
2885
1723
  )
2886
- ]
2887
- },
2888
- ep.name
2889
- );
2890
- })
2891
- ) : (
2892
- /* Individual API-fetched cards */
2893
- plans.map((plan) => /* @__PURE__ */ jsxRuntime.jsxs(
2894
- "div",
2895
- {
2896
- className: `bg-card rounded-xl p-6 sm:p-8 transition-all duration-300 flex flex-col ${plan.popular ? "border-2 border-primary shadow-xl ring-1 ring-primary/20" : "border hover:shadow-lg hover:border-primary/50"}`,
2897
- children: [
2898
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center mb-6", children: [
2899
- plan.popular && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block bg-primary text-primary-foreground px-4 py-1 rounded-full text-xs font-semibold mb-3", children: "Most Popular" }),
2900
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-xl sm:text-2xl font-bold mb-2", children: plan.name }),
2901
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground text-sm", children: plan.description })
2902
- ] }),
2903
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center mb-8", children: plan.price === "Custom" ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-3xl sm:text-4xl font-bold", children: "Custom" }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline justify-center gap-1", children: [
2904
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-4xl sm:text-5xl font-bold", children: plan.price }),
2905
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground text-lg", children: plan.interval })
2906
- ] }) }),
2907
- /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "space-y-3 mb-8 flex-1", children: plan.features.map((feature, index) => /* @__PURE__ */ jsxRuntime.jsxs(
2908
- "li",
2909
- {
2910
- className: "text-sm flex items-start gap-2",
2911
- children: [
2912
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-primary mt-0.5 flex-shrink-0", children: "\u2713" }),
2913
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: feature })
2914
- ]
2915
- },
2916
- index
2917
- )) }),
2918
- plan.ctaLink.startsWith("/") ? /* @__PURE__ */ jsxRuntime.jsx(
2919
- Button,
2920
- {
2921
- className: "w-full h-11",
2922
- variant: plan.popular ? "default" : "outline",
2923
- size: "lg",
2924
- asChild: true,
2925
- children: /* @__PURE__ */ jsxRuntime.jsx(
2926
- "a",
2927
- {
2928
- href: plan.ctaLink,
2929
- onClick: () => window.scrollTo({ top: 0, behavior: "smooth" }),
2930
- children: plan.ctaText
2931
- }
2932
- )
2933
- }
2934
- ) : /* @__PURE__ */ jsxRuntime.jsx(
2935
- Button,
2936
- {
2937
- className: "w-full h-11",
2938
- variant: plan.popular ? "default" : "outline",
2939
- size: "lg",
2940
- asChild: true,
2941
- children: /* @__PURE__ */ jsxRuntime.jsx(
2942
- "a",
2943
- {
2944
- href: plan.ctaLink,
2945
- target: "_blank",
2946
- rel: "noopener noreferrer",
2947
- children: plan.ctaText
2948
- }
2949
- )
2950
- }
2951
- )
2952
- ]
2953
- },
2954
- plan.id
2955
- ))
2956
- ) })
1724
+ }
1725
+ )
1726
+ ]
1727
+ },
1728
+ plan.id
1729
+ )) })
2957
1730
  ] }) });
2958
1731
  }
2959
1732
  function PricingPage(props) {
@@ -3251,11 +2024,7 @@ function WaitlistPage({
3251
2024
  }
3252
2025
  setIsSubmitting(true);
3253
2026
  try {
3254
- const result = await joinWaitlist(
3255
- client,
3256
- data,
3257
- product?.id || config.productId
3258
- );
2027
+ const result = await joinWaitlist(client, data, config.productId);
3259
2028
  if (result.success) {
3260
2029
  sonner.toast.success(result.message ?? successMessage);
3261
2030
  reset();
@@ -3404,7 +2173,7 @@ function WaitlistForm({
3404
2173
  const result = await joinWaitlist(
3405
2174
  client,
3406
2175
  { email, name: name || void 0 },
3407
- product?.id || config.productId
2176
+ config.productId
3408
2177
  );
3409
2178
  if (result.success) {
3410
2179
  sonner.toast.success(result.message ?? "You have been added to the waitlist!");
@@ -3758,2511 +2527,196 @@ function UnsubscribePage({
3758
2527
  )
3759
2528
  }
3760
2529
  ) }),
3761
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl font-bold text-foreground mb-3", children: "Invalid Unsubscribe Link" }),
3762
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: "This unsubscribe link is invalid or missing. If you want to unsubscribe, please use the link from your newsletter email." })
3763
- ] })
3764
- ] })
3765
- ]
3766
- }
3767
- );
3768
- }
3769
- function DiscordIcon({ className }) {
3770
- return /* @__PURE__ */ jsxRuntime.jsx(
3771
- "svg",
3772
- {
3773
- role: "img",
3774
- viewBox: "0 0 24 24",
3775
- fill: "currentColor",
3776
- xmlns: "http://www.w3.org/2000/svg",
3777
- className,
3778
- "aria-hidden": "true",
3779
- children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M20.317 4.369A19.79 19.79 0 0 0 16.558 3a.075.075 0 0 0-.079.037c-.34.6-.717 1.382-.98 1.99a18.27 18.27 0 0 0-5.998 0 12.51 12.51 0 0 0-.99-1.99.077.077 0 0 0-.079-.037 19.74 19.74 0 0 0-3.76 1.369.07.07 0 0 0-.032.028C.533 8.045-.32 11.6.099 15.114a.082.082 0 0 0 .031.056 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.027 14.09 14.09 0 0 0 1.226-1.994.076.076 0 0 0-.041-.105 13.1 13.1 0 0 1-1.872-.892.077.077 0 0 1-.008-.128c.126-.094.252-.192.372-.291a.074.074 0 0 1 .078-.01c3.927 1.793 8.18 1.793 12.061 0a.074.074 0 0 1 .079.009c.12.099.246.198.372.292a.077.077 0 0 1-.006.127 12.3 12.3 0 0 1-1.873.892.077.077 0 0 0-.041.106c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.84 19.84 0 0 0 6.002-3.03.077.077 0 0 0 .032-.055c.5-4.063-.838-7.587-3.549-10.717a.061.061 0 0 0-.031-.028zM8.02 13.001c-1.182 0-2.157-1.085-2.157-2.419 0-1.333.956-2.418 2.157-2.418 1.21 0 2.176 1.094 2.157 2.418 0 1.334-.956 2.419-2.157 2.419zm7.974 0c-1.182 0-2.157-1.085-2.157-2.419 0-1.333.956-2.418 2.157-2.418 1.21 0 2.176 1.094 2.157 2.418 0 1.334-.946 2.419-2.157 2.419z" })
3780
- }
3781
- );
3782
- }
3783
- function PinterestIcon({ className }) {
3784
- return /* @__PURE__ */ jsxRuntime.jsx(
3785
- "svg",
3786
- {
3787
- role: "img",
3788
- viewBox: "0 0 24 24",
3789
- fill: "currentColor",
3790
- xmlns: "http://www.w3.org/2000/svg",
3791
- className,
3792
- "aria-hidden": "true",
3793
- children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12.017 0C5.396 0 .029 5.367.029 11.987c0 5.079 3.158 9.417 7.618 11.162-.105-.949-.199-2.403.041-3.439.219-.937 1.406-5.957 1.406-5.957s-.359-.72-.359-1.781c0-1.663.967-2.911 2.168-2.911 1.024 0 1.518.769 1.518 1.688 0 1.029-.653 2.567-.992 3.992-.285 1.193.6 2.165 1.775 2.165 2.128 0 3.768-2.245 3.768-5.487 0-2.861-2.063-4.869-5.008-4.869-3.41 0-5.409 2.562-5.409 5.199 0 1.033.394 2.143.889 2.741.099.12.112.225.085.345-.09.375-.293 1.199-.334 1.363-.053.225-.172.271-.401.165-1.495-.69-2.433-2.878-2.433-4.646 0-3.776 2.748-7.252 7.92-7.252 4.158 0 7.392 2.967 7.392 6.923 0 4.135-2.607 7.462-6.233 7.462-1.214 0-2.357-.629-2.75-1.378l-.748 2.853c-.271 1.043-1.002 2.35-1.492 3.146C9.57 23.812 10.763 24 12.017 24c6.624 0 11.99-5.367 11.99-11.987C24.007 5.367 18.641 0 12.017 0z" })
3794
- }
3795
- );
3796
- }
3797
- var PLATFORMS = {
3798
- twitter: { Icon: lucideReact.Twitter, label: "Twitter / X", aliases: ["x"] },
3799
- linkedin: { Icon: lucideReact.Linkedin, label: "LinkedIn" },
3800
- github: { Icon: lucideReact.Github, label: "GitHub" },
3801
- facebook: { Icon: lucideReact.Facebook, label: "Facebook" },
3802
- youtube: { Icon: lucideReact.Youtube, label: "YouTube" },
3803
- instagram: { Icon: lucideReact.Instagram, label: "Instagram" },
3804
- discord: { Icon: DiscordIcon, label: "Discord" },
3805
- pinterest: { Icon: PinterestIcon, label: "Pinterest" }
3806
- };
3807
- var KEY_TO_PLATFORM = (() => {
3808
- const map = {};
3809
- for (const [key, def] of Object.entries(PLATFORMS)) {
3810
- map[key] = key;
3811
- for (const alias of def.aliases ?? []) {
3812
- map[alias] = key;
3813
- }
3814
- }
3815
- return map;
3816
- })();
3817
- var DEFAULT_ITEM_CLASS = "text-muted-foreground hover:text-foreground transition-colors";
3818
- function isSafeUrl(value) {
3819
- const trimmed = value.trim();
3820
- if (/^[/?#]/.test(trimmed)) return true;
3821
- return /^(https?:|mailto:|tel:)/i.test(trimmed);
3822
- }
3823
- function SocialLinks({
3824
- links: linksProp,
3825
- email: emailProp,
3826
- platforms: platformAllow,
3827
- showEmail,
3828
- iconClassName = "h-5 w-5",
3829
- itemClassName = DEFAULT_ITEM_CLASS,
3830
- className = "flex space-x-4"
3831
- }) {
3832
- const { product } = useProduct();
3833
- const links = linksProp ?? product?.socialLinks ?? {};
3834
- const email = emailProp !== void 0 ? emailProp : product?.contactEmail ?? null;
3835
- const resolved = /* @__PURE__ */ new Map();
3836
- for (const [rawKey, url] of Object.entries(links)) {
3837
- if (!url || typeof url !== "string") continue;
3838
- if (!isSafeUrl(url)) continue;
3839
- const platform = KEY_TO_PLATFORM[rawKey.toLowerCase()];
3840
- if (!platform) continue;
3841
- resolved.set(platform, url);
3842
- }
3843
- const orderedPlatforms = platformAllow ? platformAllow.filter((p) => resolved.has(p)) : Array.from(resolved.keys());
3844
- const wantEmail = (showEmail ?? !!email) && !!email;
3845
- if (orderedPlatforms.length === 0 && !wantEmail) return null;
3846
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, children: [
3847
- orderedPlatforms.map((platform) => {
3848
- const { Icon, label } = PLATFORMS[platform];
3849
- const url = resolved.get(platform);
3850
- return /* @__PURE__ */ jsxRuntime.jsx(
3851
- "a",
3852
- {
3853
- href: url,
3854
- target: "_blank",
3855
- rel: "noopener noreferrer",
3856
- "aria-label": label,
3857
- className: itemClassName,
3858
- children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: iconClassName })
3859
- },
3860
- platform
3861
- );
3862
- }),
3863
- wantEmail && /* @__PURE__ */ jsxRuntime.jsx(
3864
- "a",
3865
- {
3866
- href: `mailto:${email}`,
3867
- "aria-label": "Email",
3868
- className: itemClassName,
3869
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Mail, { className: iconClassName })
3870
- },
3871
- "email"
3872
- )
3873
- ] });
3874
- }
3875
- var DEFAULT_LABELS = {
3876
- docs: "Docs",
3877
- community: "Community",
3878
- careers: "Careers",
3879
- team: "Team",
3880
- companies: "Sister Companies"
3881
- };
3882
- var BURDENOFF_GROUP_DEFAULTS = {
3883
- docs: null,
3884
- // per-product → read from `Product.docsUrl`
3885
- community: null,
3886
- // per-product → caller supplies via prop until CMS field exists
3887
- careers: "https://burdenoff.com/careers",
3888
- team: "https://burdenoff.com/team",
3889
- companies: "https://burdenoff.com/companies"
3890
- };
3891
- var DEFAULT_ORDER = [
3892
- "docs",
3893
- "community",
3894
- "careers",
3895
- "team",
3896
- "companies"
3897
- ];
3898
- var DEFAULT_ITEM_CLASS2 = "text-sm text-muted-foreground hover:text-foreground transition-colors";
3899
- function isSafeUrl2(value) {
3900
- const trimmed = value.trim();
3901
- if (/^[/?#]/.test(trimmed)) return true;
3902
- return /^(https?:|mailto:|tel:)/i.test(trimmed);
3903
- }
3904
- function ResourceLinks({
3905
- links: linksProp,
3906
- resources = DEFAULT_ORDER,
3907
- labels = {},
3908
- className = "space-y-2",
3909
- itemClassName = DEFAULT_ITEM_CLASS2
3910
- }) {
3911
- const { product } = useProduct();
3912
- function resolve(resource) {
3913
- if (linksProp && resource in linksProp) {
3914
- const override = linksProp[resource];
3915
- if (override === void 0) ; else {
3916
- return override;
3917
- }
3918
- }
3919
- const fromCms = product?.resourceLinks?.[resource];
3920
- if (typeof fromCms === "string" && fromCms.length > 0) {
3921
- return fromCms;
3922
- }
3923
- if (resource === "docs" && product?.docsUrl) {
3924
- return product.docsUrl;
3925
- }
3926
- return BURDENOFF_GROUP_DEFAULTS[resource];
3927
- }
3928
- const items = resources.map((r) => ({ resource: r, url: resolve(r) })).filter(
3929
- (x) => !!x.url && isSafeUrl2(x.url)
3930
- );
3931
- if (items.length === 0) return null;
3932
- return /* @__PURE__ */ jsxRuntime.jsx("ul", { className, children: items.map(({ resource, url }) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(
3933
- "a",
3934
- {
3935
- href: url,
3936
- target: "_blank",
3937
- rel: "noopener noreferrer",
3938
- className: itemClassName,
3939
- children: labels[resource] ?? DEFAULT_LABELS[resource]
3940
- }
3941
- ) }, resource)) });
3942
- }
3943
- function normalizeSearch(value) {
3944
- return value.toLowerCase().trim();
3945
- }
3946
- function filterProducts(products, query) {
3947
- const normalized = normalizeSearch(query);
3948
- if (!normalized) return products;
3949
- return products.filter((p) => normalizeSearch(p.name).includes(normalized));
3950
- }
3951
- function WebsiteSwitcher({
3952
- products,
3953
- triggerLabel = "Explore products",
3954
- searchPlaceholder = "Find a product\u2026",
3955
- emptyText = "No products match your search.",
3956
- className
3957
- }) {
3958
- const [open, setOpen] = React.useState(false);
3959
- const [query, setQuery] = React.useState("");
3960
- const filtered = React.useMemo(
3961
- () => filterProducts(products, query),
3962
- [products, query]
3963
- );
3964
- return /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenu__namespace.Root, { open, onOpenChange: setOpen, children: [
3965
- /* @__PURE__ */ jsxRuntime.jsxs(
3966
- DropdownMenu__namespace.Trigger,
3967
- {
3968
- className: cn(
3969
- "inline-flex items-center gap-1.5 text-sm font-medium transition-colors",
3970
- "text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring rounded-md",
3971
- className
3972
- ),
3973
- "aria-label": triggerLabel,
3974
- children: [
3975
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Globe, { className: "h-4 w-4", "aria-hidden": "true" }),
3976
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden sm:inline", children: triggerLabel }),
3977
- /* @__PURE__ */ jsxRuntime.jsx(
3978
- lucideReact.ChevronDown,
3979
- {
3980
- className: cn(
3981
- "h-3.5 w-3.5 transition-transform duration-200",
3982
- open && "rotate-180"
3983
- ),
3984
- "aria-hidden": "true"
3985
- }
3986
- )
3987
- ]
3988
- }
3989
- ),
3990
- /* @__PURE__ */ jsxRuntime.jsx(DropdownMenu__namespace.Portal, { children: /* @__PURE__ */ jsxRuntime.jsxs(
3991
- DropdownMenu__namespace.Content,
3992
- {
3993
- className: "z-50 min-w-[16rem] max-w-[20rem] rounded-lg border border-border/60 bg-popover p-2 shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2",
3994
- sideOffset: 8,
3995
- align: "end",
3996
- avoidCollisions: true,
3997
- children: [
3998
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-2 pb-2 pt-1", children: [
3999
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs font-medium text-muted-foreground mb-2", children: "Burdenoff ecosystem" }),
4000
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
4001
- /* @__PURE__ */ jsxRuntime.jsx(
4002
- lucideReact.Search,
4003
- {
4004
- className: "absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-muted-foreground",
4005
- "aria-hidden": "true"
4006
- }
4007
- ),
4008
- /* @__PURE__ */ jsxRuntime.jsx(
4009
- Input,
4010
- {
4011
- type: "search",
4012
- value: query,
4013
- onChange: (e) => setQuery(e.target.value),
4014
- placeholder: searchPlaceholder,
4015
- className: "h-8 pl-8 text-sm",
4016
- "aria-label": searchPlaceholder,
4017
- onKeyDown: (e) => {
4018
- if (e.key === "Escape") {
4019
- setQuery("");
4020
- }
4021
- }
4022
- }
4023
- )
4024
- ] })
4025
- ] }),
4026
- /* @__PURE__ */ jsxRuntime.jsx(DropdownMenu__namespace.Separator, { className: "my-1 h-px bg-border/60" }),
4027
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-h-[min(60vh,24rem)] overflow-y-auto py-1", children: filtered.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-4 text-center text-sm text-muted-foreground", children: emptyText }) : filtered.map((product) => /* @__PURE__ */ jsxRuntime.jsx(DropdownMenu__namespace.Item, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
4028
- "a",
4029
- {
4030
- href: product.websiteUrl,
4031
- target: "_blank",
4032
- rel: "noopener noreferrer",
4033
- className: cn(
4034
- "flex items-center gap-3 rounded-md px-2 py-2 text-sm outline-none",
4035
- "text-foreground hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground"
4036
- ),
4037
- children: [
4038
- product.logoUrl ? /* @__PURE__ */ jsxRuntime.jsx(
4039
- "img",
4040
- {
4041
- src: product.logoUrl,
4042
- alt: "",
4043
- "aria-hidden": "true",
4044
- className: "h-5 w-5 shrink-0 rounded object-contain"
4045
- }
4046
- ) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-5 w-5 shrink-0 items-center justify-center rounded bg-primary/10 text-[10px] font-bold text-primary", children: product.name.charAt(0) }),
4047
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: product.name })
4048
- ]
4049
- }
4050
- ) }, product.slug)) }),
4051
- /* @__PURE__ */ jsxRuntime.jsx(DropdownMenu__namespace.Arrow, { className: "fill-popover" })
4052
- ]
4053
- }
4054
- ) })
4055
- ] });
4056
- }
4057
- function splitRemaining(ms) {
4058
- const clamped = Math.max(0, ms);
4059
- const totalSeconds = Math.floor(clamped / 1e3);
4060
- return {
4061
- days: Math.floor(totalSeconds / 86400),
4062
- hours: Math.floor(totalSeconds % 86400 / 3600),
4063
- minutes: Math.floor(totalSeconds % 3600 / 60),
4064
- seconds: totalSeconds % 60
4065
- };
4066
- }
4067
- function padTwo(n) {
4068
- return String(n).padStart(2, "0");
4069
- }
4070
- function LaunchCountdown({
4071
- launchDate,
4072
- launchLabel,
4073
- launchedMessage = "We have launched!",
4074
- className
4075
- }) {
4076
- const [now, setNow] = React.useState(() => Date.now());
4077
- const remainingMs = launchDate - now;
4078
- const hasLaunched = remainingMs <= 0;
4079
- React.useEffect(() => {
4080
- if (hasLaunched) return;
4081
- const interval = setInterval(() => {
4082
- setNow(Date.now());
4083
- }, 1e3);
4084
- return () => clearInterval(interval);
4085
- }, [hasLaunched]);
4086
- const { days, hours, minutes, seconds } = splitRemaining(remainingMs);
4087
- const cells = [
4088
- { label: "Days", value: padTwo(Math.min(days, 99)) },
4089
- { label: "Hours", value: padTwo(hours) },
4090
- { label: "Minutes", value: padTwo(minutes) },
4091
- { label: "Seconds", value: padTwo(seconds) }
4092
- ];
4093
- return /* @__PURE__ */ jsxRuntime.jsxs(
4094
- "div",
4095
- {
4096
- className: cn("inline-flex flex-col gap-3", className),
4097
- role: "timer",
4098
- "aria-label": launchLabel ?? (hasLaunched ? launchedMessage : `Countdown to launch`),
4099
- children: [
4100
- !hasLaunched && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-mono uppercase tracking-wider text-muted-foreground", children: "Launching in" }),
4101
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex gap-2 sm:gap-3", children: hasLaunched ? /* @__PURE__ */ jsxRuntime.jsxs(
4102
- "div",
4103
- {
4104
- className: cn(
4105
- "flex min-w-[15rem] items-center justify-center gap-3 rounded-lg border border-primary/30 bg-primary/10 px-6 py-4",
4106
- "text-center"
4107
- ),
4108
- children: [
4109
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Rocket, { className: "h-5 w-5 text-primary", "aria-hidden": "true" }),
4110
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-lg font-semibold text-foreground", children: launchedMessage })
4111
- ]
4112
- }
4113
- ) : cells.map((cell) => /* @__PURE__ */ jsxRuntime.jsxs(
4114
- "div",
4115
- {
4116
- className: cn(
4117
- "flex min-w-[3.75rem] flex-col items-center rounded-lg border border-border/60 bg-card px-3 py-2 sm:min-w-[4.5rem] sm:px-4 sm:py-3"
4118
- ),
4119
- children: [
4120
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-2xl font-bold tabular-nums text-foreground sm:text-3xl", children: cell.value }),
4121
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mt-1 text-[10px] font-medium uppercase tracking-wide text-muted-foreground sm:text-xs", children: cell.label })
4122
- ]
4123
- },
4124
- cell.label
4125
- )) }),
4126
- !hasLaunched && launchLabel && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-muted-foreground", children: launchLabel })
4127
- ]
4128
- }
4129
- );
4130
- }
4131
- function ProblemsSolvedSection({
4132
- eyebrow,
4133
- title,
4134
- subtitle,
4135
- problems,
4136
- storyBasePath = "/use-cases",
4137
- storyLabel = "See the story",
4138
- className,
4139
- children
4140
- }) {
4141
- return /* @__PURE__ */ jsxRuntime.jsx(
4142
- "section",
4143
- {
4144
- className: cn(
4145
- "py-16 md:py-24 px-4 sm:px-6 lg:px-8 bg-muted/30",
4146
- className
4147
- ),
4148
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-7xl mx-auto", children: [
4149
- (eyebrow || title || subtitle) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-3xl mx-auto text-center mb-12 md:mb-16", children: [
4150
- eyebrow && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs font-mono uppercase tracking-widest text-primary mb-4", children: eyebrow }),
4151
- title && /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-3xl sm:text-4xl font-bold text-foreground mb-4", children: title }),
4152
- subtitle && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-lg text-muted-foreground", children: subtitle })
4153
- ] }),
4154
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6", children: problems.map((item, index) => {
4155
- const Icon = item.icon;
4156
- const storyHref = item.storySlug ? `${storyBasePath.replace(/\/$/, "")}/${item.storySlug}` : null;
4157
- return /* @__PURE__ */ jsxRuntime.jsxs(
4158
- "div",
4159
- {
4160
- className: cn(
4161
- "group relative flex flex-col rounded-2xl border border-border/40 bg-card p-6 transition-colors",
4162
- "hover:border-primary/30"
4163
- ),
4164
- children: [
4165
- /* @__PURE__ */ jsxRuntime.jsx(
4166
- "div",
4167
- {
4168
- className: cn(
4169
- "mb-5 inline-flex h-12 w-12 items-center justify-center rounded-xl",
4170
- "bg-primary/10 text-primary"
4171
- ),
4172
- children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "h-6 w-6", "aria-hidden": true })
4173
- }
4174
- ),
4175
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-foreground mb-2", children: item.problem }),
4176
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground leading-relaxed mb-4 flex-1", children: item.solution }),
4177
- storyHref && /* @__PURE__ */ jsxRuntime.jsx(
4178
- Button,
4179
- {
4180
- variant: "ghost",
4181
- className: "justify-start px-0 h-auto py-0 text-sm group/link",
4182
- asChild: true,
4183
- children: /* @__PURE__ */ jsxRuntime.jsxs(
4184
- "a",
4185
- {
4186
- href: storyHref,
4187
- className: "inline-flex items-center gap-1 text-primary hover:underline",
4188
- children: [
4189
- storyLabel,
4190
- /* @__PURE__ */ jsxRuntime.jsx(
4191
- lucideReact.ArrowRight,
4192
- {
4193
- className: "h-3.5 w-3.5 transition-transform group-hover/link:translate-x-0.5",
4194
- "aria-hidden": "true"
4195
- }
4196
- )
4197
- ]
4198
- }
4199
- )
4200
- }
4201
- )
4202
- ]
4203
- },
4204
- `${item.problem}-${index}`
4205
- );
4206
- }) }),
4207
- children && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-12", children })
4208
- ] })
4209
- }
4210
- );
4211
- }
4212
- function detectElectronPlatform(userAgent = typeof navigator !== "undefined" ? navigator.userAgent : "") {
4213
- const ua = userAgent.toLowerCase();
4214
- if (ua.includes("win")) return "win";
4215
- if (ua.includes("mac")) return "mac";
4216
- if (ua.includes("linux")) return "linux";
4217
- return "unknown";
4218
- }
4219
- function buildDownloadUrl(base, artifactName, platform, platformMap) {
4220
- const normalizedBase = base.replace(/\/$/, "");
4221
- const platformKey = platformMap[platform] ?? platform;
4222
- const fileName = artifactName.includes("{platform}") ? artifactName.replace(/\{platform\}/g, platformKey) : artifactName;
4223
- return `${normalizedBase}/${fileName}`;
4224
- }
4225
- function ElectronDownloadLink({
4226
- downloadBaseUrl,
4227
- artifactName,
4228
- hidden = true,
4229
- label = "Download desktop app",
4230
- downloadLabel = label,
4231
- platformMap = {},
4232
- className,
4233
- variant = "default",
4234
- size = "default"
4235
- }) {
4236
- if (hidden) return null;
4237
- const platform = detectElectronPlatform();
4238
- const href = buildDownloadUrl(
4239
- downloadBaseUrl,
4240
- artifactName,
4241
- platform,
4242
- platformMap
4243
- );
4244
- return /* @__PURE__ */ jsxRuntime.jsx(
4245
- Button,
4246
- {
4247
- variant,
4248
- size,
4249
- className: cn("inline-flex items-center gap-2", className),
4250
- asChild: true,
4251
- children: /* @__PURE__ */ jsxRuntime.jsxs(
4252
- "a",
4253
- {
4254
- href,
4255
- download: true,
4256
- "aria-label": `${downloadLabel}${platform !== "unknown" ? ` for ${platform}` : ""}`,
4257
- children: [
4258
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Download, { className: "h-4 w-4", "aria-hidden": "true" }),
4259
- label
4260
- ]
4261
- }
4262
- )
4263
- }
4264
- );
4265
- }
4266
- function RybbitAnalytics({
4267
- siteId,
4268
- scriptUrl = "https://analytics.burdenoff.com/api/script.js",
4269
- enabled = true
4270
- }) {
4271
- React.useEffect(() => {
4272
- if (!enabled || !siteId) return;
4273
- const existingScript = document.querySelector(
4274
- `script[data-site-id="${siteId}"]`
4275
- );
4276
- if (existingScript) return;
4277
- const script = document.createElement("script");
4278
- script.src = scriptUrl;
4279
- script.defer = true;
4280
- script.crossOrigin = "anonymous";
4281
- script.setAttribute("data-site-id", siteId);
4282
- document.head.appendChild(script);
4283
- return () => {
4284
- const scriptToRemove = document.querySelector(
4285
- `script[data-site-id="${siteId}"]`
4286
- );
4287
- if (scriptToRemove) {
4288
- document.head.removeChild(scriptToRemove);
4289
- }
4290
- };
4291
- }, [siteId, scriptUrl, enabled]);
4292
- return null;
4293
- }
4294
- var BLOG_POSTS_QUERY = `
4295
- query BlogPosts($productId: String!, $limit: Int, $offset: Int) {
4296
- publicContentPages(productId: $productId, limit: $limit, offset: $offset) {
4297
- items {
4298
- id
4299
- title
4300
- slug
4301
- lastUpdated
4302
- tags
4303
- seoDescription
4304
- excerpt
4305
- thumbnail
4306
- author
4307
- publishedAt
4308
- readingTime
4309
- featured
4310
- category
4311
- }
4312
- total
4313
- hasMore
4314
- }
4315
- }
4316
- `;
4317
- var BLOG_POST_QUERY = `
4318
- query BlogPost($slug: String!, $productId: String) {
4319
- publicContentPage(slug: $slug, productId: $productId) {
4320
- id
4321
- title
4322
- slug
4323
- content
4324
- contentFormat
4325
- seoTitle
4326
- seoDescription
4327
- seoKeywords
4328
- lastUpdated
4329
- tags
4330
- excerpt
4331
- thumbnail
4332
- author
4333
- publishedAt
4334
- readingTime
4335
- featured
4336
- category
4337
- }
4338
- }
4339
- `;
4340
- function BlogCard({
4341
- post,
4342
- basePath = "/blog"
4343
- }) {
4344
- const date = post.publishedAt || post.lastUpdated;
4345
- const formattedDate = date ? new Date(date).toLocaleDateString("en-US", {
4346
- year: "numeric",
4347
- month: "long",
4348
- day: "numeric"
4349
- }) : "";
4350
- const [imgError, setImgError] = React.useState(false);
4351
- return /* @__PURE__ */ jsxRuntime.jsxs(
4352
- "a",
4353
- {
4354
- href: `${basePath}/${post.slug}`,
4355
- className: "group block bg-card border rounded-xl overflow-hidden hover:shadow-lg hover:border-primary/30 transition-all duration-300",
4356
- children: [
4357
- post.thumbnail && !imgError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
4358
- "img",
4359
- {
4360
- src: post.thumbnail,
4361
- alt: post.title,
4362
- className: "w-full h-full object-cover group-hover:scale-105 transition-transform duration-300",
4363
- loading: "lazy",
4364
- onError: () => setImgError(true)
4365
- }
4366
- ) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] bg-muted/50 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(
4367
- "svg",
4368
- {
4369
- className: "w-12 h-12 text-muted-foreground/20",
4370
- fill: "none",
4371
- viewBox: "0 0 24 24",
4372
- stroke: "currentColor",
4373
- children: /* @__PURE__ */ jsxRuntime.jsx(
4374
- "path",
4375
- {
4376
- strokeLinecap: "round",
4377
- strokeLinejoin: "round",
4378
- strokeWidth: 1.5,
4379
- d: "M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"
4380
- }
4381
- )
4382
- }
4383
- ) }),
4384
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-5 sm:p-6", children: [
4385
- post.category && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block text-xs font-medium text-primary bg-primary/10 px-2.5 py-1 rounded-full mb-3", children: post.category }),
4386
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-foreground mb-2 group-hover:text-primary transition-colors line-clamp-2", children: post.title }),
4387
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground mb-4 line-clamp-3", children: post.excerpt || post.seoDescription || "" }),
4388
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 text-xs text-muted-foreground", children: [
4389
- post.author && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: post.author }),
4390
- post.author && formattedDate && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
4391
- formattedDate && /* @__PURE__ */ jsxRuntime.jsx("span", { children: formattedDate }),
4392
- post.readingTime && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4393
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
4394
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
4395
- post.readingTime,
4396
- " min read"
4397
- ] })
4398
- ] })
4399
- ] })
4400
- ] })
4401
- ]
4402
- }
4403
- );
4404
- }
4405
- function BlogListPage(props) {
4406
- const {
4407
- productName,
4408
- heroTitle = "Blog",
4409
- heroSubtitle = "Latest updates, engineering insights, and product news",
4410
- blogPostBasePath = "/blog",
4411
- className = "",
4412
- seoTitle,
4413
- seoDescription,
4414
- seoKeywords
4415
- } = props;
4416
- const client = useWebSDK();
4417
- const config = useWebSDKConfig();
4418
- const { product } = useProduct();
4419
- const resolvedProductId = product?.id || config.productId;
4420
- const [posts, setPosts] = React.useState([]);
4421
- const [total, setTotal] = React.useState(0);
4422
- const [loading, setLoading] = React.useState(true);
4423
- const [error, setError] = React.useState(null);
4424
- const [offset, setOffset] = React.useState(0);
4425
- const limit = 12;
4426
- const fetchPosts = React.useCallback(async () => {
4427
- if (!resolvedProductId) return;
4428
- setLoading(true);
4429
- try {
4430
- const result = await client.query(BLOG_POSTS_QUERY, {
4431
- productId: resolvedProductId,
4432
- limit,
4433
- offset
4434
- });
4435
- if (result.errors?.length) {
4436
- setError(result.errors[0]?.message ?? "Failed to load posts");
4437
- return;
4438
- }
4439
- const blogPosts = (result.data?.publicContentPages?.items || []).filter(
4440
- (p) => p.tags?.includes("blog")
4441
- );
4442
- setPosts(blogPosts);
4443
- setTotal(result.data?.publicContentPages?.total || 0);
4444
- setError(null);
4445
- } catch {
4446
- setError("Failed to load blog posts");
4447
- } finally {
4448
- setLoading(false);
4449
- }
4450
- }, [client, resolvedProductId, offset]);
4451
- React.useEffect(() => {
4452
- fetchPosts();
4453
- }, [fetchPosts]);
4454
- const featuredPosts = posts.filter((p) => p.featured);
4455
- const regularPosts = posts.filter((p) => !p.featured);
4456
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `w-full ${className}`, children: [
4457
- seoTitle && /* @__PURE__ */ jsxRuntime.jsx(
4458
- PageHead,
4459
- {
4460
- title: seoTitle,
4461
- description: seoDescription || heroSubtitle,
4462
- productName,
4463
- keywords: seoKeywords
4464
- }
4465
- ),
4466
- /* @__PURE__ */ jsxRuntime.jsx("section", { className: "py-16 sm:py-20 md:py-24", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center max-w-3xl mx-auto", children: [
4467
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-3xl sm:text-4xl md:text-5xl font-bold tracking-tight text-foreground", children: heroTitle }),
4468
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-4 text-lg text-muted-foreground", children: heroSubtitle })
4469
- ] }) }) }),
4470
- /* @__PURE__ */ jsxRuntime.jsx("section", { className: "pb-16 sm:pb-20", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid md:grid-cols-2 lg:grid-cols-3 gap-6 sm:gap-8", children: [1, 2, 3].map((i) => /* @__PURE__ */ jsxRuntime.jsxs(
4471
- "div",
4472
- {
4473
- className: "bg-card border rounded-xl overflow-hidden animate-pulse",
4474
- children: [
4475
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] bg-muted" }),
4476
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6 space-y-3", children: [
4477
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-1/4" }),
4478
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-6 bg-muted rounded w-3/4" }),
4479
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded" }),
4480
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-1/2" })
4481
- ] })
4482
- ]
4483
- },
4484
- i
4485
- )) }) : error ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center py-12", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: error }) }) : posts.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center py-12", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: "No blog posts yet. Check back soon!" }) }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4486
- featuredPosts.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid md:grid-cols-2 gap-6 sm:gap-8 mb-8", children: featuredPosts.map((post) => /* @__PURE__ */ jsxRuntime.jsx(
4487
- BlogCard,
4488
- {
4489
- post,
4490
- basePath: blogPostBasePath
4491
- },
4492
- post.id
4493
- )) }),
4494
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid md:grid-cols-2 lg:grid-cols-3 gap-6 sm:gap-8", children: regularPosts.map((post) => /* @__PURE__ */ jsxRuntime.jsx(
4495
- BlogCard,
4496
- {
4497
- post,
4498
- basePath: blogPostBasePath
4499
- },
4500
- post.id
4501
- )) }),
4502
- total > limit && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-center gap-4 mt-12", children: [
4503
- /* @__PURE__ */ jsxRuntime.jsx(
4504
- "button",
4505
- {
4506
- onClick: () => setOffset(Math.max(0, offset - limit)),
4507
- disabled: offset === 0,
4508
- className: "px-5 py-2.5 text-sm font-medium border rounded-lg disabled:opacity-40 hover:bg-muted transition-colors",
4509
- children: "Previous"
4510
- }
4511
- ),
4512
- /* @__PURE__ */ jsxRuntime.jsx(
4513
- "button",
4514
- {
4515
- onClick: () => setOffset(offset + limit),
4516
- disabled: offset + limit >= total,
4517
- className: "px-5 py-2.5 text-sm font-medium border rounded-lg disabled:opacity-40 hover:bg-muted transition-colors",
4518
- children: "Next"
4519
- }
4520
- )
4521
- ] })
4522
- ] }) }) })
4523
- ] });
4524
- }
4525
- function BlogPostPage(props) {
4526
- const {
4527
- productName,
4528
- blogListUrl = "/blog",
4529
- backLabel = "Back to Blog",
4530
- className = ""
4531
- } = props;
4532
- const client = useWebSDK();
4533
- const config = useWebSDKConfig();
4534
- const { product } = useProduct();
4535
- const resolvedProductId = product?.id || config.productId;
4536
- const [post, setPost] = React.useState(null);
4537
- const [loading, setLoading] = React.useState(true);
4538
- const [error, setError] = React.useState(null);
4539
- const slug = typeof window !== "undefined" ? window.location.pathname.split("/").pop() || "" : "";
4540
- React.useEffect(() => {
4541
- if (!slug || !resolvedProductId) return;
4542
- async function fetchPost() {
4543
- setLoading(true);
4544
- try {
4545
- const result = await client.query(BLOG_POST_QUERY, {
4546
- slug,
4547
- productId: resolvedProductId
4548
- });
4549
- if (result.errors?.length) {
4550
- setError(result.errors[0]?.message ?? "Failed to load post");
4551
- return;
4552
- }
4553
- setPost(result.data?.publicContentPage ?? null);
4554
- setError(null);
4555
- } catch {
4556
- setError("Failed to load blog post");
4557
- } finally {
4558
- setLoading(false);
4559
- }
4560
- }
4561
- fetchPost();
4562
- }, [client, slug, resolvedProductId]);
4563
- if (loading) {
4564
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-full ${className}`, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-24", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "animate-pulse space-y-6", children: [
4565
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-8 bg-muted rounded w-2/3" }),
4566
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-1/3" }),
4567
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] bg-muted rounded-xl" }),
4568
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-3", children: [1, 2, 3, 4, 5].map((i) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded" }, i)) })
4569
- ] }) }) });
4570
- }
4571
- if (error || !post) {
4572
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-full ${className}`, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-24 text-center", children: [
4573
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl font-bold mb-4", children: "Post not found" }),
4574
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground mb-6", children: error || "This blog post doesn't exist or has been removed." }),
4575
- /* @__PURE__ */ jsxRuntime.jsxs(
4576
- "a",
4577
- {
4578
- href: blogListUrl,
4579
- className: "text-primary hover:underline font-medium",
4580
- children: [
4581
- "\u2190 ",
4582
- backLabel
4583
- ]
4584
- }
4585
- )
4586
- ] }) });
4587
- }
4588
- const date = post.publishedAt || post.lastUpdated;
4589
- const formattedDate = date ? new Date(date).toLocaleDateString("en-US", {
4590
- year: "numeric",
4591
- month: "long",
4592
- day: "numeric"
4593
- }) : "";
4594
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `w-full ${className}`, children: [
4595
- /* @__PURE__ */ jsxRuntime.jsx(
4596
- PageHead,
4597
- {
4598
- title: post.seoTitle || post.title,
4599
- description: post.seoDescription || post.excerpt || "",
4600
- productName,
4601
- keywords: post.seoKeywords || ""
4602
- }
4603
- ),
4604
- /* @__PURE__ */ jsxRuntime.jsxs("article", { className: "max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-16 sm:py-20", children: [
4605
- /* @__PURE__ */ jsxRuntime.jsxs(
4606
- "a",
4607
- {
4608
- href: blogListUrl,
4609
- className: "inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground mb-8 transition-colors",
4610
- children: [
4611
- "\u2190 ",
4612
- backLabel
4613
- ]
4614
- }
4615
- ),
4616
- post.category && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block text-xs font-medium text-primary bg-primary/10 px-2.5 py-1 rounded-full mb-4", children: post.category }),
4617
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-3xl sm:text-4xl md:text-5xl font-bold tracking-tight text-foreground mb-4", children: post.title }),
4618
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 text-sm text-muted-foreground mb-8", children: [
4619
- post.author && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-foreground", children: post.author }),
4620
- post.author && formattedDate && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
4621
- formattedDate && /* @__PURE__ */ jsxRuntime.jsx("span", { children: formattedDate }),
4622
- post.readingTime && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4623
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
4624
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
4625
- post.readingTime,
4626
- " min read"
4627
- ] })
4628
- ] })
4629
- ] }),
4630
- post.thumbnail && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] rounded-xl overflow-hidden mb-10", children: /* @__PURE__ */ jsxRuntime.jsx(
4631
- "img",
4632
- {
4633
- src: post.thumbnail,
4634
- alt: post.title,
4635
- className: "w-full h-full object-cover"
4636
- }
4637
- ) }),
4638
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "prose prose-gray dark:prose-invert max-w-none", children: /* @__PURE__ */ jsxRuntime.jsx(ContentRenderer, { content: post.content, format: post.contentFormat }) })
4639
- ] })
4640
- ] });
4641
- }
4642
- var CASE_STUDY_TAG = "case-study";
4643
- var CASE_STUDIES_QUERY = `
4644
- query CaseStudies($productId: String!, $limit: Int, $offset: Int) {
4645
- publicContentPages(productId: $productId, limit: $limit, offset: $offset) {
4646
- items {
4647
- id
4648
- title
4649
- slug
4650
- lastUpdated
4651
- tags
4652
- seoDescription
4653
- excerpt
4654
- thumbnail
4655
- author
4656
- publishedAt
4657
- readingTime
4658
- featured
4659
- category
4660
- }
4661
- total
4662
- hasMore
4663
- }
4664
- }
4665
- `;
4666
- var CASE_STUDY_QUERY = `
4667
- query CaseStudy($slug: String!, $productId: String) {
4668
- publicContentPage(slug: $slug, productId: $productId) {
4669
- id
4670
- title
4671
- slug
4672
- content
4673
- contentFormat
4674
- seoTitle
4675
- seoDescription
4676
- seoKeywords
4677
- lastUpdated
4678
- tags
4679
- excerpt
4680
- thumbnail
4681
- author
4682
- publishedAt
4683
- readingTime
4684
- featured
4685
- category
4686
- }
4687
- }
4688
- `;
4689
- function CaseStudyCard({
4690
- post,
4691
- basePath = "/case-studies"
4692
- }) {
4693
- const date = post.publishedAt || post.lastUpdated;
4694
- const formattedDate = date ? new Date(date).toLocaleDateString("en-US", {
4695
- year: "numeric",
4696
- month: "long",
4697
- day: "numeric"
4698
- }) : "";
4699
- const [imgError, setImgError] = React.useState(false);
4700
- return /* @__PURE__ */ jsxRuntime.jsxs(
4701
- "a",
4702
- {
4703
- href: `${basePath}/${post.slug}`,
4704
- className: "group block bg-card border rounded-xl overflow-hidden hover:shadow-lg hover:border-primary/30 transition-all duration-300",
4705
- children: [
4706
- post.thumbnail && !imgError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
4707
- "img",
4708
- {
4709
- src: post.thumbnail,
4710
- alt: post.title,
4711
- className: "w-full h-full object-cover group-hover:scale-105 transition-transform duration-300",
4712
- loading: "lazy",
4713
- onError: () => setImgError(true)
4714
- }
4715
- ) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] bg-muted/50 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(
4716
- "svg",
4717
- {
4718
- className: "w-12 h-12 text-muted-foreground/20",
4719
- fill: "none",
4720
- viewBox: "0 0 24 24",
4721
- stroke: "currentColor",
4722
- children: /* @__PURE__ */ jsxRuntime.jsx(
4723
- "path",
4724
- {
4725
- strokeLinecap: "round",
4726
- strokeLinejoin: "round",
4727
- strokeWidth: 1.5,
4728
- d: "M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"
4729
- }
4730
- )
4731
- }
4732
- ) }),
4733
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-5 sm:p-6", children: [
4734
- post.category && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block text-xs font-medium text-primary bg-primary/10 px-2.5 py-1 rounded-full mb-3", children: post.category }),
4735
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-foreground mb-2 group-hover:text-primary transition-colors line-clamp-2", children: post.title }),
4736
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground mb-4 line-clamp-3", children: post.excerpt || post.seoDescription || "" }),
4737
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 text-xs text-muted-foreground", children: [
4738
- post.author && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: post.author }),
4739
- post.author && formattedDate && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
4740
- formattedDate && /* @__PURE__ */ jsxRuntime.jsx("span", { children: formattedDate }),
4741
- post.readingTime && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4742
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
4743
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
4744
- post.readingTime,
4745
- " min read"
4746
- ] })
4747
- ] })
4748
- ] })
4749
- ] })
4750
- ]
4751
- }
4752
- );
4753
- }
4754
- function CaseStudiesListPage(props) {
4755
- const {
4756
- productName,
4757
- heroTitle = "Case Studies",
4758
- heroSubtitle = "Real-world stories of teams building with our platform",
4759
- caseStudyBasePath = "/case-studies",
4760
- className = "",
4761
- seoTitle,
4762
- seoDescription,
4763
- seoKeywords
4764
- } = props;
4765
- const client = useWebSDK();
4766
- const config = useWebSDKConfig();
4767
- const { product } = useProduct();
4768
- const resolvedProductId = product?.id || config.productId;
4769
- const [posts, setPosts] = React.useState([]);
4770
- const [total, setTotal] = React.useState(0);
4771
- const [loading, setLoading] = React.useState(true);
4772
- const [error, setError] = React.useState(null);
4773
- const [offset, setOffset] = React.useState(0);
4774
- const limit = 12;
4775
- const fetchPosts = React.useCallback(async () => {
4776
- if (!resolvedProductId) return;
4777
- setLoading(true);
4778
- try {
4779
- const result = await client.query(CASE_STUDIES_QUERY, {
4780
- productId: resolvedProductId,
4781
- limit,
4782
- offset
4783
- });
4784
- if (result.errors?.length) {
4785
- setError(result.errors[0]?.message ?? "Failed to load case studies");
4786
- return;
4787
- }
4788
- const caseStudies = (result.data?.publicContentPages?.items || []).filter(
4789
- (p) => p.tags?.includes(CASE_STUDY_TAG)
4790
- );
4791
- setPosts(caseStudies);
4792
- setTotal(result.data?.publicContentPages?.total || 0);
4793
- setError(null);
4794
- } catch {
4795
- setError("Failed to load case studies");
4796
- } finally {
4797
- setLoading(false);
4798
- }
4799
- }, [client, resolvedProductId, offset]);
4800
- React.useEffect(() => {
4801
- fetchPosts();
4802
- }, [fetchPosts]);
4803
- const featuredPosts = posts.filter((p) => p.featured);
4804
- const regularPosts = posts.filter((p) => !p.featured);
4805
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `w-full ${className}`, children: [
4806
- seoTitle && /* @__PURE__ */ jsxRuntime.jsx(
4807
- PageHead,
4808
- {
4809
- title: seoTitle,
4810
- description: seoDescription || heroSubtitle,
4811
- productName,
4812
- keywords: seoKeywords
4813
- }
4814
- ),
4815
- /* @__PURE__ */ jsxRuntime.jsx("section", { className: "py-16 sm:py-20 md:py-24", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center max-w-3xl mx-auto", children: [
4816
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-3xl sm:text-4xl md:text-5xl font-bold tracking-tight text-foreground", children: heroTitle }),
4817
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-4 text-lg text-muted-foreground", children: heroSubtitle })
4818
- ] }) }) }),
4819
- /* @__PURE__ */ jsxRuntime.jsx("section", { className: "pb-16 sm:pb-20", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid md:grid-cols-2 lg:grid-cols-3 gap-6 sm:gap-8", children: [1, 2, 3].map((i) => /* @__PURE__ */ jsxRuntime.jsxs(
4820
- "div",
4821
- {
4822
- className: "bg-card border rounded-xl overflow-hidden animate-pulse",
4823
- children: [
4824
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] bg-muted" }),
4825
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6 space-y-3", children: [
4826
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-1/4" }),
4827
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-6 bg-muted rounded w-3/4" }),
4828
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded" }),
4829
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-1/2" })
4830
- ] })
4831
- ]
4832
- },
4833
- i
4834
- )) }) : error ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center py-12", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: error }) }) : posts.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center py-12", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: "No case studies yet. Check back soon!" }) }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4835
- featuredPosts.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid md:grid-cols-2 gap-6 sm:gap-8 mb-8", children: featuredPosts.map((post) => /* @__PURE__ */ jsxRuntime.jsx(
4836
- CaseStudyCard,
4837
- {
4838
- post,
4839
- basePath: caseStudyBasePath
4840
- },
4841
- post.id
4842
- )) }),
4843
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid md:grid-cols-2 lg:grid-cols-3 gap-6 sm:gap-8", children: regularPosts.map((post) => /* @__PURE__ */ jsxRuntime.jsx(
4844
- CaseStudyCard,
4845
- {
4846
- post,
4847
- basePath: caseStudyBasePath
4848
- },
4849
- post.id
4850
- )) }),
4851
- total > limit && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-center gap-4 mt-12", children: [
4852
- /* @__PURE__ */ jsxRuntime.jsx(
4853
- "button",
4854
- {
4855
- onClick: () => setOffset(Math.max(0, offset - limit)),
4856
- disabled: offset === 0,
4857
- className: "px-5 py-2.5 text-sm font-medium border rounded-lg disabled:opacity-40 hover:bg-muted transition-colors",
4858
- children: "Previous"
4859
- }
4860
- ),
4861
- /* @__PURE__ */ jsxRuntime.jsx(
4862
- "button",
4863
- {
4864
- onClick: () => setOffset(offset + limit),
4865
- disabled: offset + limit >= total,
4866
- className: "px-5 py-2.5 text-sm font-medium border rounded-lg disabled:opacity-40 hover:bg-muted transition-colors",
4867
- children: "Next"
4868
- }
4869
- )
4870
- ] })
4871
- ] }) }) })
4872
- ] });
4873
- }
4874
- function CaseStudyPage(props) {
4875
- const {
4876
- productName,
4877
- caseStudiesListUrl = "/case-studies",
4878
- backLabel = "Back to Case Studies",
4879
- className = ""
4880
- } = props;
4881
- const client = useWebSDK();
4882
- const config = useWebSDKConfig();
4883
- const { product } = useProduct();
4884
- const resolvedProductId = product?.id || config.productId;
4885
- const [post, setPost] = React.useState(null);
4886
- const [loading, setLoading] = React.useState(true);
4887
- const [error, setError] = React.useState(null);
4888
- const slug = typeof window !== "undefined" ? window.location.pathname.split("/").pop() || "" : "";
4889
- React.useEffect(() => {
4890
- if (!slug || !resolvedProductId) return;
4891
- async function fetchPost() {
4892
- setLoading(true);
4893
- try {
4894
- const result = await client.query(CASE_STUDY_QUERY, {
4895
- slug,
4896
- productId: resolvedProductId
4897
- });
4898
- if (result.errors?.length) {
4899
- setError(result.errors[0]?.message ?? "Failed to load case study");
4900
- return;
4901
- }
4902
- setPost(result.data?.publicContentPage ?? null);
4903
- setError(null);
4904
- } catch {
4905
- setError("Failed to load case study");
4906
- } finally {
4907
- setLoading(false);
4908
- }
4909
- }
4910
- fetchPost();
4911
- }, [client, slug, resolvedProductId]);
4912
- if (loading) {
4913
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-full ${className}`, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-24", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "animate-pulse space-y-6", children: [
4914
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-8 bg-muted rounded w-2/3" }),
4915
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-1/3" }),
4916
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] bg-muted rounded-xl" }),
4917
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-3", children: [1, 2, 3, 4, 5].map((i) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded" }, i)) })
4918
- ] }) }) });
4919
- }
4920
- if (error || !post) {
4921
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-full ${className}`, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-24 text-center", children: [
4922
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl font-bold mb-4", children: "Case study not found" }),
4923
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground mb-6", children: error || "This case study doesn't exist or has been removed." }),
4924
- /* @__PURE__ */ jsxRuntime.jsxs(
4925
- "a",
4926
- {
4927
- href: caseStudiesListUrl,
4928
- className: "text-primary hover:underline font-medium",
4929
- children: [
4930
- "\u2190 ",
4931
- backLabel
4932
- ]
4933
- }
4934
- )
4935
- ] }) });
4936
- }
4937
- const date = post.publishedAt || post.lastUpdated;
4938
- const formattedDate = date ? new Date(date).toLocaleDateString("en-US", {
4939
- year: "numeric",
4940
- month: "long",
4941
- day: "numeric"
4942
- }) : "";
4943
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `w-full ${className}`, children: [
4944
- /* @__PURE__ */ jsxRuntime.jsx(
4945
- PageHead,
4946
- {
4947
- title: post.seoTitle || post.title,
4948
- description: post.seoDescription || post.excerpt || "",
4949
- productName,
4950
- keywords: post.seoKeywords || ""
4951
- }
4952
- ),
4953
- /* @__PURE__ */ jsxRuntime.jsxs("article", { className: "max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-16 sm:py-20", children: [
4954
- /* @__PURE__ */ jsxRuntime.jsxs(
4955
- "a",
4956
- {
4957
- href: caseStudiesListUrl,
4958
- className: "inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground mb-8 transition-colors",
4959
- children: [
4960
- "\u2190 ",
4961
- backLabel
4962
- ]
4963
- }
4964
- ),
4965
- post.category && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block text-xs font-medium text-primary bg-primary/10 px-2.5 py-1 rounded-full mb-4", children: post.category }),
4966
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-3xl sm:text-4xl md:text-5xl font-bold tracking-tight text-foreground mb-4", children: post.title }),
4967
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 text-sm text-muted-foreground mb-8", children: [
4968
- post.author && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-foreground", children: post.author }),
4969
- post.author && formattedDate && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
4970
- formattedDate && /* @__PURE__ */ jsxRuntime.jsx("span", { children: formattedDate }),
4971
- post.readingTime && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4972
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
4973
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
4974
- post.readingTime,
4975
- " min read"
4976
- ] })
4977
- ] })
4978
- ] }),
4979
- post.thumbnail && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] rounded-xl overflow-hidden mb-10", children: /* @__PURE__ */ jsxRuntime.jsx(
4980
- "img",
4981
- {
4982
- src: post.thumbnail,
4983
- alt: post.title,
4984
- className: "w-full h-full object-cover"
4985
- }
4986
- ) }),
4987
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "prose prose-gray dark:prose-invert max-w-none", children: /* @__PURE__ */ jsxRuntime.jsx(ContentRenderer, { content: post.content, format: post.contentFormat }) })
4988
- ] })
4989
- ] });
4990
- }
4991
- var CHANGELOG_TAG = "changelog";
4992
- var CHANGELOG_LIST_QUERY = `
4993
- query ChangelogList($productId: String!, $limit: Int, $offset: Int) {
4994
- publicContentPages(productId: $productId, limit: $limit, offset: $offset) {
4995
- items {
4996
- id
4997
- title
4998
- slug
4999
- lastUpdated
5000
- tags
5001
- excerpt
5002
- seoDescription
5003
- thumbnail
5004
- author
5005
- publishedAt
5006
- readingTime
5007
- featured
5008
- category
5009
- }
5010
- total
5011
- hasMore
5012
- }
5013
- }
5014
- `;
5015
- var CHANGELOG_ENTRY_QUERY = `
5016
- query ChangelogEntry($slug: String!, $productId: String) {
5017
- publicContentPage(slug: $slug, productId: $productId) {
5018
- content
5019
- contentFormat
5020
- }
5021
- }
5022
- `;
5023
- function formatDate(value) {
5024
- if (!value) return "";
5025
- return new Date(value).toLocaleDateString("en-US", {
5026
- year: "numeric",
5027
- month: "long",
5028
- day: "numeric"
5029
- });
5030
- }
5031
- function sortByDateDesc(a, b) {
5032
- const da = new Date(a.publishedAt || a.lastUpdated || 0).getTime();
5033
- const db = new Date(b.publishedAt || b.lastUpdated || 0).getTime();
5034
- return db - da;
5035
- }
5036
- function normalize(value) {
5037
- return value.toLowerCase().trim();
5038
- }
5039
- function paginate(items, page, pageSize) {
5040
- const start = (page - 1) * pageSize;
5041
- return items.slice(start, start + pageSize);
5042
- }
5043
- function parseEntryDate(date) {
5044
- return new Date(date).getTime();
5045
- }
5046
- function useCmsChangelog() {
5047
- const client = useWebSDK();
5048
- const config = useWebSDKConfig();
5049
- const { product } = useProduct();
5050
- const resolvedProductId = product?.id || config.productId;
5051
- const [entries, setEntries] = React.useState([]);
5052
- const [loading, setLoading] = React.useState(true);
5053
- const [error, setError] = React.useState(null);
5054
- const fetchEntries = React.useCallback(async () => {
5055
- if (!resolvedProductId) {
5056
- setLoading(false);
5057
- return;
5058
- }
5059
- setLoading(true);
5060
- try {
5061
- const listResult = await client.query(CHANGELOG_LIST_QUERY, {
5062
- productId: resolvedProductId,
5063
- limit: 50,
5064
- offset: 0
5065
- });
5066
- if (listResult.errors?.length) {
5067
- setError(listResult.errors[0]?.message ?? "Failed to load changelog");
5068
- return;
5069
- }
5070
- const changelogItems = (listResult.data?.publicContentPages?.items || []).filter((p) => p.tags?.includes(CHANGELOG_TAG)).sort(sortByDateDesc);
5071
- const resolved = await Promise.all(
5072
- changelogItems.map(async (item) => {
5073
- try {
5074
- const detail = await client.query(CHANGELOG_ENTRY_QUERY, {
5075
- slug: item.slug,
5076
- productId: resolvedProductId
5077
- });
5078
- const page = detail.data?.publicContentPage;
5079
- return {
5080
- ...item,
5081
- content: page?.content,
5082
- contentFormat: page?.contentFormat
5083
- };
5084
- } catch {
5085
- return { ...item };
5086
- }
5087
- })
5088
- );
5089
- setEntries(resolved);
5090
- setError(null);
5091
- } catch {
5092
- setError("Failed to load changelog");
5093
- } finally {
5094
- setLoading(false);
5095
- }
5096
- }, [client, resolvedProductId]);
5097
- React.useEffect(() => {
5098
- fetchEntries();
5099
- }, [fetchEntries]);
5100
- return { entries, loading, error };
5101
- }
5102
- function CmsTimeline(props) {
5103
- const { entries, loading, error } = props;
5104
- if (loading) {
5105
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-3xl mx-auto space-y-12", children: [1, 2, 3].map((i) => /* @__PURE__ */ jsxRuntime.jsx(
5106
- "div",
5107
- {
5108
- className: "rounded-2xl border border-border/40 bg-card overflow-hidden animate-pulse",
5109
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6 sm:p-8 space-y-4", children: [
5110
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3 bg-muted rounded w-1/3" }),
5111
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-6 bg-muted rounded w-2/3" }),
5112
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded" }),
5113
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-5/6" })
5114
- ] })
5115
- },
5116
- i
5117
- )) });
5118
- }
5119
- if (error) {
5120
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center py-12", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: error }) });
5121
- }
5122
- if (entries.length === 0) {
5123
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center py-12", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: "No changelog entries yet. Check back soon!" }) });
5124
- }
5125
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-3xl mx-auto space-y-12", children: entries.map((entry) => /* @__PURE__ */ jsxRuntime.jsxs(
5126
- "article",
5127
- {
5128
- id: entry.slug,
5129
- className: "rounded-2xl border border-border/40 bg-card overflow-hidden scroll-mt-24",
5130
- children: [
5131
- entry.thumbnail && /* @__PURE__ */ jsxRuntime.jsx(
5132
- "img",
5133
- {
5134
- src: entry.thumbnail,
5135
- alt: "",
5136
- width: 1024,
5137
- height: 576,
5138
- loading: "lazy",
5139
- decoding: "async",
5140
- className: "w-full aspect-video object-cover border-b border-border/40"
5141
- }
5142
- ),
5143
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6 sm:p-8", children: [
5144
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground mb-2", children: [
5145
- entry.publishedAt && /* @__PURE__ */ jsxRuntime.jsx("time", { dateTime: entry.publishedAt, children: formatDate(entry.publishedAt || entry.lastUpdated) }),
5146
- entry.author && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5147
- " \xB7 ",
5148
- entry.author
5149
- ] })
5150
- ] }),
5151
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-xl sm:text-2xl font-bold text-foreground mb-4", children: entry.title }),
5152
- entry.content ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "prose prose-sm prose-gray dark:prose-invert max-w-none", children: /* @__PURE__ */ jsxRuntime.jsx(
5153
- ContentRenderer,
5154
- {
5155
- content: entry.content,
5156
- format: entry.contentFormat
5157
- }
5158
- ) }) : entry.excerpt && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground leading-relaxed", children: entry.excerpt })
5159
- ] })
5160
- ]
5161
- },
5162
- entry.id
5163
- )) });
5164
- }
5165
- function EntryContent({ content }) {
5166
- if (typeof content === "string") {
5167
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "prose prose-sm prose-gray dark:prose-invert max-w-none", children: /* @__PURE__ */ jsxRuntime.jsx(ContentRenderer, { content, format: "markdown" }) });
5168
- }
5169
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "prose prose-sm max-w-none", children: content });
5170
- }
5171
- function EntriesTimeline(props) {
5172
- const { entries, pageSize, productName } = props;
5173
- const [query, setQuery] = React.useState("");
5174
- const [selectedCategory, setSelectedCategory] = React.useState(null);
5175
- const [page, setPage] = React.useState(1);
5176
- const categories = React.useMemo(
5177
- () => Array.from(new Set(entries.map((e) => e.category))).sort(),
5178
- [entries]
5179
- );
5180
- const filtered = React.useMemo(() => {
5181
- const normalized = normalize(query);
5182
- return entries.filter((entry) => {
5183
- if (selectedCategory && entry.category !== selectedCategory) {
5184
- return false;
5185
- }
5186
- if (!normalized) return true;
5187
- const haystack = [
5188
- entry.title,
5189
- entry.version,
5190
- entry.category,
5191
- ...entry.tags ?? [],
5192
- typeof entry.content === "string" ? entry.content : ""
5193
- ].join(" ").toLowerCase();
5194
- return haystack.includes(normalized);
5195
- }).sort((a, b) => parseEntryDate(b.date) - parseEntryDate(a.date));
5196
- }, [entries, query, selectedCategory]);
5197
- const totalPages = Math.max(1, Math.ceil(filtered.length / pageSize));
5198
- const safePage = Math.min(page, totalPages);
5199
- const pageItems = paginate(filtered, safePage, pageSize);
5200
- React.useEffect(() => {
5201
- setPage(1);
5202
- }, [query, selectedCategory]);
5203
- if (entries.length === 0) {
5204
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center py-12", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: "No changelog entries yet. Check back soon!" }) });
5205
- }
5206
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-3xl mx-auto", children: [
5207
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-8 space-y-4", children: [
5208
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
5209
- /* @__PURE__ */ jsxRuntime.jsx(
5210
- lucideReact.Search,
5211
- {
5212
- className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground",
5213
- "aria-hidden": "true"
5214
- }
5215
- ),
5216
- /* @__PURE__ */ jsxRuntime.jsx(
5217
- Input,
5218
- {
5219
- type: "search",
5220
- placeholder: "Search releases, features, fixes\u2026",
5221
- value: query,
5222
- onChange: (e) => setQuery(e.target.value),
5223
- className: "pl-9",
5224
- "aria-label": "Search changelog"
5225
- }
5226
- )
5227
- ] }),
5228
- categories.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap gap-2", children: [
5229
- /* @__PURE__ */ jsxRuntime.jsx(
5230
- "button",
5231
- {
5232
- type: "button",
5233
- onClick: () => setSelectedCategory(null),
5234
- className: cn(
5235
- "px-3 py-1 rounded-full text-xs font-medium border transition-colors",
5236
- selectedCategory === null ? "bg-primary text-primary-foreground border-primary" : "bg-background text-muted-foreground border-border/60 hover:border-primary/40"
5237
- ),
5238
- children: "All"
5239
- }
5240
- ),
5241
- categories.map((category) => /* @__PURE__ */ jsxRuntime.jsx(
5242
- "button",
5243
- {
5244
- type: "button",
5245
- onClick: () => setSelectedCategory(category),
5246
- className: cn(
5247
- "px-3 py-1 rounded-full text-xs font-medium border transition-colors",
5248
- selectedCategory === category ? "bg-primary text-primary-foreground border-primary" : "bg-background text-muted-foreground border-border/60 hover:border-primary/40"
5249
- ),
5250
- children: category
5251
- },
5252
- category
5253
- ))
5254
- ] })
5255
- ] }),
5256
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
5257
- /* @__PURE__ */ jsxRuntime.jsx(
5258
- "div",
5259
- {
5260
- className: "absolute left-[1.125rem] top-3 bottom-3 w-px bg-border/60 hidden sm:block",
5261
- "aria-hidden": "true"
5262
- }
5263
- ),
5264
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-8", children: pageItems.map((entry) => /* @__PURE__ */ jsxRuntime.jsxs(
5265
- "article",
5266
- {
5267
- className: "relative sm:pl-12",
5268
- children: [
5269
- /* @__PURE__ */ jsxRuntime.jsx(
5270
- "div",
5271
- {
5272
- className: "hidden sm:flex absolute left-2 top-2 h-5 w-5 items-center justify-center rounded-full border-2 border-background bg-primary ring-1 ring-border/40",
5273
- "aria-hidden": "true",
5274
- children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-2 w-2 rounded-full bg-primary-foreground" })
5275
- }
5276
- ),
5277
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-2xl border border-border/40 bg-card overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6 sm:p-8", children: [
5278
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2 mb-3", children: [
5279
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex items-center rounded-md bg-primary/10 px-2 py-1 text-xs font-medium text-primary", children: entry.category }),
5280
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center rounded-md bg-muted px-2 py-1 text-xs font-mono font-medium text-muted-foreground", children: [
5281
- "v",
5282
- entry.version
5283
- ] }),
5284
- /* @__PURE__ */ jsxRuntime.jsx(
5285
- "time",
5286
- {
5287
- className: "text-xs text-muted-foreground",
5288
- dateTime: entry.date,
5289
- children: formatDate(entry.date)
5290
- }
5291
- )
5292
- ] }),
5293
- entry.tags && entry.tags.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1.5 mb-3", children: entry.tags.map((tag) => /* @__PURE__ */ jsxRuntime.jsx(
5294
- "span",
5295
- {
5296
- className: "text-[10px] uppercase tracking-wide text-muted-foreground bg-background border border-border/40 px-1.5 py-0.5 rounded",
5297
- children: tag
5298
- },
5299
- tag
5300
- )) }),
5301
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-xl sm:text-2xl font-bold text-foreground mb-4", children: entry.title }),
5302
- /* @__PURE__ */ jsxRuntime.jsx(EntryContent, { content: entry.content })
5303
- ] }) })
5304
- ]
5305
- },
5306
- `${entry.version}-${entry.date}`
5307
- )) })
5308
- ] }),
5309
- pageItems.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center py-12", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: "No entries match your filters." }) }),
5310
- totalPages > 1 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-10 flex items-center justify-between", children: [
5311
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-muted-foreground", children: [
5312
- "Page ",
5313
- safePage,
5314
- " of ",
5315
- totalPages,
5316
- " \xB7 ",
5317
- filtered.length,
5318
- " entries"
5319
- ] }),
5320
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
5321
- /* @__PURE__ */ jsxRuntime.jsx(
5322
- Button,
5323
- {
5324
- variant: "outline",
5325
- size: "sm",
5326
- onClick: () => setPage((p) => Math.max(1, p - 1)),
5327
- disabled: safePage <= 1,
5328
- "aria-label": "Previous page",
5329
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { className: "h-4 w-4" })
5330
- }
5331
- ),
5332
- /* @__PURE__ */ jsxRuntime.jsx(
5333
- Button,
5334
- {
5335
- variant: "outline",
5336
- size: "sm",
5337
- onClick: () => setPage((p) => Math.min(totalPages, p + 1)),
5338
- disabled: safePage >= totalPages,
5339
- "aria-label": "Next page",
5340
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "h-4 w-4" })
5341
- }
5342
- )
5343
- ] })
5344
- ] }),
5345
- productName && pageItems.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("script", { type: "application/ld+json", children: JSON.stringify({
5346
- "@context": "https://schema.org",
5347
- "@type": "ItemList",
5348
- name: `${productName} Changelog`,
5349
- itemListElement: pageItems.map((entry, index) => ({
5350
- "@type": "ListItem",
5351
- position: (safePage - 1) * pageSize + index + 1,
5352
- name: entry.title,
5353
- description: typeof entry.content === "string" ? entry.content.slice(0, 200) : void 0,
5354
- datePublished: entry.date
5355
- }))
5356
- }) })
5357
- ] });
5358
- }
5359
- function ChangelogPage(props) {
5360
- const {
5361
- productName,
5362
- heroEyebrow = "What's new",
5363
- heroTitle = "Changelog",
5364
- heroSubtitle = "Platform updates, improvements, and announcements \u2014 newest first.",
5365
- ctaTitle,
5366
- ctaSubtitle,
5367
- ctaHref,
5368
- ctaLabel,
5369
- className = "",
5370
- seoTitle,
5371
- seoDescription,
5372
- seoKeywords,
5373
- entries,
5374
- pageSize = 10
5375
- } = props;
5376
- const { product } = useProduct();
5377
- const resolvedProductName = productName || product?.name;
5378
- const cms = useCmsChangelog();
5379
- const showCta = Boolean(ctaHref && ctaLabel);
5380
- const isPropDriven = entries != null;
5381
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `w-full ${className}`, children: [
5382
- /* @__PURE__ */ jsxRuntime.jsx(
5383
- PageHead,
5384
- {
5385
- title: seoTitle || heroTitle,
5386
- description: seoDescription || heroSubtitle,
5387
- productName: resolvedProductName,
5388
- keywords: seoKeywords
5389
- }
5390
- ),
5391
- /* @__PURE__ */ jsxRuntime.jsx("section", { className: "py-16 md:py-20 px-4 sm:px-6 lg:px-8 text-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-3xl mx-auto", children: [
5392
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-primary/10 border border-primary/20 text-xs font-medium text-primary mb-6", children: [
5393
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.History, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
5394
- heroEyebrow
5395
- ] }),
5396
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-4xl sm:text-5xl font-bold text-foreground mb-4", children: heroTitle }),
5397
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-lg text-muted-foreground", children: heroSubtitle })
5398
- ] }) }),
5399
- /* @__PURE__ */ jsxRuntime.jsx("section", { className: "px-4 sm:px-6 lg:px-8 pb-20", children: isPropDriven ? /* @__PURE__ */ jsxRuntime.jsx(
5400
- EntriesTimeline,
5401
- {
5402
- entries,
5403
- pageSize,
5404
- productName: resolvedProductName
5405
- }
5406
- ) : /* @__PURE__ */ jsxRuntime.jsx(
5407
- CmsTimeline,
5408
- {
5409
- entries: cms.entries,
5410
- loading: cms.loading,
5411
- error: cms.error
5412
- }
5413
- ) }),
5414
- showCta && /* @__PURE__ */ jsxRuntime.jsx("section", { className: "py-14 px-4 sm:px-6 lg:px-8 bg-primary/5 border-t border-border/40", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-2xl mx-auto text-center", children: [
5415
- ctaTitle && /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl font-bold text-foreground mb-3", children: ctaTitle }),
5416
- ctaSubtitle && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground mb-7", children: ctaSubtitle }),
5417
- /* @__PURE__ */ jsxRuntime.jsx(
5418
- "a",
5419
- {
5420
- href: ctaHref,
5421
- className: "inline-flex items-center justify-center rounded-lg bg-primary px-6 py-3 text-sm font-medium text-primary-foreground hover:opacity-90 transition-opacity",
5422
- children: ctaLabel
5423
- }
5424
- )
5425
- ] }) })
5426
- ] });
5427
- }
5428
-
5429
- // src/content/utils.ts
5430
- function formatBytes(bytes) {
5431
- if (bytes == null) return "";
5432
- if (bytes < 1024) return `${bytes} B`;
5433
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)} KB`;
5434
- return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
5435
- }
5436
- function fileExt(file) {
5437
- const name = file.fileName || file.fileUrl.split("/").pop()?.split("?")[0] || "";
5438
- const ext = name.split(".").pop();
5439
- return ext && ext !== name ? ext.toUpperCase() : "FILE";
5440
- }
5441
- async function downloadFile(e, file) {
5442
- e.preventDefault();
5443
- const openInTab = () => window.open(file.fileUrl, "_blank", "noopener,noreferrer");
5444
- const name = file.fileName || file.fileUrl.split("/").pop()?.split("?")[0] || file.title || "download";
5445
- try {
5446
- const res = await fetch(file.fileUrl, { mode: "cors" });
5447
- if (!res.ok) {
5448
- openInTab();
5449
- return;
5450
- }
5451
- const blob = await res.blob();
5452
- const objUrl = URL.createObjectURL(blob);
5453
- const a = document.createElement("a");
5454
- a.href = objUrl;
5455
- a.download = name;
5456
- document.body.appendChild(a);
5457
- a.click();
5458
- a.remove();
5459
- URL.revokeObjectURL(objUrl);
5460
- } catch {
5461
- openInTab();
5462
- }
5463
- }
5464
- var PRESS_KIT_QUERY = `
5465
- query PublicPressKit($productId: String!, $limit: Int, $offset: Int) {
5466
- publicPressKitAssets(productId: $productId, limit: $limit, offset: $offset) {
5467
- items {
5468
- id
5469
- category
5470
- title
5471
- slug
5472
- description
5473
- fileUrl
5474
- fileName
5475
- fileSize
5476
- mimeType
5477
- thumbnailUrl
5478
- version
5479
- tags
5480
- sortOrder
5481
- updatedAt
5482
- }
5483
- total
5484
- hasMore
5485
- }
5486
- }
5487
- `;
5488
- var PRESS_RELEASES_QUERY = `
5489
- query PublicPressReleases($productId: String!, $limit: Int, $offset: Int) {
5490
- publicContentPages(productId: $productId, limit: $limit, offset: $offset) {
5491
- items {
5492
- id
5493
- title
5494
- slug
5495
- lastUpdated
5496
- tags
5497
- seoDescription
5498
- excerpt
5499
- thumbnail
5500
- author
5501
- publishedAt
5502
- readingTime
5503
- featured
5504
- category
5505
- }
5506
- total
5507
- hasMore
5508
- }
5509
- }
5510
- `;
5511
- var CATEGORY_ORDER = [
5512
- "LOGO",
5513
- "BANNER",
5514
- "SCREENSHOT",
5515
- "BRAND_GUIDE",
5516
- "BROCHURE",
5517
- "DOCUMENT",
5518
- "PRESS_RELEASE"
5519
- ];
5520
- var CATEGORY_LABELS = {
5521
- LOGO: "Logos",
5522
- BANNER: "Banners",
5523
- SCREENSHOT: "Screenshots",
5524
- BRAND_GUIDE: "Brand Guides",
5525
- BROCHURE: "Brochures",
5526
- DOCUMENT: "Documents",
5527
- PRESS_RELEASE: "Press Releases"
5528
- };
5529
- function isImageAsset(a) {
5530
- return (a.mimeType || "").startsWith("image/");
5531
- }
5532
- function PressKitLightbox({
5533
- asset,
5534
- onClose
5535
- }) {
5536
- React.useEffect(() => {
5537
- if (!asset) return;
5538
- const onKey = (e) => {
5539
- if (e.key === "Escape") onClose();
5540
- };
5541
- document.addEventListener("keydown", onKey);
5542
- const prevOverflow = document.body.style.overflow;
5543
- document.body.style.overflow = "hidden";
5544
- return () => {
5545
- document.removeEventListener("keydown", onKey);
5546
- document.body.style.overflow = prevOverflow;
5547
- };
5548
- }, [asset, onClose]);
5549
- if (!asset) return null;
5550
- const ext = (asset.mimeType || "file").split("/").pop()?.toUpperCase() || "FILE";
5551
- const size = formatBytes(asset.fileSize);
5552
- return /* @__PURE__ */ jsxRuntime.jsx(
5553
- "div",
5554
- {
5555
- role: "dialog",
5556
- "aria-modal": "true",
5557
- "aria-label": `${asset.title} preview`,
5558
- onClick: onClose,
5559
- className: "fixed inset-0 z-50 flex items-center justify-center bg-black/80 p-4 sm:p-8",
5560
- children: /* @__PURE__ */ jsxRuntime.jsxs(
5561
- "div",
5562
- {
5563
- onClick: (e) => e.stopPropagation(),
5564
- className: "relative flex max-h-full w-full max-w-4xl flex-col overflow-hidden rounded-xl border bg-card shadow-2xl",
5565
- children: [
5566
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-4 border-b p-4", children: [
5567
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
5568
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "truncate text-sm font-semibold text-foreground", children: asset.title }),
5569
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground", children: [
5570
- ext,
5571
- size ? ` \xB7 ${size}` : ""
5572
- ] })
5573
- ] }),
5574
- /* @__PURE__ */ jsxRuntime.jsx(
5575
- "button",
5576
- {
5577
- type: "button",
5578
- onClick: onClose,
5579
- "aria-label": "Close preview",
5580
- className: "shrink-0 rounded-md p-1.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
5581
- children: /* @__PURE__ */ jsxRuntime.jsx(
5582
- "svg",
5583
- {
5584
- className: "h-5 w-5",
5585
- fill: "none",
5586
- viewBox: "0 0 24 24",
5587
- stroke: "currentColor",
5588
- strokeWidth: 2,
5589
- children: /* @__PURE__ */ jsxRuntime.jsx(
5590
- "path",
5591
- {
5592
- strokeLinecap: "round",
5593
- strokeLinejoin: "round",
5594
- d: "M6 18L18 6M6 6l12 12"
5595
- }
5596
- )
5597
- }
5598
- )
5599
- }
5600
- )
5601
- ] }),
5602
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-1 items-center justify-center overflow-auto bg-muted/20 p-4", children: /* @__PURE__ */ jsxRuntime.jsx(
5603
- "img",
5604
- {
5605
- src: asset.fileUrl,
5606
- alt: asset.title,
5607
- className: "max-h-[70vh] w-auto max-w-full object-contain"
5608
- }
5609
- ) }),
5610
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-end border-t p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(
5611
- "a",
5612
- {
5613
- href: asset.fileUrl,
5614
- target: "_blank",
5615
- rel: "noopener noreferrer",
5616
- download: true,
5617
- onClick: (e) => {
5618
- void downloadFile(e, asset);
5619
- },
5620
- className: "inline-flex items-center gap-1.5 rounded-lg bg-primary px-4 py-2 text-sm font-medium text-primary-foreground transition-opacity hover:opacity-90",
5621
- children: [
5622
- /* @__PURE__ */ jsxRuntime.jsx(
5623
- "svg",
5624
- {
5625
- className: "h-4 w-4",
5626
- fill: "none",
5627
- viewBox: "0 0 24 24",
5628
- stroke: "currentColor",
5629
- strokeWidth: 2,
5630
- children: /* @__PURE__ */ jsxRuntime.jsx(
5631
- "path",
5632
- {
5633
- strokeLinecap: "round",
5634
- strokeLinejoin: "round",
5635
- d: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
5636
- }
5637
- )
5638
- }
5639
- ),
5640
- "Download"
5641
- ]
5642
- }
5643
- ) })
5644
- ]
5645
- }
5646
- )
5647
- }
5648
- );
5649
- }
5650
- function AssetCard({
5651
- asset,
5652
- onPreview
5653
- }) {
5654
- const date = asset.updatedAt ? new Date(asset.updatedAt).toLocaleDateString("en-US", {
5655
- year: "numeric",
5656
- month: "short",
5657
- day: "numeric"
5658
- }) : "";
5659
- const ext = (asset.mimeType || "file").split("/").pop()?.toUpperCase() || "FILE";
5660
- const size = formatBytes(asset.fileSize);
5661
- const [imgError, setImgError] = React.useState(false);
5662
- const thumb = asset.thumbnailUrl && !imgError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] overflow-hidden bg-muted/30", children: /* @__PURE__ */ jsxRuntime.jsx(
5663
- "img",
5664
- {
5665
- src: asset.thumbnailUrl,
5666
- alt: asset.title,
5667
- className: "w-full h-full object-contain group-hover:scale-105 transition-transform duration-300",
5668
- loading: "lazy",
5669
- onError: () => setImgError(true)
5670
- }
5671
- ) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] bg-muted/50 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(
5672
- "svg",
5673
- {
5674
- className: "w-12 h-12 text-muted-foreground/20",
5675
- fill: "none",
5676
- viewBox: "0 0 24 24",
5677
- stroke: "currentColor",
5678
- children: /* @__PURE__ */ jsxRuntime.jsx(
5679
- "path",
5680
- {
5681
- strokeLinecap: "round",
5682
- strokeLinejoin: "round",
5683
- strokeWidth: 1.5,
5684
- d: "M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z"
5685
- }
5686
- )
5687
- }
5688
- ) });
5689
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "group flex flex-col bg-card border rounded-xl overflow-hidden hover:shadow-lg hover:border-primary/30 transition-all duration-300", children: [
5690
- onPreview && isImageAsset(asset) ? /* @__PURE__ */ jsxRuntime.jsx(
5691
- "button",
5692
- {
5693
- type: "button",
5694
- onClick: () => onPreview(asset),
5695
- "aria-label": `Preview ${asset.title}`,
5696
- className: "block w-full text-left cursor-zoom-in",
5697
- children: thumb
5698
- }
5699
- ) : /* @__PURE__ */ jsxRuntime.jsx(
5700
- "a",
5701
- {
5702
- href: asset.fileUrl,
5703
- target: "_blank",
5704
- rel: "noopener noreferrer",
5705
- "aria-label": `Open ${asset.title}`,
5706
- className: "block",
5707
- children: thumb
5708
- }
5709
- ),
5710
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col flex-1 p-5", children: [
5711
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block self-start text-xs font-medium text-primary bg-primary/10 px-2.5 py-1 rounded-full mb-3", children: CATEGORY_LABELS[asset.category] ?? asset.category }),
5712
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-base font-semibold text-foreground mb-1 line-clamp-2", children: asset.title }),
5713
- asset.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground mb-3 line-clamp-2", children: asset.description }),
5714
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-auto flex items-center justify-between pt-3", children: [
5715
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
5716
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: ext }),
5717
- size && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5718
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
5719
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: size })
5720
- ] }),
5721
- date && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5722
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
5723
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: date })
5724
- ] })
5725
- ] }),
5726
- /* @__PURE__ */ jsxRuntime.jsxs(
5727
- "a",
5728
- {
5729
- href: asset.fileUrl,
5730
- target: "_blank",
5731
- rel: "noopener noreferrer",
5732
- download: true,
5733
- onClick: (e) => {
5734
- void downloadFile(e, asset);
5735
- },
5736
- className: "inline-flex items-center gap-1.5 text-sm font-medium text-primary hover:underline",
5737
- children: [
5738
- /* @__PURE__ */ jsxRuntime.jsx(
5739
- "svg",
5740
- {
5741
- className: "w-4 h-4",
5742
- fill: "none",
5743
- viewBox: "0 0 24 24",
5744
- stroke: "currentColor",
5745
- strokeWidth: 2,
5746
- children: /* @__PURE__ */ jsxRuntime.jsx(
5747
- "path",
5748
- {
5749
- strokeLinecap: "round",
5750
- strokeLinejoin: "round",
5751
- d: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
5752
- }
5753
- )
5754
- }
5755
- ),
5756
- "Download"
5757
- ]
5758
- }
5759
- )
5760
- ] })
5761
- ] })
5762
- ] });
5763
- }
5764
- function PressReleaseCard({
5765
- release,
5766
- basePath
5767
- }) {
5768
- const date = release.publishedAt || release.lastUpdated;
5769
- const formattedDate = date ? new Date(date).toLocaleDateString("en-US", {
5770
- year: "numeric",
5771
- month: "long",
5772
- day: "numeric"
5773
- }) : "";
5774
- const [imgError, setImgError] = React.useState(false);
5775
- return /* @__PURE__ */ jsxRuntime.jsxs(
5776
- "a",
5777
- {
5778
- href: `${basePath}/${release.slug}`,
5779
- className: "group flex flex-col bg-card border rounded-xl overflow-hidden hover:shadow-lg hover:border-primary/30 transition-all duration-300",
5780
- children: [
5781
- release.thumbnail && !imgError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] overflow-hidden bg-muted/30", children: /* @__PURE__ */ jsxRuntime.jsx(
5782
- "img",
5783
- {
5784
- src: release.thumbnail,
5785
- alt: release.title,
5786
- className: "w-full h-full object-cover group-hover:scale-105 transition-transform duration-300",
5787
- loading: "lazy",
5788
- onError: () => setImgError(true)
5789
- }
5790
- ) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] bg-muted/50 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(
5791
- "svg",
5792
- {
5793
- className: "w-12 h-12 text-muted-foreground/20",
5794
- fill: "none",
5795
- viewBox: "0 0 24 24",
5796
- stroke: "currentColor",
5797
- children: /* @__PURE__ */ jsxRuntime.jsx(
5798
- "path",
5799
- {
5800
- strokeLinecap: "round",
5801
- strokeLinejoin: "round",
5802
- strokeWidth: 1.5,
5803
- d: "M19 20H5a2 2 0 01-2-2V6a2 2 0 012-2h10a2 2 0 012 2v1m-1 13a2 2 0 01-2-2V7m3 13a2 2 0 002-2V9a2 2 0 00-2-2h-2m-4-3H9M7 16h6M7 8h6v4H7V8z"
5804
- }
5805
- )
5806
- }
5807
- ) }),
5808
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col flex-1 p-5 sm:p-6", children: [
5809
- release.category && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block self-start text-xs font-medium text-primary bg-primary/10 px-2.5 py-1 rounded-full mb-3", children: release.category }),
5810
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-foreground mb-2 group-hover:text-primary transition-colors line-clamp-2", children: release.title }),
5811
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground mb-4 line-clamp-3", children: release.excerpt || release.seoDescription || "" }),
5812
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-auto flex items-center gap-3 text-xs text-muted-foreground", children: [
5813
- release.author && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: release.author }),
5814
- release.author && formattedDate && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
5815
- formattedDate && /* @__PURE__ */ jsxRuntime.jsx("span", { children: formattedDate }),
5816
- release.readingTime ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5817
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
5818
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
5819
- release.readingTime,
5820
- " min read"
5821
- ] })
5822
- ] }) : null
2530
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl font-bold text-foreground mb-3", children: "Invalid Unsubscribe Link" }),
2531
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: "This unsubscribe link is invalid or missing. If you want to unsubscribe, please use the link from your newsletter email." })
5823
2532
  ] })
5824
2533
  ] })
5825
2534
  ]
5826
2535
  }
5827
2536
  );
5828
2537
  }
5829
- function PressKitPage(props) {
5830
- const {
5831
- productName,
5832
- heroTitle = "Press & Media Kit",
5833
- heroSubtitle = "Official logos, brand assets, and media resources. Everything you need to write about us.",
5834
- mediaContactEmail,
5835
- brandKitUrl,
5836
- className = "",
5837
- seoTitle,
5838
- seoDescription,
5839
- seoKeywords,
5840
- showPressReleases = true,
5841
- pressReleaseBasePath = "/press",
5842
- pressReleasesTitle = "Press Releases"
5843
- } = props;
5844
- const client = useWebSDK();
5845
- const config = useWebSDKConfig();
5846
- const { product } = useProduct();
5847
- const resolvedProductId = product?.id || config.productId;
5848
- const contactEmail = mediaContactEmail || product?.contactEmail || void 0;
5849
- const [assets, setAssets] = React.useState([]);
5850
- const [loading, setLoading] = React.useState(true);
5851
- const [error, setError] = React.useState(null);
5852
- const [activeCategory, setActiveCategory] = React.useState("ALL");
5853
- const [releases, setReleases] = React.useState([]);
5854
- const [releasesLoading, setReleasesLoading] = React.useState(true);
5855
- const [preview, setPreview] = React.useState(null);
5856
- const fetchAssets = React.useCallback(async () => {
5857
- if (!resolvedProductId) {
5858
- setLoading(false);
5859
- return;
5860
- }
5861
- setLoading(true);
5862
- try {
5863
- const PAGE_SIZE = 100;
5864
- const MAX_PAGES = 10;
5865
- const all = [];
5866
- let offset = 0;
5867
- for (let pageNum = 0; pageNum < MAX_PAGES; pageNum++) {
5868
- const result = await client.query(PRESS_KIT_QUERY, {
5869
- productId: resolvedProductId,
5870
- limit: PAGE_SIZE,
5871
- offset
5872
- });
5873
- if (result.errors?.length) {
5874
- setError(result.errors[0]?.message ?? "Failed to load press kit");
5875
- return;
5876
- }
5877
- const page = result.data?.publicPressKitAssets;
5878
- const items = page?.items || [];
5879
- all.push(...items);
5880
- if (!page?.hasMore || items.length === 0) break;
5881
- offset += PAGE_SIZE;
5882
- }
5883
- setAssets(all);
5884
- setError(null);
5885
- } catch {
5886
- setError("Failed to load press kit");
5887
- } finally {
5888
- setLoading(false);
5889
- }
5890
- }, [client, resolvedProductId]);
5891
- React.useEffect(() => {
5892
- fetchAssets();
5893
- }, [fetchAssets]);
5894
- const fetchReleases = React.useCallback(async () => {
5895
- if (!resolvedProductId || !showPressReleases) {
5896
- setReleasesLoading(false);
5897
- return;
5898
- }
5899
- setReleasesLoading(true);
5900
- try {
5901
- const PAGE_SIZE = 100;
5902
- const MAX_PAGES = 10;
5903
- const all = [];
5904
- let offset = 0;
5905
- for (let pageNum = 0; pageNum < MAX_PAGES; pageNum++) {
5906
- const result = await client.query(PRESS_RELEASES_QUERY, {
5907
- productId: resolvedProductId,
5908
- limit: PAGE_SIZE,
5909
- offset
5910
- });
5911
- if (result.errors?.length) {
5912
- setReleases([]);
5913
- return;
5914
- }
5915
- const page = result.data?.publicContentPages;
5916
- const items = page?.items || [];
5917
- all.push(...items);
5918
- if (!page?.hasMore || items.length === 0) break;
5919
- offset += PAGE_SIZE;
5920
- }
5921
- const pressOnly = all.filter((p) => p.tags?.includes("press")).sort((a, b) => {
5922
- const da = new Date(a.publishedAt || a.lastUpdated || 0).getTime();
5923
- const db = new Date(b.publishedAt || b.lastUpdated || 0).getTime();
5924
- return db - da;
5925
- });
5926
- setReleases(pressOnly);
5927
- } catch {
5928
- setReleases([]);
5929
- } finally {
5930
- setReleasesLoading(false);
5931
- }
5932
- }, [client, resolvedProductId, showPressReleases]);
2538
+ function RybbitAnalytics({
2539
+ siteId,
2540
+ scriptUrl = "https://analytics.burdenoff.com/api/script.js",
2541
+ enabled = true
2542
+ }) {
5933
2543
  React.useEffect(() => {
5934
- fetchReleases();
5935
- }, [fetchReleases]);
5936
- const presentCategories = CATEGORY_ORDER.filter(
5937
- (c) => assets.some((a) => a.category === c)
5938
- );
5939
- const visibleAssets = activeCategory === "ALL" ? assets : assets.filter((a) => a.category === activeCategory);
5940
- const grouped = (activeCategory === "ALL" ? presentCategories : [activeCategory]).map((cat) => ({
5941
- cat,
5942
- items: visibleAssets.filter((a) => a.category === cat).sort((a, b) => a.sortOrder - b.sortOrder)
5943
- })).filter((g) => g.items.length > 0);
5944
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `w-full ${className}`, children: [
5945
- seoTitle && /* @__PURE__ */ jsxRuntime.jsx(
5946
- PageHead,
5947
- {
5948
- title: seoTitle,
5949
- description: seoDescription || heroSubtitle,
5950
- productName,
5951
- keywords: seoKeywords
2544
+ if (!enabled || !siteId) return;
2545
+ const existingScript = document.querySelector(
2546
+ `script[data-site-id="${siteId}"]`
2547
+ );
2548
+ if (existingScript) return;
2549
+ const script = document.createElement("script");
2550
+ script.src = scriptUrl;
2551
+ script.defer = true;
2552
+ script.setAttribute("data-site-id", siteId);
2553
+ document.head.appendChild(script);
2554
+ return () => {
2555
+ const scriptToRemove = document.querySelector(
2556
+ `script[data-site-id="${siteId}"]`
2557
+ );
2558
+ if (scriptToRemove) {
2559
+ document.head.removeChild(scriptToRemove);
5952
2560
  }
5953
- ),
5954
- /* @__PURE__ */ jsxRuntime.jsx("section", { className: "py-16 sm:py-20 md:py-24", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center max-w-3xl mx-auto", children: [
5955
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-3xl sm:text-4xl md:text-5xl font-bold tracking-tight text-foreground", children: heroTitle }),
5956
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-4 text-lg text-muted-foreground", children: heroSubtitle }),
5957
- (brandKitUrl || contactEmail) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-8 flex flex-wrap items-center justify-center gap-4", children: [
5958
- brandKitUrl && /* @__PURE__ */ jsxRuntime.jsx(
5959
- "a",
5960
- {
5961
- href: brandKitUrl,
5962
- target: "_blank",
5963
- rel: "noopener noreferrer",
5964
- className: "inline-flex items-center gap-2 px-6 py-3 rounded-lg bg-primary text-primary-foreground font-medium hover:opacity-90 transition-opacity",
5965
- children: "Download brand kit"
5966
- }
5967
- ),
5968
- contactEmail && /* @__PURE__ */ jsxRuntime.jsx(
5969
- "a",
5970
- {
5971
- href: `mailto:${contactEmail}`,
5972
- className: "inline-flex items-center gap-2 px-6 py-3 rounded-lg border font-medium text-foreground hover:bg-muted transition-colors",
5973
- children: "Media inquiries"
5974
- }
5975
- )
5976
- ] })
5977
- ] }) }) }),
5978
- showPressReleases && (releasesLoading || releases.length > 0) && /* @__PURE__ */ jsxRuntime.jsx("section", { className: "pb-16 sm:pb-20", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: [
5979
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-xl font-semibold text-foreground mb-6", children: pressReleasesTitle }),
5980
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid sm:grid-cols-2 lg:grid-cols-3 gap-6", children: releasesLoading ? [1, 2, 3].map((i) => /* @__PURE__ */ jsxRuntime.jsxs(
5981
- "div",
5982
- {
5983
- className: "bg-card border rounded-xl overflow-hidden animate-pulse",
5984
- children: [
5985
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] bg-muted" }),
5986
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-5 space-y-3", children: [
5987
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-1/4" }),
5988
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-5 bg-muted rounded w-3/4" }),
5989
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-1/2" })
5990
- ] })
5991
- ]
5992
- },
5993
- i
5994
- )) : releases.map((r) => /* @__PURE__ */ jsxRuntime.jsx(
5995
- PressReleaseCard,
5996
- {
5997
- release: r,
5998
- basePath: pressReleaseBasePath
5999
- },
6000
- r.id
6001
- )) })
6002
- ] }) }),
6003
- /* @__PURE__ */ jsxRuntime.jsx("section", { className: "pb-16 sm:pb-20", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid sm:grid-cols-2 lg:grid-cols-3 gap-6", children: [1, 2, 3, 4, 5, 6].map((i) => /* @__PURE__ */ jsxRuntime.jsxs(
6004
- "div",
6005
- {
6006
- className: "bg-card border rounded-xl overflow-hidden animate-pulse",
6007
- children: [
6008
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] bg-muted" }),
6009
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-5 space-y-3", children: [
6010
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-1/4" }),
6011
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-5 bg-muted rounded w-3/4" }),
6012
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-1/2" })
6013
- ] })
6014
- ]
6015
- },
6016
- i
6017
- )) }) : error ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center py-12", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: error }) }) : assets.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center py-12", children: /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-muted-foreground", children: [
6018
- "Press materials are coming soon. For media inquiries",
6019
- contactEmail ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
6020
- ", contact ",
6021
- /* @__PURE__ */ jsxRuntime.jsx(
6022
- "a",
6023
- {
6024
- href: `mailto:${contactEmail}`,
6025
- className: "text-primary hover:underline",
6026
- children: contactEmail
6027
- }
6028
- )
6029
- ] }) : " please reach out to our team",
6030
- "."
6031
- ] }) }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
6032
- presentCategories.length > 1 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-2 mb-10 justify-center", children: ["ALL", ...presentCategories].map((c) => {
6033
- const active = activeCategory === c;
6034
- return /* @__PURE__ */ jsxRuntime.jsx(
6035
- "button",
6036
- {
6037
- type: "button",
6038
- onClick: () => setActiveCategory(c),
6039
- className: `px-4 py-2 rounded-full text-sm font-medium transition-colors ${active ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground hover:text-foreground"}`,
6040
- children: c === "ALL" ? "All" : CATEGORY_LABELS[c] ?? c
6041
- },
6042
- c
6043
- );
6044
- }) }),
6045
- grouped.map((group) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-12", children: [
6046
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-xl font-semibold text-foreground mb-5", children: CATEGORY_LABELS[group.cat] ?? group.cat }),
6047
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid sm:grid-cols-2 lg:grid-cols-3 gap-6", children: group.items.map((a) => /* @__PURE__ */ jsxRuntime.jsx(AssetCard, { asset: a, onPreview: setPreview }, a.id)) })
6048
- ] }, group.cat)),
6049
- contactEmail && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-8 border-t pt-10 text-center", children: [
6050
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-semibold text-foreground mb-2", children: "Media Contact" }),
6051
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-muted-foreground", children: [
6052
- "For interviews, partnerships, and press inquiries:",
6053
- " ",
6054
- /* @__PURE__ */ jsxRuntime.jsx(
6055
- "a",
6056
- {
6057
- href: `mailto:${contactEmail}`,
6058
- className: "text-primary hover:underline",
6059
- children: contactEmail
6060
- }
6061
- )
6062
- ] })
6063
- ] })
6064
- ] }) }) }),
6065
- /* @__PURE__ */ jsxRuntime.jsx(PressKitLightbox, { asset: preview, onClose: () => setPreview(null) })
6066
- ] });
2561
+ };
2562
+ }, [siteId, scriptUrl, enabled]);
2563
+ return null;
6067
2564
  }
6068
- var CONTRACTS_QUERY = `
6069
- query PublicContracts($productId: String!, $limit: Int, $offset: Int) {
6070
- publicPressKitAssets(productId: $productId, category: CONTRACT, limit: $limit, offset: $offset) {
2565
+ var PUBLIC_CONTENT_PAGE_QUERY = `
2566
+ query PublicContentPage($slug: String!, $productId: String) {
2567
+ publicContentPage(slug: $slug, productId: $productId) {
2568
+ id
2569
+ title
2570
+ slug
2571
+ content
2572
+ contentFormat
2573
+ seoTitle
2574
+ seoDescription
2575
+ seoKeywords
2576
+ lastUpdated
2577
+ version
2578
+ }
2579
+ }
2580
+ `;
2581
+ var PUBLIC_RESOLVED_CONTENT_PAGE_QUERY = `
2582
+ query PublicResolvedContentPage($slug: String!, $productId: String!) {
2583
+ publicResolvedContentPage(slug: $slug, productId: $productId) {
2584
+ id
2585
+ title
2586
+ slug
2587
+ content
2588
+ contentFormat
2589
+ seoTitle
2590
+ seoDescription
2591
+ seoKeywords
2592
+ lastUpdated
2593
+ version
2594
+ }
2595
+ }
2596
+ `;
2597
+ var PUBLIC_CONTENT_PAGES_QUERY = `
2598
+ query PublicContentPages($productId: String!, $limit: Int, $offset: Int) {
2599
+ publicContentPages(productId: $productId, limit: $limit, offset: $offset) {
6071
2600
  items {
6072
2601
  id
6073
2602
  title
6074
2603
  slug
6075
- description
6076
- fileUrl
6077
- fileName
6078
- fileSize
6079
- mimeType
6080
- version
6081
- tags
6082
- sortOrder
6083
- updatedAt
2604
+ lastUpdated
6084
2605
  }
2606
+ total
6085
2607
  hasMore
6086
2608
  }
6087
2609
  }
6088
2610
  `;
6089
- function ContractsPage(props) {
6090
- const {
6091
- productName,
6092
- heroTitle = "Contracts & Legal Agreements",
6093
- heroSubtitle = "Download our standard legal agreements and policies.",
6094
- className = "",
6095
- seoTitle,
6096
- seoDescription,
6097
- seoKeywords
6098
- } = props;
2611
+ function useContentPage(slug, options) {
6099
2612
  const client = useWebSDK();
6100
2613
  const config = useWebSDKConfig();
6101
- const { product } = useProduct();
6102
- const resolvedProductId = product?.id || config.productId;
6103
- const displayName = productName ?? product?.name ?? config.productId;
6104
- const [contracts, setContracts] = React.useState([]);
2614
+ const [data, setData] = React.useState(null);
6105
2615
  const [loading, setLoading] = React.useState(true);
6106
2616
  const [error, setError] = React.useState(null);
6107
- const fetchContracts = React.useCallback(async () => {
6108
- if (!resolvedProductId) {
2617
+ const fetchPage = React.useCallback(async () => {
2618
+ setLoading(true);
2619
+ setError(null);
2620
+ try {
2621
+ const query = options?.resolve ? PUBLIC_RESOLVED_CONTENT_PAGE_QUERY : PUBLIC_CONTENT_PAGE_QUERY;
2622
+ const queryName = options?.resolve ? "publicResolvedContentPage" : "publicContentPage";
2623
+ const result = await client.query(
2624
+ query,
2625
+ {
2626
+ slug,
2627
+ productId: config.productId
2628
+ }
2629
+ );
2630
+ if (result.errors?.length) {
2631
+ setError(result.errors[0]?.message ?? "Unknown error");
2632
+ setData(null);
2633
+ } else {
2634
+ setData(result.data?.[queryName] ?? null);
2635
+ }
2636
+ } catch (err) {
2637
+ setError(err instanceof Error ? err.message : "Failed to fetch content");
2638
+ setData(null);
2639
+ } finally {
6109
2640
  setLoading(false);
6110
- return;
6111
2641
  }
2642
+ }, [client, config.productId, slug, options?.resolve]);
2643
+ React.useEffect(() => {
2644
+ fetchPage();
2645
+ }, [fetchPage]);
2646
+ return { data, loading, error, refetch: fetchPage };
2647
+ }
2648
+ function useContentPages(options) {
2649
+ const client = useWebSDK();
2650
+ const config = useWebSDKConfig();
2651
+ const [data, setData] = React.useState(null);
2652
+ const [loading, setLoading] = React.useState(true);
2653
+ const [error, setError] = React.useState(null);
2654
+ const fetchPages = React.useCallback(async () => {
6112
2655
  setLoading(true);
2656
+ setError(null);
6113
2657
  try {
6114
- const PAGE_SIZE = 100;
6115
- const MAX_PAGES = 10;
6116
- const all = [];
6117
- let offset = 0;
6118
- for (let pageNum = 0; pageNum < MAX_PAGES; pageNum++) {
6119
- const result = await client.query(CONTRACTS_QUERY, {
6120
- productId: resolvedProductId,
6121
- limit: PAGE_SIZE,
6122
- offset
6123
- });
6124
- if (result.errors?.length) {
6125
- setError(result.errors[0]?.message ?? "Failed to load contracts");
6126
- return;
6127
- }
6128
- const page = result.data?.publicPressKitAssets;
6129
- const items = page?.items || [];
6130
- all.push(...items);
6131
- if (!page?.hasMore || items.length === 0) break;
6132
- offset += PAGE_SIZE;
2658
+ const result = await client.query(PUBLIC_CONTENT_PAGES_QUERY, {
2659
+ productId: config.productId,
2660
+ limit: options?.limit ?? 50,
2661
+ offset: options?.offset ?? 0
2662
+ });
2663
+ if (result.errors?.length) {
2664
+ setError(result.errors[0]?.message ?? "Unknown error");
2665
+ setData(null);
2666
+ } else {
2667
+ setData(result.data?.publicContentPages ?? null);
6133
2668
  }
6134
- all.sort(
6135
- (a, b) => a.sortOrder - b.sortOrder || a.title.localeCompare(b.title)
2669
+ } catch (err) {
2670
+ setError(
2671
+ err instanceof Error ? err.message : "Failed to fetch content pages"
6136
2672
  );
6137
- setContracts(all);
6138
- setError(null);
6139
- } catch {
6140
- setError("Failed to load contracts");
2673
+ setData(null);
6141
2674
  } finally {
6142
2675
  setLoading(false);
6143
2676
  }
6144
- }, [client, resolvedProductId]);
2677
+ }, [client, config.productId, options?.limit, options?.offset]);
6145
2678
  React.useEffect(() => {
6146
- fetchContracts();
6147
- }, [fetchContracts]);
6148
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `min-h-screen bg-background ${className}`, children: [
6149
- /* @__PURE__ */ jsxRuntime.jsx(
6150
- PageHead,
2679
+ fetchPages();
2680
+ }, [fetchPages]);
2681
+ return { data, loading, error, refetch: fetchPages };
2682
+ }
2683
+ function ContentRenderer({
2684
+ content,
2685
+ format = "markdown",
2686
+ className
2687
+ }) {
2688
+ if (format === "html" || format === "markdown") {
2689
+ return /* @__PURE__ */ jsxRuntime.jsx(
2690
+ "div",
6151
2691
  {
6152
- title: seoTitle || `Contracts \u2014 ${displayName}`,
6153
- description: seoDescription || heroSubtitle,
6154
- productName,
6155
- keywords: seoKeywords
2692
+ className,
2693
+ dangerouslySetInnerHTML: { __html: content }
6156
2694
  }
6157
- ),
6158
- /* @__PURE__ */ jsxRuntime.jsx("section", { className: "border-b border-border bg-muted/30", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 py-16 text-center", children: [
6159
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-4xl md:text-5xl font-bold text-foreground mb-4", children: heroTitle }),
6160
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-lg text-muted-foreground max-w-2xl mx-auto", children: heroSubtitle })
6161
- ] }) }),
6162
- /* @__PURE__ */ jsxRuntime.jsx("section", { className: "max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 py-12", children: loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center py-20", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-8 w-8 animate-spin rounded-full border-2 border-primary border-t-transparent" }) }) : error ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center py-16", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: error }) }) : contracts.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center py-16", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-base text-muted-foreground", children: "No contracts are available yet." }) }) : /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "divide-y divide-border rounded-xl border border-border overflow-hidden bg-card", children: contracts.map((c) => {
6163
- const ext = fileExt(c);
6164
- const size = formatBytes(c.fileSize);
6165
- return /* @__PURE__ */ jsxRuntime.jsxs(
6166
- "li",
6167
- {
6168
- className: "flex items-center justify-between gap-4 p-5 hover:bg-muted/40 transition-colors",
6169
- children: [
6170
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
6171
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [
6172
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-base font-semibold text-foreground truncate", children: c.title }),
6173
- c.version && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-primary bg-primary/10 px-2 py-0.5 rounded-full", children: c.version })
6174
- ] }),
6175
- c.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground mt-1 line-clamp-2", children: c.description }),
6176
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1 flex items-center gap-2 text-xs text-muted-foreground", children: [
6177
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: ext }),
6178
- size && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
6179
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
6180
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: size })
6181
- ] })
6182
- ] })
6183
- ] }),
6184
- /* @__PURE__ */ jsxRuntime.jsxs(
6185
- "a",
6186
- {
6187
- href: c.fileUrl,
6188
- target: "_blank",
6189
- rel: "noopener noreferrer",
6190
- download: true,
6191
- "aria-label": `Download ${c.title}`,
6192
- onClick: (e) => {
6193
- void downloadFile(e, c);
6194
- },
6195
- className: "flex-shrink-0 inline-flex items-center gap-1.5 text-sm font-medium text-primary-foreground bg-primary hover:opacity-90 px-4 py-2 rounded-lg transition-opacity",
6196
- children: [
6197
- /* @__PURE__ */ jsxRuntime.jsx(
6198
- "svg",
6199
- {
6200
- className: "w-4 h-4",
6201
- fill: "none",
6202
- viewBox: "0 0 24 24",
6203
- stroke: "currentColor",
6204
- "aria-hidden": "true",
6205
- children: /* @__PURE__ */ jsxRuntime.jsx(
6206
- "path",
6207
- {
6208
- strokeLinecap: "round",
6209
- strokeLinejoin: "round",
6210
- strokeWidth: 2,
6211
- d: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
6212
- }
6213
- )
6214
- }
6215
- ),
6216
- "Download"
6217
- ]
6218
- }
6219
- )
6220
- ]
6221
- },
6222
- c.id
6223
- );
6224
- }) }) })
6225
- ] });
2695
+ );
2696
+ }
2697
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className, children: /* @__PURE__ */ jsxRuntime.jsx("pre", { style: { whiteSpace: "pre-wrap", fontFamily: "inherit" }, children: content }) });
6226
2698
  }
6227
2699
 
6228
- exports.BlogListPage = BlogListPage;
6229
- exports.BlogPostPage = BlogPostPage;
6230
2700
  exports.Button = Button;
6231
- exports.CareersApplyForm = CareersApplyForm;
6232
- exports.CaseStudiesListPage = CaseStudiesListPage;
6233
- exports.CaseStudyPage = CaseStudyPage;
6234
- exports.ChangelogPage = ChangelogPage;
6235
2701
  exports.ContactPage = ContactPage;
6236
2702
  exports.ContentRenderer = ContentRenderer;
6237
- exports.ContractsPage = ContractsPage;
6238
- exports.DEFAULT_CONTACT_CATEGORIES = DEFAULT_CONTACT_CATEGORIES;
6239
- exports.ElectronDownloadLink = ElectronDownloadLink;
6240
2703
  exports.Input = Input;
6241
2704
  exports.Label = Label;
6242
- exports.LaunchCountdown = LaunchCountdown;
6243
2705
  exports.NewsletterForm = NewsletterForm;
6244
2706
  exports.NewsletterPage = NewsletterPage;
6245
2707
  exports.PageHead = PageHead;
6246
- exports.PartnersPage = PartnersPage;
6247
- exports.PressKitPage = PressKitPage;
6248
2708
  exports.PricingPage = PricingPage;
6249
2709
  exports.PricingSection = PricingSection;
6250
- exports.ProblemsSolvedSection = ProblemsSolvedSection;
6251
2710
  exports.ProductProvider = ProductProvider;
6252
- exports.ResourceLinks = ResourceLinks;
6253
2711
  exports.RybbitAnalytics = RybbitAnalytics;
6254
- exports.Select = Select;
6255
- exports.SocialLinks = SocialLinks;
6256
2712
  exports.Textarea = Textarea;
6257
2713
  exports.UnsubscribePage = UnsubscribePage;
6258
2714
  exports.WaitlistForm = WaitlistForm;
6259
2715
  exports.WaitlistPage = WaitlistPage;
6260
2716
  exports.WebSDKClient = WebSDKClient;
6261
2717
  exports.WebSDKProvider = WebSDKProvider;
6262
- exports.WebsiteSwitcher = WebsiteSwitcher;
6263
2718
  exports.buttonVariants = buttonVariants;
6264
2719
  exports.cn = cn;
6265
- exports.detectElectronPlatform = detectElectronPlatform;
6266
2720
  exports.useContentPage = useContentPage;
6267
2721
  exports.useContentPages = useContentPages;
6268
2722
  exports.useProduct = useProduct;