@burdenoff/website-sdk 0.0.0-dev

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 (119) hide show
  1. package/LICENSE +24 -0
  2. package/README.md +357 -0
  3. package/dist/analytics/index.d.mts +27 -0
  4. package/dist/analytics/index.d.ts +27 -0
  5. package/dist/analytics/index.js +36 -0
  6. package/dist/analytics/index.js.map +1 -0
  7. package/dist/analytics/index.mjs +34 -0
  8. package/dist/analytics/index.mjs.map +1 -0
  9. package/dist/client/index.d.mts +29 -0
  10. package/dist/client/index.d.ts +29 -0
  11. package/dist/client/index.js +261 -0
  12. package/dist/client/index.js.map +1 -0
  13. package/dist/client/index.mjs +255 -0
  14. package/dist/client/index.mjs.map +1 -0
  15. package/dist/components/contact.d.mts +144 -0
  16. package/dist/components/contact.d.ts +144 -0
  17. package/dist/components/contact.js +806 -0
  18. package/dist/components/contact.js.map +1 -0
  19. package/dist/components/contact.mjs +780 -0
  20. package/dist/components/contact.mjs.map +1 -0
  21. package/dist/components/electron-download.d.mts +32 -0
  22. package/dist/components/electron-download.d.ts +32 -0
  23. package/dist/components/electron-download.js +131 -0
  24. package/dist/components/electron-download.js.map +1 -0
  25. package/dist/components/electron-download.mjs +108 -0
  26. package/dist/components/electron-download.mjs.map +1 -0
  27. package/dist/components/launch-countdown.d.mts +15 -0
  28. package/dist/components/launch-countdown.d.ts +15 -0
  29. package/dist/components/launch-countdown.js +89 -0
  30. package/dist/components/launch-countdown.js.map +1 -0
  31. package/dist/components/launch-countdown.mjs +87 -0
  32. package/dist/components/launch-countdown.mjs.map +1 -0
  33. package/dist/components/newsletter.d.mts +155 -0
  34. package/dist/components/newsletter.d.ts +155 -0
  35. package/dist/components/newsletter.js +880 -0
  36. package/dist/components/newsletter.js.map +1 -0
  37. package/dist/components/newsletter.mjs +854 -0
  38. package/dist/components/newsletter.mjs.map +1 -0
  39. package/dist/components/partners.d.mts +88 -0
  40. package/dist/components/partners.d.ts +88 -0
  41. package/dist/components/partners.js +1169 -0
  42. package/dist/components/partners.js.map +1 -0
  43. package/dist/components/partners.mjs +1143 -0
  44. package/dist/components/partners.mjs.map +1 -0
  45. package/dist/components/pricing.d.mts +194 -0
  46. package/dist/components/pricing.d.ts +194 -0
  47. package/dist/components/pricing.js +813 -0
  48. package/dist/components/pricing.js.map +1 -0
  49. package/dist/components/pricing.mjs +791 -0
  50. package/dist/components/pricing.mjs.map +1 -0
  51. package/dist/components/problems-solved.d.mts +37 -0
  52. package/dist/components/problems-solved.d.ts +37 -0
  53. package/dist/components/problems-solved.js +157 -0
  54. package/dist/components/problems-solved.js.map +1 -0
  55. package/dist/components/problems-solved.mjs +135 -0
  56. package/dist/components/problems-solved.mjs.map +1 -0
  57. package/dist/components/resource-links.d.mts +28 -0
  58. package/dist/components/resource-links.d.ts +28 -0
  59. package/dist/components/resource-links.js +229 -0
  60. package/dist/components/resource-links.js.map +1 -0
  61. package/dist/components/resource-links.mjs +227 -0
  62. package/dist/components/resource-links.mjs.map +1 -0
  63. package/dist/components/social-links.d.mts +37 -0
  64. package/dist/components/social-links.d.ts +37 -0
  65. package/dist/components/social-links.js +268 -0
  66. package/dist/components/social-links.js.map +1 -0
  67. package/dist/components/social-links.mjs +266 -0
  68. package/dist/components/social-links.mjs.map +1 -0
  69. package/dist/components/unsubscribe.d.mts +14 -0
  70. package/dist/components/unsubscribe.d.ts +14 -0
  71. package/dist/components/unsubscribe.js +538 -0
  72. package/dist/components/unsubscribe.js.map +1 -0
  73. package/dist/components/unsubscribe.mjs +517 -0
  74. package/dist/components/unsubscribe.mjs.map +1 -0
  75. package/dist/components/waitlist.d.mts +70 -0
  76. package/dist/components/waitlist.d.ts +70 -0
  77. package/dist/components/waitlist.js +864 -0
  78. package/dist/components/waitlist.js.map +1 -0
  79. package/dist/components/waitlist.mjs +841 -0
  80. package/dist/components/waitlist.mjs.map +1 -0
  81. package/dist/components/website-switcher.d.mts +27 -0
  82. package/dist/components/website-switcher.d.ts +27 -0
  83. package/dist/components/website-switcher.js +168 -0
  84. package/dist/components/website-switcher.js.map +1 -0
  85. package/dist/components/website-switcher.mjs +146 -0
  86. package/dist/components/website-switcher.mjs.map +1 -0
  87. package/dist/content/index.d.mts +247 -0
  88. package/dist/content/index.d.ts +247 -0
  89. package/dist/content/index.js +2458 -0
  90. package/dist/content/index.js.map +1 -0
  91. package/dist/content/index.mjs +2428 -0
  92. package/dist/content/index.mjs.map +1 -0
  93. package/dist/index.d.mts +72 -0
  94. package/dist/index.d.ts +72 -0
  95. package/dist/index.js +6273 -0
  96. package/dist/index.js.map +1 -0
  97. package/dist/index.mjs +6204 -0
  98. package/dist/index.mjs.map +1 -0
  99. package/dist/product/index.d.mts +94 -0
  100. package/dist/product/index.d.ts +94 -0
  101. package/dist/product/index.js +282 -0
  102. package/dist/product/index.js.map +1 -0
  103. package/dist/product/index.mjs +278 -0
  104. package/dist/product/index.mjs.map +1 -0
  105. package/dist/provider-D7Rij9UM.d.mts +86 -0
  106. package/dist/provider-D7Rij9UM.d.ts +86 -0
  107. package/dist/seo/index.d.mts +64 -0
  108. package/dist/seo/index.d.ts +64 -0
  109. package/dist/seo/index.js +61 -0
  110. package/dist/seo/index.js.map +1 -0
  111. package/dist/seo/index.mjs +59 -0
  112. package/dist/seo/index.mjs.map +1 -0
  113. package/dist/utils/index.d.mts +21 -0
  114. package/dist/utils/index.d.ts +21 -0
  115. package/dist/utils/index.js +12 -0
  116. package/dist/utils/index.js.map +1 -0
  117. package/dist/utils/index.mjs +10 -0
  118. package/dist/utils/index.mjs.map +1 -0
  119. package/package.json +168 -0
@@ -0,0 +1,2458 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+ var reactHelmetAsync = require('react-helmet-async');
6
+ var lucideReact = require('lucide-react');
7
+ var reactSlot = require('@radix-ui/react-slot');
8
+ var classVarianceAuthority = require('class-variance-authority');
9
+ var clsx = require('clsx');
10
+ var tailwindMerge = require('tailwind-merge');
11
+
12
+ function _interopNamespace(e) {
13
+ if (e && e.__esModule) return e;
14
+ var n = Object.create(null);
15
+ if (e) {
16
+ Object.keys(e).forEach(function (k) {
17
+ if (k !== 'default') {
18
+ var d = Object.getOwnPropertyDescriptor(e, k);
19
+ Object.defineProperty(n, k, d.get ? d : {
20
+ enumerable: true,
21
+ get: function () { return e[k]; }
22
+ });
23
+ }
24
+ });
25
+ }
26
+ n.default = e;
27
+ return Object.freeze(n);
28
+ }
29
+
30
+ var React__namespace = /*#__PURE__*/_interopNamespace(React);
31
+
32
+ var __defProp = Object.defineProperty;
33
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
34
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
35
+
36
+ // src/client/security.ts
37
+ var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1"]);
38
+ var RESERVED_HEADERS = /* @__PURE__ */ new Set([
39
+ "authorization",
40
+ "cookie",
41
+ "x-actor-id",
42
+ "x-actor-type",
43
+ "x-consent-granted-by",
44
+ "x-consent-scope",
45
+ "x-consent-granted-at",
46
+ "x-consent-expires-at"
47
+ ]);
48
+ function validateGatewayUrl(value) {
49
+ let url;
50
+ try {
51
+ url = new URL(value);
52
+ } catch {
53
+ throw new Error("Gateway URL must be a valid absolute URL");
54
+ }
55
+ if (url.protocol === "https:") {
56
+ return url.toString();
57
+ }
58
+ if (url.protocol === "http:" && LOOPBACK_HOSTS.has(url.hostname.toLowerCase())) {
59
+ return url.toString();
60
+ }
61
+ throw new Error("Gateway URL must use HTTPS unless it targets localhost");
62
+ }
63
+ function sanitizeHeaders(headers) {
64
+ if (!headers) return void 0;
65
+ return Object.fromEntries(
66
+ Object.entries(headers).filter(
67
+ ([key]) => !RESERVED_HEADERS.has(key.toLowerCase())
68
+ )
69
+ );
70
+ }
71
+
72
+ // src/client/graphql-client.ts
73
+ var _WebSDKClient = class _WebSDKClient {
74
+ constructor(config) {
75
+ __publicField(this, "config");
76
+ __publicField(this, "queryCache", /* @__PURE__ */ new Map());
77
+ __publicField(this, "inflight", /* @__PURE__ */ new Map());
78
+ this.config = {
79
+ ...config,
80
+ gatewayUrl: validateGatewayUrl(config.gatewayUrl),
81
+ headers: sanitizeHeaders(config.headers)
82
+ };
83
+ }
84
+ getConfig() {
85
+ return this.config;
86
+ }
87
+ /**
88
+ * Execute a GraphQL query (with deduplication and caching)
89
+ */
90
+ async query(query, variables) {
91
+ const cacheKey = JSON.stringify({ query, variables });
92
+ const cached = this.queryCache.get(cacheKey);
93
+ if (cached) {
94
+ if (Date.now() - cached.ts < _WebSDKClient.CACHE_TTL) {
95
+ return cached.data;
96
+ }
97
+ this.queryCache.delete(cacheKey);
98
+ }
99
+ const existing = this.inflight.get(cacheKey);
100
+ if (existing) {
101
+ return existing;
102
+ }
103
+ const promise = this.request(query, variables).then((result) => {
104
+ if (result.data && !result.errors?.length) {
105
+ if (this.queryCache.size >= _WebSDKClient.MAX_CACHE_ENTRIES) {
106
+ const oldestEntry = this.queryCache.keys().next().value;
107
+ if (oldestEntry) {
108
+ this.queryCache.delete(oldestEntry);
109
+ }
110
+ }
111
+ this.queryCache.set(cacheKey, { data: result, ts: Date.now() });
112
+ }
113
+ return result;
114
+ }).finally(() => {
115
+ this.inflight.delete(cacheKey);
116
+ });
117
+ this.inflight.set(cacheKey, promise);
118
+ return promise;
119
+ }
120
+ /**
121
+ * Execute a GraphQL mutation
122
+ */
123
+ async mutate(mutation, variables) {
124
+ return this.request(mutation, variables);
125
+ }
126
+ async request(query, variables) {
127
+ try {
128
+ const response = await fetch(this.config.gatewayUrl, {
129
+ method: "POST",
130
+ headers: {
131
+ "Content-Type": "application/json",
132
+ "x-product-id": this.config.productId,
133
+ ...this.config.headers
134
+ },
135
+ body: JSON.stringify({ query, variables })
136
+ });
137
+ if (!response.ok) {
138
+ console.error(
139
+ "[WebSDK] GraphQL request failed",
140
+ response.status,
141
+ response.statusText
142
+ );
143
+ return {
144
+ errors: [
145
+ {
146
+ message: `HTTP ${response.status}: ${response.statusText}`,
147
+ extensions: { code: `HTTP_${response.status}` }
148
+ }
149
+ ]
150
+ };
151
+ }
152
+ return await response.json();
153
+ } catch (error) {
154
+ console.error(
155
+ "[WebSDK] Network error",
156
+ error instanceof Error ? error.message : "unknown error"
157
+ );
158
+ return {
159
+ errors: [
160
+ {
161
+ message: error instanceof Error ? error.message : "Network error",
162
+ extensions: { code: "NETWORK_ERROR" }
163
+ }
164
+ ]
165
+ };
166
+ }
167
+ }
168
+ };
169
+ __publicField(_WebSDKClient, "CACHE_TTL", 5 * 60 * 1e3);
170
+ // 5 minutes
171
+ __publicField(_WebSDKClient, "MAX_CACHE_ENTRIES", 100);
172
+ var WebSDKContext = React.createContext(null);
173
+ function useWebSDK() {
174
+ const client = React.useContext(WebSDKContext);
175
+ if (!client) {
176
+ throw new Error("useWebSDK must be used within a WebSDKProvider");
177
+ }
178
+ return client;
179
+ }
180
+ function useWebSDKConfig() {
181
+ const client = useWebSDK();
182
+ return client.getConfig();
183
+ }
184
+ var ProductContext = React.createContext({
185
+ product: null,
186
+ loading: true,
187
+ error: null,
188
+ hasProvider: false,
189
+ refetch: () => {
190
+ }
191
+ });
192
+ function useProduct() {
193
+ return React.useContext(ProductContext);
194
+ }
195
+ var PUBLIC_CONTENT_PAGE_QUERY = `
196
+ query PublicContentPage($slug: String!, $productId: String) {
197
+ publicContentPage(slug: $slug, productId: $productId) {
198
+ id
199
+ title
200
+ slug
201
+ content
202
+ contentFormat
203
+ seoTitle
204
+ seoDescription
205
+ seoKeywords
206
+ lastUpdated
207
+ version
208
+ excerpt
209
+ thumbnail
210
+ author
211
+ publishedAt
212
+ readingTime
213
+ featured
214
+ category
215
+ }
216
+ }
217
+ `;
218
+ var PUBLIC_RESOLVED_CONTENT_PAGE_QUERY = `
219
+ query PublicResolvedContentPage($slug: String!, $productId: String!) {
220
+ publicResolvedContentPage(slug: $slug, productId: $productId) {
221
+ id
222
+ title
223
+ slug
224
+ content
225
+ contentFormat
226
+ seoTitle
227
+ seoDescription
228
+ seoKeywords
229
+ lastUpdated
230
+ version
231
+ excerpt
232
+ thumbnail
233
+ author
234
+ publishedAt
235
+ readingTime
236
+ featured
237
+ category
238
+ }
239
+ }
240
+ `;
241
+ var PUBLIC_CONTENT_PAGES_QUERY = `
242
+ query PublicContentPages($productId: String!, $limit: Int, $offset: Int) {
243
+ publicContentPages(productId: $productId, limit: $limit, offset: $offset) {
244
+ items {
245
+ id
246
+ title
247
+ slug
248
+ lastUpdated
249
+ tags
250
+ seoDescription
251
+ excerpt
252
+ thumbnail
253
+ author
254
+ publishedAt
255
+ readingTime
256
+ featured
257
+ category
258
+ }
259
+ total
260
+ hasMore
261
+ }
262
+ }
263
+ `;
264
+ function useContentPage(slug, options) {
265
+ const client = useWebSDK();
266
+ const config = useWebSDKConfig();
267
+ const { product } = useProduct();
268
+ const resolvedProductId = product?.id || config.productId;
269
+ const [data, setData] = React.useState(null);
270
+ const [loading, setLoading] = React.useState(true);
271
+ const [error, setError] = React.useState(null);
272
+ const fetchPage = React.useCallback(async () => {
273
+ if (!resolvedProductId) return;
274
+ setLoading(true);
275
+ setError(null);
276
+ try {
277
+ const query = options?.resolve ? PUBLIC_RESOLVED_CONTENT_PAGE_QUERY : PUBLIC_CONTENT_PAGE_QUERY;
278
+ const queryName = options?.resolve ? "publicResolvedContentPage" : "publicContentPage";
279
+ const result = await client.query(
280
+ query,
281
+ {
282
+ slug,
283
+ productId: resolvedProductId
284
+ }
285
+ );
286
+ if (result.errors?.length) {
287
+ setError(result.errors[0]?.message ?? "Unknown error");
288
+ setData(null);
289
+ } else {
290
+ setData(result.data?.[queryName] ?? null);
291
+ }
292
+ } catch (err) {
293
+ setError(err instanceof Error ? err.message : "Failed to fetch content");
294
+ setData(null);
295
+ } finally {
296
+ setLoading(false);
297
+ }
298
+ }, [client, resolvedProductId, slug, options?.resolve]);
299
+ React.useEffect(() => {
300
+ fetchPage();
301
+ }, [fetchPage]);
302
+ return { data, loading, error, refetch: fetchPage };
303
+ }
304
+ function useContentPages(options) {
305
+ const client = useWebSDK();
306
+ const config = useWebSDKConfig();
307
+ const [data, setData] = React.useState(null);
308
+ const [loading, setLoading] = React.useState(true);
309
+ const [error, setError] = React.useState(null);
310
+ const fetchPages = React.useCallback(async () => {
311
+ setLoading(true);
312
+ setError(null);
313
+ try {
314
+ const result = await client.query(PUBLIC_CONTENT_PAGES_QUERY, {
315
+ productId: config.productId,
316
+ limit: options?.limit ?? 50,
317
+ offset: options?.offset ?? 0
318
+ });
319
+ if (result.errors?.length) {
320
+ setError(result.errors[0]?.message ?? "Unknown error");
321
+ setData(null);
322
+ } else {
323
+ setData(result.data?.publicContentPages ?? null);
324
+ }
325
+ } catch (err) {
326
+ setError(
327
+ err instanceof Error ? err.message : "Failed to fetch content pages"
328
+ );
329
+ setData(null);
330
+ } finally {
331
+ setLoading(false);
332
+ }
333
+ }, [client, config.productId, options?.limit, options?.offset]);
334
+ React.useEffect(() => {
335
+ fetchPages();
336
+ }, [fetchPages]);
337
+ return { data, loading, error, refetch: fetchPages };
338
+ }
339
+ function renderMarkdown(md) {
340
+ let html = md;
341
+ html = html.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
342
+ html = html.replace(/```(\w*)\n([\s\S]*?)```/g, (_m, _lang, code) => {
343
+ return `<pre><code>${code.trim()}</code></pre>`;
344
+ });
345
+ html = html.replace(/`([^`]+)`/g, "<code>$1</code>");
346
+ html = html.replace(/^###### (.+)$/gm, "<h6>$1</h6>");
347
+ html = html.replace(/^##### (.+)$/gm, "<h5>$1</h5>");
348
+ html = html.replace(/^#### (.+)$/gm, "<h4>$1</h4>");
349
+ html = html.replace(/^### (.+)$/gm, "<h3>$1</h3>");
350
+ html = html.replace(/^## (.+)$/gm, "<h2>$1</h2>");
351
+ html = html.replace(/^# (.+)$/gm, "<h1>$1</h1>");
352
+ html = html.replace(/\*\*\*(.+?)\*\*\*/g, "<strong><em>$1</em></strong>");
353
+ html = html.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>");
354
+ html = html.replace(/\*(.+?)\*/g, "<em>$1</em>");
355
+ html = html.replace(
356
+ /\[([^\]]+)\]\(([^)]+)\)/g,
357
+ '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>'
358
+ );
359
+ html = html.replace(/^&gt; (.+)$/gm, "<blockquote>$1</blockquote>");
360
+ html = html.replace(/(^- .+$(\n^- .+$)*)/gm, (block) => {
361
+ const items = block.split("\n").map((line) => `<li>${line.replace(/^- /, "")}</li>`).join("");
362
+ return `<ul>${items}</ul>`;
363
+ });
364
+ html = html.replace(/(^\d+\. .+$(\n^\d+\. .+$)*)/gm, (block) => {
365
+ const items = block.split("\n").map((line) => `<li>${line.replace(/^\d+\. /, "")}</li>`).join("");
366
+ return `<ol>${items}</ol>`;
367
+ });
368
+ html = html.replace(/^---$/gm, "<hr />");
369
+ html = html.split(/\n\n+/).map((block) => {
370
+ const trimmed = block.trim();
371
+ if (!trimmed) return "";
372
+ if (/^<(h[1-6]|ul|ol|pre|blockquote|hr)/.test(trimmed)) return trimmed;
373
+ return `<p>${trimmed.replace(/\n/g, "<br />")}</p>`;
374
+ }).join("\n");
375
+ return html;
376
+ }
377
+ function ContentRenderer({
378
+ content,
379
+ format = "markdown",
380
+ className
381
+ }) {
382
+ if (format === "markdown") {
383
+ return /* @__PURE__ */ jsxRuntime.jsx(
384
+ "div",
385
+ {
386
+ className,
387
+ dangerouslySetInnerHTML: { __html: renderMarkdown(content) }
388
+ }
389
+ );
390
+ }
391
+ if (format === "html") {
392
+ return /* @__PURE__ */ jsxRuntime.jsx(
393
+ "div",
394
+ {
395
+ className,
396
+ dangerouslySetInnerHTML: { __html: content }
397
+ }
398
+ );
399
+ }
400
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className, children: /* @__PURE__ */ jsxRuntime.jsx("pre", { style: { whiteSpace: "pre-wrap", fontFamily: "inherit" }, children: content }) });
401
+ }
402
+ function PageHead({
403
+ title,
404
+ description,
405
+ productName,
406
+ keywords,
407
+ image,
408
+ url,
409
+ favicon,
410
+ robots = "index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1",
411
+ locale = "en_US",
412
+ twitterHandle,
413
+ themeColor = "#000000",
414
+ structuredData,
415
+ additionalMeta
416
+ }) {
417
+ const fullTitle = productName && !title.includes(productName) ? `${title} | ${productName}` : title;
418
+ const fullImageUrl = image && !image.startsWith("http") && url ? `${url.replace(/\/$/, "")}${image}` : image;
419
+ const structuredDataItems = structuredData ? Array.isArray(structuredData) ? structuredData : [structuredData] : [];
420
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactHelmetAsync.Helmet, { children: [
421
+ /* @__PURE__ */ jsxRuntime.jsx("title", { children: fullTitle }),
422
+ /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "title", content: fullTitle }),
423
+ description && /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "description", content: description }),
424
+ keywords && /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "keywords", content: keywords }),
425
+ /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "author", content: "Burdenoff Consultancy Services Pvt Ltd" }),
426
+ /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "robots", content: robots }),
427
+ url && /* @__PURE__ */ jsxRuntime.jsx("link", { rel: "canonical", href: url }),
428
+ favicon && /* @__PURE__ */ jsxRuntime.jsx("link", { rel: "icon", href: favicon }),
429
+ /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:type", content: "website" }),
430
+ /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:title", content: fullTitle }),
431
+ description && /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:description", content: description }),
432
+ fullImageUrl && /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:image", content: fullImageUrl }),
433
+ url && /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:url", content: url }),
434
+ /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:locale", content: locale }),
435
+ productName && /* @__PURE__ */ jsxRuntime.jsx("meta", { property: "og:site_name", content: productName }),
436
+ /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "twitter:card", content: "summary_large_image" }),
437
+ /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "twitter:title", content: fullTitle }),
438
+ description && /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "twitter:description", content: description }),
439
+ fullImageUrl && /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "twitter:image", content: fullImageUrl }),
440
+ twitterHandle && /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "twitter:creator", content: twitterHandle }),
441
+ /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "theme-color", content: themeColor }),
442
+ structuredDataItems.map((data, index) => /* @__PURE__ */ jsxRuntime.jsx("script", { type: "application/ld+json", children: JSON.stringify(data) }, `ld-${index}`)),
443
+ additionalMeta?.map((meta, index) => /* @__PURE__ */ jsxRuntime.jsx(
444
+ "meta",
445
+ {
446
+ ...meta.name ? { name: meta.name } : {},
447
+ ...meta.property ? { property: meta.property } : {},
448
+ content: meta.content
449
+ },
450
+ `meta-${index}`
451
+ ))
452
+ ] });
453
+ }
454
+ var BLOG_POSTS_QUERY = `
455
+ query BlogPosts($productId: String!, $limit: Int, $offset: Int) {
456
+ publicContentPages(productId: $productId, limit: $limit, offset: $offset) {
457
+ items {
458
+ id
459
+ title
460
+ slug
461
+ lastUpdated
462
+ tags
463
+ seoDescription
464
+ excerpt
465
+ thumbnail
466
+ author
467
+ publishedAt
468
+ readingTime
469
+ featured
470
+ category
471
+ }
472
+ total
473
+ hasMore
474
+ }
475
+ }
476
+ `;
477
+ var BLOG_POST_QUERY = `
478
+ query BlogPost($slug: String!, $productId: String) {
479
+ publicContentPage(slug: $slug, productId: $productId) {
480
+ id
481
+ title
482
+ slug
483
+ content
484
+ contentFormat
485
+ seoTitle
486
+ seoDescription
487
+ seoKeywords
488
+ lastUpdated
489
+ tags
490
+ excerpt
491
+ thumbnail
492
+ author
493
+ publishedAt
494
+ readingTime
495
+ featured
496
+ category
497
+ }
498
+ }
499
+ `;
500
+ function BlogCard({
501
+ post,
502
+ basePath = "/blog"
503
+ }) {
504
+ const date = post.publishedAt || post.lastUpdated;
505
+ const formattedDate = date ? new Date(date).toLocaleDateString("en-US", {
506
+ year: "numeric",
507
+ month: "long",
508
+ day: "numeric"
509
+ }) : "";
510
+ const [imgError, setImgError] = React.useState(false);
511
+ return /* @__PURE__ */ jsxRuntime.jsxs(
512
+ "a",
513
+ {
514
+ href: `${basePath}/${post.slug}`,
515
+ className: "group block bg-card border rounded-xl overflow-hidden hover:shadow-lg hover:border-primary/30 transition-all duration-300",
516
+ children: [
517
+ post.thumbnail && !imgError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
518
+ "img",
519
+ {
520
+ src: post.thumbnail,
521
+ alt: post.title,
522
+ className: "w-full h-full object-cover group-hover:scale-105 transition-transform duration-300",
523
+ loading: "lazy",
524
+ onError: () => setImgError(true)
525
+ }
526
+ ) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] bg-muted/50 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(
527
+ "svg",
528
+ {
529
+ className: "w-12 h-12 text-muted-foreground/20",
530
+ fill: "none",
531
+ viewBox: "0 0 24 24",
532
+ stroke: "currentColor",
533
+ children: /* @__PURE__ */ jsxRuntime.jsx(
534
+ "path",
535
+ {
536
+ strokeLinecap: "round",
537
+ strokeLinejoin: "round",
538
+ strokeWidth: 1.5,
539
+ 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"
540
+ }
541
+ )
542
+ }
543
+ ) }),
544
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-5 sm:p-6", children: [
545
+ 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 }),
546
+ /* @__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 }),
547
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground mb-4 line-clamp-3", children: post.excerpt || post.seoDescription || "" }),
548
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 text-xs text-muted-foreground", children: [
549
+ post.author && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: post.author }),
550
+ post.author && formattedDate && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
551
+ formattedDate && /* @__PURE__ */ jsxRuntime.jsx("span", { children: formattedDate }),
552
+ post.readingTime && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
553
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
554
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
555
+ post.readingTime,
556
+ " min read"
557
+ ] })
558
+ ] })
559
+ ] })
560
+ ] })
561
+ ]
562
+ }
563
+ );
564
+ }
565
+ function BlogListPage(props) {
566
+ const {
567
+ productName,
568
+ heroTitle = "Blog",
569
+ heroSubtitle = "Latest updates, engineering insights, and product news",
570
+ blogPostBasePath = "/blog",
571
+ className = "",
572
+ seoTitle,
573
+ seoDescription,
574
+ seoKeywords
575
+ } = props;
576
+ const client = useWebSDK();
577
+ const config = useWebSDKConfig();
578
+ const { product } = useProduct();
579
+ const resolvedProductId = product?.id || config.productId;
580
+ const [posts, setPosts] = React.useState([]);
581
+ const [total, setTotal] = React.useState(0);
582
+ const [loading, setLoading] = React.useState(true);
583
+ const [error, setError] = React.useState(null);
584
+ const [offset, setOffset] = React.useState(0);
585
+ const limit = 12;
586
+ const fetchPosts = React.useCallback(async () => {
587
+ if (!resolvedProductId) return;
588
+ setLoading(true);
589
+ try {
590
+ const result = await client.query(BLOG_POSTS_QUERY, {
591
+ productId: resolvedProductId,
592
+ limit,
593
+ offset
594
+ });
595
+ if (result.errors?.length) {
596
+ setError(result.errors[0]?.message ?? "Failed to load posts");
597
+ return;
598
+ }
599
+ const blogPosts = (result.data?.publicContentPages?.items || []).filter(
600
+ (p) => p.tags?.includes("blog")
601
+ );
602
+ setPosts(blogPosts);
603
+ setTotal(result.data?.publicContentPages?.total || 0);
604
+ setError(null);
605
+ } catch {
606
+ setError("Failed to load blog posts");
607
+ } finally {
608
+ setLoading(false);
609
+ }
610
+ }, [client, resolvedProductId, offset]);
611
+ React.useEffect(() => {
612
+ fetchPosts();
613
+ }, [fetchPosts]);
614
+ const featuredPosts = posts.filter((p) => p.featured);
615
+ const regularPosts = posts.filter((p) => !p.featured);
616
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `w-full ${className}`, children: [
617
+ seoTitle && /* @__PURE__ */ jsxRuntime.jsx(
618
+ PageHead,
619
+ {
620
+ title: seoTitle,
621
+ description: seoDescription || heroSubtitle,
622
+ productName,
623
+ keywords: seoKeywords
624
+ }
625
+ ),
626
+ /* @__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: [
627
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-3xl sm:text-4xl md:text-5xl font-bold tracking-tight text-foreground", children: heroTitle }),
628
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-4 text-lg text-muted-foreground", children: heroSubtitle })
629
+ ] }) }) }),
630
+ /* @__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(
631
+ "div",
632
+ {
633
+ className: "bg-card border rounded-xl overflow-hidden animate-pulse",
634
+ children: [
635
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] bg-muted" }),
636
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6 space-y-3", children: [
637
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-1/4" }),
638
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-6 bg-muted rounded w-3/4" }),
639
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded" }),
640
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-1/2" })
641
+ ] })
642
+ ]
643
+ },
644
+ i
645
+ )) }) : 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: [
646
+ 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(
647
+ BlogCard,
648
+ {
649
+ post,
650
+ basePath: blogPostBasePath
651
+ },
652
+ post.id
653
+ )) }),
654
+ /* @__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(
655
+ BlogCard,
656
+ {
657
+ post,
658
+ basePath: blogPostBasePath
659
+ },
660
+ post.id
661
+ )) }),
662
+ total > limit && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-center gap-4 mt-12", children: [
663
+ /* @__PURE__ */ jsxRuntime.jsx(
664
+ "button",
665
+ {
666
+ onClick: () => setOffset(Math.max(0, offset - limit)),
667
+ disabled: offset === 0,
668
+ className: "px-5 py-2.5 text-sm font-medium border rounded-lg disabled:opacity-40 hover:bg-muted transition-colors",
669
+ children: "Previous"
670
+ }
671
+ ),
672
+ /* @__PURE__ */ jsxRuntime.jsx(
673
+ "button",
674
+ {
675
+ onClick: () => setOffset(offset + limit),
676
+ disabled: offset + limit >= total,
677
+ className: "px-5 py-2.5 text-sm font-medium border rounded-lg disabled:opacity-40 hover:bg-muted transition-colors",
678
+ children: "Next"
679
+ }
680
+ )
681
+ ] })
682
+ ] }) }) })
683
+ ] });
684
+ }
685
+ function BlogPostPage(props) {
686
+ const {
687
+ productName,
688
+ blogListUrl = "/blog",
689
+ backLabel = "Back to Blog",
690
+ className = ""
691
+ } = props;
692
+ const client = useWebSDK();
693
+ const config = useWebSDKConfig();
694
+ const { product } = useProduct();
695
+ const resolvedProductId = product?.id || config.productId;
696
+ const [post, setPost] = React.useState(null);
697
+ const [loading, setLoading] = React.useState(true);
698
+ const [error, setError] = React.useState(null);
699
+ const slug = typeof window !== "undefined" ? window.location.pathname.split("/").pop() || "" : "";
700
+ React.useEffect(() => {
701
+ if (!slug || !resolvedProductId) return;
702
+ async function fetchPost() {
703
+ setLoading(true);
704
+ try {
705
+ const result = await client.query(BLOG_POST_QUERY, {
706
+ slug,
707
+ productId: resolvedProductId
708
+ });
709
+ if (result.errors?.length) {
710
+ setError(result.errors[0]?.message ?? "Failed to load post");
711
+ return;
712
+ }
713
+ setPost(result.data?.publicContentPage ?? null);
714
+ setError(null);
715
+ } catch {
716
+ setError("Failed to load blog post");
717
+ } finally {
718
+ setLoading(false);
719
+ }
720
+ }
721
+ fetchPost();
722
+ }, [client, slug, resolvedProductId]);
723
+ if (loading) {
724
+ 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: [
725
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-8 bg-muted rounded w-2/3" }),
726
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-1/3" }),
727
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] bg-muted rounded-xl" }),
728
+ /* @__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)) })
729
+ ] }) }) });
730
+ }
731
+ if (error || !post) {
732
+ 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: [
733
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl font-bold mb-4", children: "Post not found" }),
734
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground mb-6", children: error || "This blog post doesn't exist or has been removed." }),
735
+ /* @__PURE__ */ jsxRuntime.jsxs(
736
+ "a",
737
+ {
738
+ href: blogListUrl,
739
+ className: "text-primary hover:underline font-medium",
740
+ children: [
741
+ "\u2190 ",
742
+ backLabel
743
+ ]
744
+ }
745
+ )
746
+ ] }) });
747
+ }
748
+ const date = post.publishedAt || post.lastUpdated;
749
+ const formattedDate = date ? new Date(date).toLocaleDateString("en-US", {
750
+ year: "numeric",
751
+ month: "long",
752
+ day: "numeric"
753
+ }) : "";
754
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `w-full ${className}`, children: [
755
+ /* @__PURE__ */ jsxRuntime.jsx(
756
+ PageHead,
757
+ {
758
+ title: post.seoTitle || post.title,
759
+ description: post.seoDescription || post.excerpt || "",
760
+ productName,
761
+ keywords: post.seoKeywords || ""
762
+ }
763
+ ),
764
+ /* @__PURE__ */ jsxRuntime.jsxs("article", { className: "max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-16 sm:py-20", children: [
765
+ /* @__PURE__ */ jsxRuntime.jsxs(
766
+ "a",
767
+ {
768
+ href: blogListUrl,
769
+ className: "inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground mb-8 transition-colors",
770
+ children: [
771
+ "\u2190 ",
772
+ backLabel
773
+ ]
774
+ }
775
+ ),
776
+ 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 }),
777
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-3xl sm:text-4xl md:text-5xl font-bold tracking-tight text-foreground mb-4", children: post.title }),
778
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 text-sm text-muted-foreground mb-8", children: [
779
+ post.author && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-foreground", children: post.author }),
780
+ post.author && formattedDate && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
781
+ formattedDate && /* @__PURE__ */ jsxRuntime.jsx("span", { children: formattedDate }),
782
+ post.readingTime && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
783
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
784
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
785
+ post.readingTime,
786
+ " min read"
787
+ ] })
788
+ ] })
789
+ ] }),
790
+ post.thumbnail && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] rounded-xl overflow-hidden mb-10", children: /* @__PURE__ */ jsxRuntime.jsx(
791
+ "img",
792
+ {
793
+ src: post.thumbnail,
794
+ alt: post.title,
795
+ className: "w-full h-full object-cover"
796
+ }
797
+ ) }),
798
+ /* @__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 }) })
799
+ ] })
800
+ ] });
801
+ }
802
+ var CASE_STUDY_TAG = "case-study";
803
+ var CASE_STUDIES_QUERY = `
804
+ query CaseStudies($productId: String!, $limit: Int, $offset: Int) {
805
+ publicContentPages(productId: $productId, limit: $limit, offset: $offset) {
806
+ items {
807
+ id
808
+ title
809
+ slug
810
+ lastUpdated
811
+ tags
812
+ seoDescription
813
+ excerpt
814
+ thumbnail
815
+ author
816
+ publishedAt
817
+ readingTime
818
+ featured
819
+ category
820
+ }
821
+ total
822
+ hasMore
823
+ }
824
+ }
825
+ `;
826
+ var CASE_STUDY_QUERY = `
827
+ query CaseStudy($slug: String!, $productId: String) {
828
+ publicContentPage(slug: $slug, productId: $productId) {
829
+ id
830
+ title
831
+ slug
832
+ content
833
+ contentFormat
834
+ seoTitle
835
+ seoDescription
836
+ seoKeywords
837
+ lastUpdated
838
+ tags
839
+ excerpt
840
+ thumbnail
841
+ author
842
+ publishedAt
843
+ readingTime
844
+ featured
845
+ category
846
+ }
847
+ }
848
+ `;
849
+ function CaseStudyCard({
850
+ post,
851
+ basePath = "/case-studies"
852
+ }) {
853
+ const date = post.publishedAt || post.lastUpdated;
854
+ const formattedDate = date ? new Date(date).toLocaleDateString("en-US", {
855
+ year: "numeric",
856
+ month: "long",
857
+ day: "numeric"
858
+ }) : "";
859
+ const [imgError, setImgError] = React.useState(false);
860
+ return /* @__PURE__ */ jsxRuntime.jsxs(
861
+ "a",
862
+ {
863
+ href: `${basePath}/${post.slug}`,
864
+ className: "group block bg-card border rounded-xl overflow-hidden hover:shadow-lg hover:border-primary/30 transition-all duration-300",
865
+ children: [
866
+ post.thumbnail && !imgError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
867
+ "img",
868
+ {
869
+ src: post.thumbnail,
870
+ alt: post.title,
871
+ className: "w-full h-full object-cover group-hover:scale-105 transition-transform duration-300",
872
+ loading: "lazy",
873
+ onError: () => setImgError(true)
874
+ }
875
+ ) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] bg-muted/50 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(
876
+ "svg",
877
+ {
878
+ className: "w-12 h-12 text-muted-foreground/20",
879
+ fill: "none",
880
+ viewBox: "0 0 24 24",
881
+ stroke: "currentColor",
882
+ children: /* @__PURE__ */ jsxRuntime.jsx(
883
+ "path",
884
+ {
885
+ strokeLinecap: "round",
886
+ strokeLinejoin: "round",
887
+ strokeWidth: 1.5,
888
+ 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"
889
+ }
890
+ )
891
+ }
892
+ ) }),
893
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-5 sm:p-6", children: [
894
+ 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 }),
895
+ /* @__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 }),
896
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground mb-4 line-clamp-3", children: post.excerpt || post.seoDescription || "" }),
897
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 text-xs text-muted-foreground", children: [
898
+ post.author && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: post.author }),
899
+ post.author && formattedDate && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
900
+ formattedDate && /* @__PURE__ */ jsxRuntime.jsx("span", { children: formattedDate }),
901
+ post.readingTime && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
902
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
903
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
904
+ post.readingTime,
905
+ " min read"
906
+ ] })
907
+ ] })
908
+ ] })
909
+ ] })
910
+ ]
911
+ }
912
+ );
913
+ }
914
+ function CaseStudiesListPage(props) {
915
+ const {
916
+ productName,
917
+ heroTitle = "Case Studies",
918
+ heroSubtitle = "Real-world stories of teams building with our platform",
919
+ caseStudyBasePath = "/case-studies",
920
+ className = "",
921
+ seoTitle,
922
+ seoDescription,
923
+ seoKeywords
924
+ } = props;
925
+ const client = useWebSDK();
926
+ const config = useWebSDKConfig();
927
+ const { product } = useProduct();
928
+ const resolvedProductId = product?.id || config.productId;
929
+ const [posts, setPosts] = React.useState([]);
930
+ const [total, setTotal] = React.useState(0);
931
+ const [loading, setLoading] = React.useState(true);
932
+ const [error, setError] = React.useState(null);
933
+ const [offset, setOffset] = React.useState(0);
934
+ const limit = 12;
935
+ const fetchPosts = React.useCallback(async () => {
936
+ if (!resolvedProductId) return;
937
+ setLoading(true);
938
+ try {
939
+ const result = await client.query(CASE_STUDIES_QUERY, {
940
+ productId: resolvedProductId,
941
+ limit,
942
+ offset
943
+ });
944
+ if (result.errors?.length) {
945
+ setError(result.errors[0]?.message ?? "Failed to load case studies");
946
+ return;
947
+ }
948
+ const caseStudies = (result.data?.publicContentPages?.items || []).filter(
949
+ (p) => p.tags?.includes(CASE_STUDY_TAG)
950
+ );
951
+ setPosts(caseStudies);
952
+ setTotal(result.data?.publicContentPages?.total || 0);
953
+ setError(null);
954
+ } catch {
955
+ setError("Failed to load case studies");
956
+ } finally {
957
+ setLoading(false);
958
+ }
959
+ }, [client, resolvedProductId, offset]);
960
+ React.useEffect(() => {
961
+ fetchPosts();
962
+ }, [fetchPosts]);
963
+ const featuredPosts = posts.filter((p) => p.featured);
964
+ const regularPosts = posts.filter((p) => !p.featured);
965
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `w-full ${className}`, children: [
966
+ seoTitle && /* @__PURE__ */ jsxRuntime.jsx(
967
+ PageHead,
968
+ {
969
+ title: seoTitle,
970
+ description: seoDescription || heroSubtitle,
971
+ productName,
972
+ keywords: seoKeywords
973
+ }
974
+ ),
975
+ /* @__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: [
976
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-3xl sm:text-4xl md:text-5xl font-bold tracking-tight text-foreground", children: heroTitle }),
977
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-4 text-lg text-muted-foreground", children: heroSubtitle })
978
+ ] }) }) }),
979
+ /* @__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(
980
+ "div",
981
+ {
982
+ className: "bg-card border rounded-xl overflow-hidden animate-pulse",
983
+ children: [
984
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] bg-muted" }),
985
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6 space-y-3", children: [
986
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-1/4" }),
987
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-6 bg-muted rounded w-3/4" }),
988
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded" }),
989
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-1/2" })
990
+ ] })
991
+ ]
992
+ },
993
+ i
994
+ )) }) : 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: [
995
+ 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(
996
+ CaseStudyCard,
997
+ {
998
+ post,
999
+ basePath: caseStudyBasePath
1000
+ },
1001
+ post.id
1002
+ )) }),
1003
+ /* @__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(
1004
+ CaseStudyCard,
1005
+ {
1006
+ post,
1007
+ basePath: caseStudyBasePath
1008
+ },
1009
+ post.id
1010
+ )) }),
1011
+ total > limit && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-center gap-4 mt-12", children: [
1012
+ /* @__PURE__ */ jsxRuntime.jsx(
1013
+ "button",
1014
+ {
1015
+ onClick: () => setOffset(Math.max(0, offset - limit)),
1016
+ disabled: offset === 0,
1017
+ className: "px-5 py-2.5 text-sm font-medium border rounded-lg disabled:opacity-40 hover:bg-muted transition-colors",
1018
+ children: "Previous"
1019
+ }
1020
+ ),
1021
+ /* @__PURE__ */ jsxRuntime.jsx(
1022
+ "button",
1023
+ {
1024
+ onClick: () => setOffset(offset + limit),
1025
+ disabled: offset + limit >= total,
1026
+ className: "px-5 py-2.5 text-sm font-medium border rounded-lg disabled:opacity-40 hover:bg-muted transition-colors",
1027
+ children: "Next"
1028
+ }
1029
+ )
1030
+ ] })
1031
+ ] }) }) })
1032
+ ] });
1033
+ }
1034
+ function CaseStudyPage(props) {
1035
+ const {
1036
+ productName,
1037
+ caseStudiesListUrl = "/case-studies",
1038
+ backLabel = "Back to Case Studies",
1039
+ className = ""
1040
+ } = props;
1041
+ const client = useWebSDK();
1042
+ const config = useWebSDKConfig();
1043
+ const { product } = useProduct();
1044
+ const resolvedProductId = product?.id || config.productId;
1045
+ const [post, setPost] = React.useState(null);
1046
+ const [loading, setLoading] = React.useState(true);
1047
+ const [error, setError] = React.useState(null);
1048
+ const slug = typeof window !== "undefined" ? window.location.pathname.split("/").pop() || "" : "";
1049
+ React.useEffect(() => {
1050
+ if (!slug || !resolvedProductId) return;
1051
+ async function fetchPost() {
1052
+ setLoading(true);
1053
+ try {
1054
+ const result = await client.query(CASE_STUDY_QUERY, {
1055
+ slug,
1056
+ productId: resolvedProductId
1057
+ });
1058
+ if (result.errors?.length) {
1059
+ setError(result.errors[0]?.message ?? "Failed to load case study");
1060
+ return;
1061
+ }
1062
+ setPost(result.data?.publicContentPage ?? null);
1063
+ setError(null);
1064
+ } catch {
1065
+ setError("Failed to load case study");
1066
+ } finally {
1067
+ setLoading(false);
1068
+ }
1069
+ }
1070
+ fetchPost();
1071
+ }, [client, slug, resolvedProductId]);
1072
+ if (loading) {
1073
+ 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: [
1074
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-8 bg-muted rounded w-2/3" }),
1075
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-1/3" }),
1076
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] bg-muted rounded-xl" }),
1077
+ /* @__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)) })
1078
+ ] }) }) });
1079
+ }
1080
+ if (error || !post) {
1081
+ 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: [
1082
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl font-bold mb-4", children: "Case study not found" }),
1083
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground mb-6", children: error || "This case study doesn't exist or has been removed." }),
1084
+ /* @__PURE__ */ jsxRuntime.jsxs(
1085
+ "a",
1086
+ {
1087
+ href: caseStudiesListUrl,
1088
+ className: "text-primary hover:underline font-medium",
1089
+ children: [
1090
+ "\u2190 ",
1091
+ backLabel
1092
+ ]
1093
+ }
1094
+ )
1095
+ ] }) });
1096
+ }
1097
+ const date = post.publishedAt || post.lastUpdated;
1098
+ const formattedDate = date ? new Date(date).toLocaleDateString("en-US", {
1099
+ year: "numeric",
1100
+ month: "long",
1101
+ day: "numeric"
1102
+ }) : "";
1103
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `w-full ${className}`, children: [
1104
+ /* @__PURE__ */ jsxRuntime.jsx(
1105
+ PageHead,
1106
+ {
1107
+ title: post.seoTitle || post.title,
1108
+ description: post.seoDescription || post.excerpt || "",
1109
+ productName,
1110
+ keywords: post.seoKeywords || ""
1111
+ }
1112
+ ),
1113
+ /* @__PURE__ */ jsxRuntime.jsxs("article", { className: "max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-16 sm:py-20", children: [
1114
+ /* @__PURE__ */ jsxRuntime.jsxs(
1115
+ "a",
1116
+ {
1117
+ href: caseStudiesListUrl,
1118
+ className: "inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground mb-8 transition-colors",
1119
+ children: [
1120
+ "\u2190 ",
1121
+ backLabel
1122
+ ]
1123
+ }
1124
+ ),
1125
+ 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 }),
1126
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-3xl sm:text-4xl md:text-5xl font-bold tracking-tight text-foreground mb-4", children: post.title }),
1127
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 text-sm text-muted-foreground mb-8", children: [
1128
+ post.author && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-foreground", children: post.author }),
1129
+ post.author && formattedDate && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
1130
+ formattedDate && /* @__PURE__ */ jsxRuntime.jsx("span", { children: formattedDate }),
1131
+ post.readingTime && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1132
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
1133
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
1134
+ post.readingTime,
1135
+ " min read"
1136
+ ] })
1137
+ ] })
1138
+ ] }),
1139
+ post.thumbnail && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] rounded-xl overflow-hidden mb-10", children: /* @__PURE__ */ jsxRuntime.jsx(
1140
+ "img",
1141
+ {
1142
+ src: post.thumbnail,
1143
+ alt: post.title,
1144
+ className: "w-full h-full object-cover"
1145
+ }
1146
+ ) }),
1147
+ /* @__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 }) })
1148
+ ] })
1149
+ ] });
1150
+ }
1151
+ function cn(...inputs) {
1152
+ return tailwindMerge.twMerge(clsx.clsx(inputs));
1153
+ }
1154
+ var buttonVariants = classVarianceAuthority.cva(
1155
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
1156
+ {
1157
+ variants: {
1158
+ variant: {
1159
+ default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
1160
+ destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
1161
+ outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
1162
+ secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
1163
+ ghost: "hover:bg-accent hover:text-accent-foreground",
1164
+ link: "text-primary underline-offset-4 hover:underline"
1165
+ },
1166
+ size: {
1167
+ default: "h-9 px-4 py-2",
1168
+ sm: "h-8 rounded-md px-3 text-xs",
1169
+ lg: "h-10 rounded-md px-8",
1170
+ icon: "h-9 w-9"
1171
+ }
1172
+ },
1173
+ defaultVariants: {
1174
+ variant: "default",
1175
+ size: "default"
1176
+ }
1177
+ }
1178
+ );
1179
+ var Button = React__namespace.forwardRef(
1180
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
1181
+ const Comp = asChild ? reactSlot.Slot : "button";
1182
+ return /* @__PURE__ */ jsxRuntime.jsx(
1183
+ Comp,
1184
+ {
1185
+ className: cn(buttonVariants({ variant, size, className })),
1186
+ ref,
1187
+ ...props
1188
+ }
1189
+ );
1190
+ }
1191
+ );
1192
+ Button.displayName = "Button";
1193
+ var Input = React__namespace.forwardRef(
1194
+ ({ className, type, ...props }, ref) => {
1195
+ return /* @__PURE__ */ jsxRuntime.jsx(
1196
+ "input",
1197
+ {
1198
+ type,
1199
+ className: cn(
1200
+ "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground 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",
1201
+ className
1202
+ ),
1203
+ ref,
1204
+ ...props
1205
+ }
1206
+ );
1207
+ }
1208
+ );
1209
+ Input.displayName = "Input";
1210
+ var CHANGELOG_TAG = "changelog";
1211
+ var CHANGELOG_LIST_QUERY = `
1212
+ query ChangelogList($productId: String!, $limit: Int, $offset: Int) {
1213
+ publicContentPages(productId: $productId, limit: $limit, offset: $offset) {
1214
+ items {
1215
+ id
1216
+ title
1217
+ slug
1218
+ lastUpdated
1219
+ tags
1220
+ excerpt
1221
+ seoDescription
1222
+ thumbnail
1223
+ author
1224
+ publishedAt
1225
+ readingTime
1226
+ featured
1227
+ category
1228
+ }
1229
+ total
1230
+ hasMore
1231
+ }
1232
+ }
1233
+ `;
1234
+ var CHANGELOG_ENTRY_QUERY = `
1235
+ query ChangelogEntry($slug: String!, $productId: String) {
1236
+ publicContentPage(slug: $slug, productId: $productId) {
1237
+ content
1238
+ contentFormat
1239
+ }
1240
+ }
1241
+ `;
1242
+ function formatDate(value) {
1243
+ if (!value) return "";
1244
+ return new Date(value).toLocaleDateString("en-US", {
1245
+ year: "numeric",
1246
+ month: "long",
1247
+ day: "numeric"
1248
+ });
1249
+ }
1250
+ function sortByDateDesc(a, b) {
1251
+ const da = new Date(a.publishedAt || a.lastUpdated || 0).getTime();
1252
+ const db = new Date(b.publishedAt || b.lastUpdated || 0).getTime();
1253
+ return db - da;
1254
+ }
1255
+ function normalize(value) {
1256
+ return value.toLowerCase().trim();
1257
+ }
1258
+ function paginate(items, page, pageSize) {
1259
+ const start = (page - 1) * pageSize;
1260
+ return items.slice(start, start + pageSize);
1261
+ }
1262
+ function parseEntryDate(date) {
1263
+ return new Date(date).getTime();
1264
+ }
1265
+ function useCmsChangelog() {
1266
+ const client = useWebSDK();
1267
+ const config = useWebSDKConfig();
1268
+ const { product } = useProduct();
1269
+ const resolvedProductId = product?.id || config.productId;
1270
+ const [entries, setEntries] = React.useState([]);
1271
+ const [loading, setLoading] = React.useState(true);
1272
+ const [error, setError] = React.useState(null);
1273
+ const fetchEntries = React.useCallback(async () => {
1274
+ if (!resolvedProductId) {
1275
+ setLoading(false);
1276
+ return;
1277
+ }
1278
+ setLoading(true);
1279
+ try {
1280
+ const listResult = await client.query(CHANGELOG_LIST_QUERY, {
1281
+ productId: resolvedProductId,
1282
+ limit: 50,
1283
+ offset: 0
1284
+ });
1285
+ if (listResult.errors?.length) {
1286
+ setError(listResult.errors[0]?.message ?? "Failed to load changelog");
1287
+ return;
1288
+ }
1289
+ const changelogItems = (listResult.data?.publicContentPages?.items || []).filter((p) => p.tags?.includes(CHANGELOG_TAG)).sort(sortByDateDesc);
1290
+ const resolved = await Promise.all(
1291
+ changelogItems.map(async (item) => {
1292
+ try {
1293
+ const detail = await client.query(CHANGELOG_ENTRY_QUERY, {
1294
+ slug: item.slug,
1295
+ productId: resolvedProductId
1296
+ });
1297
+ const page = detail.data?.publicContentPage;
1298
+ return {
1299
+ ...item,
1300
+ content: page?.content,
1301
+ contentFormat: page?.contentFormat
1302
+ };
1303
+ } catch {
1304
+ return { ...item };
1305
+ }
1306
+ })
1307
+ );
1308
+ setEntries(resolved);
1309
+ setError(null);
1310
+ } catch {
1311
+ setError("Failed to load changelog");
1312
+ } finally {
1313
+ setLoading(false);
1314
+ }
1315
+ }, [client, resolvedProductId]);
1316
+ React.useEffect(() => {
1317
+ fetchEntries();
1318
+ }, [fetchEntries]);
1319
+ return { entries, loading, error };
1320
+ }
1321
+ function CmsTimeline(props) {
1322
+ const { entries, loading, error } = props;
1323
+ if (loading) {
1324
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-3xl mx-auto space-y-12", children: [1, 2, 3].map((i) => /* @__PURE__ */ jsxRuntime.jsx(
1325
+ "div",
1326
+ {
1327
+ className: "rounded-2xl border border-border/40 bg-card overflow-hidden animate-pulse",
1328
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6 sm:p-8 space-y-4", children: [
1329
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3 bg-muted rounded w-1/3" }),
1330
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-6 bg-muted rounded w-2/3" }),
1331
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded" }),
1332
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-5/6" })
1333
+ ] })
1334
+ },
1335
+ i
1336
+ )) });
1337
+ }
1338
+ if (error) {
1339
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center py-12", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: error }) });
1340
+ }
1341
+ if (entries.length === 0) {
1342
+ 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!" }) });
1343
+ }
1344
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-3xl mx-auto space-y-12", children: entries.map((entry) => /* @__PURE__ */ jsxRuntime.jsxs(
1345
+ "article",
1346
+ {
1347
+ id: entry.slug,
1348
+ className: "rounded-2xl border border-border/40 bg-card overflow-hidden scroll-mt-24",
1349
+ children: [
1350
+ entry.thumbnail && /* @__PURE__ */ jsxRuntime.jsx(
1351
+ "img",
1352
+ {
1353
+ src: entry.thumbnail,
1354
+ alt: "",
1355
+ width: 1024,
1356
+ height: 576,
1357
+ loading: "lazy",
1358
+ decoding: "async",
1359
+ className: "w-full aspect-video object-cover border-b border-border/40"
1360
+ }
1361
+ ),
1362
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6 sm:p-8", children: [
1363
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground mb-2", children: [
1364
+ entry.publishedAt && /* @__PURE__ */ jsxRuntime.jsx("time", { dateTime: entry.publishedAt, children: formatDate(entry.publishedAt || entry.lastUpdated) }),
1365
+ entry.author && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1366
+ " \xB7 ",
1367
+ entry.author
1368
+ ] })
1369
+ ] }),
1370
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-xl sm:text-2xl font-bold text-foreground mb-4", children: entry.title }),
1371
+ entry.content ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "prose prose-sm prose-gray dark:prose-invert max-w-none", children: /* @__PURE__ */ jsxRuntime.jsx(
1372
+ ContentRenderer,
1373
+ {
1374
+ content: entry.content,
1375
+ format: entry.contentFormat
1376
+ }
1377
+ ) }) : entry.excerpt && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground leading-relaxed", children: entry.excerpt })
1378
+ ] })
1379
+ ]
1380
+ },
1381
+ entry.id
1382
+ )) });
1383
+ }
1384
+ function EntryContent({ content }) {
1385
+ if (typeof content === "string") {
1386
+ 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" }) });
1387
+ }
1388
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "prose prose-sm max-w-none", children: content });
1389
+ }
1390
+ function EntriesTimeline(props) {
1391
+ const { entries, pageSize, productName } = props;
1392
+ const [query, setQuery] = React.useState("");
1393
+ const [selectedCategory, setSelectedCategory] = React.useState(null);
1394
+ const [page, setPage] = React.useState(1);
1395
+ const categories = React.useMemo(
1396
+ () => Array.from(new Set(entries.map((e) => e.category))).sort(),
1397
+ [entries]
1398
+ );
1399
+ const filtered = React.useMemo(() => {
1400
+ const normalized = normalize(query);
1401
+ return entries.filter((entry) => {
1402
+ if (selectedCategory && entry.category !== selectedCategory) {
1403
+ return false;
1404
+ }
1405
+ if (!normalized) return true;
1406
+ const haystack = [
1407
+ entry.title,
1408
+ entry.version,
1409
+ entry.category,
1410
+ ...entry.tags ?? [],
1411
+ typeof entry.content === "string" ? entry.content : ""
1412
+ ].join(" ").toLowerCase();
1413
+ return haystack.includes(normalized);
1414
+ }).sort((a, b) => parseEntryDate(b.date) - parseEntryDate(a.date));
1415
+ }, [entries, query, selectedCategory]);
1416
+ const totalPages = Math.max(1, Math.ceil(filtered.length / pageSize));
1417
+ const safePage = Math.min(page, totalPages);
1418
+ const pageItems = paginate(filtered, safePage, pageSize);
1419
+ React.useEffect(() => {
1420
+ setPage(1);
1421
+ }, [query, selectedCategory]);
1422
+ if (entries.length === 0) {
1423
+ 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!" }) });
1424
+ }
1425
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-3xl mx-auto", children: [
1426
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-8 space-y-4", children: [
1427
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
1428
+ /* @__PURE__ */ jsxRuntime.jsx(
1429
+ lucideReact.Search,
1430
+ {
1431
+ className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground",
1432
+ "aria-hidden": "true"
1433
+ }
1434
+ ),
1435
+ /* @__PURE__ */ jsxRuntime.jsx(
1436
+ Input,
1437
+ {
1438
+ type: "search",
1439
+ placeholder: "Search releases, features, fixes\u2026",
1440
+ value: query,
1441
+ onChange: (e) => setQuery(e.target.value),
1442
+ className: "pl-9",
1443
+ "aria-label": "Search changelog"
1444
+ }
1445
+ )
1446
+ ] }),
1447
+ categories.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap gap-2", children: [
1448
+ /* @__PURE__ */ jsxRuntime.jsx(
1449
+ "button",
1450
+ {
1451
+ type: "button",
1452
+ onClick: () => setSelectedCategory(null),
1453
+ className: cn(
1454
+ "px-3 py-1 rounded-full text-xs font-medium border transition-colors",
1455
+ selectedCategory === null ? "bg-primary text-primary-foreground border-primary" : "bg-background text-muted-foreground border-border/60 hover:border-primary/40"
1456
+ ),
1457
+ children: "All"
1458
+ }
1459
+ ),
1460
+ categories.map((category) => /* @__PURE__ */ jsxRuntime.jsx(
1461
+ "button",
1462
+ {
1463
+ type: "button",
1464
+ onClick: () => setSelectedCategory(category),
1465
+ className: cn(
1466
+ "px-3 py-1 rounded-full text-xs font-medium border transition-colors",
1467
+ selectedCategory === category ? "bg-primary text-primary-foreground border-primary" : "bg-background text-muted-foreground border-border/60 hover:border-primary/40"
1468
+ ),
1469
+ children: category
1470
+ },
1471
+ category
1472
+ ))
1473
+ ] })
1474
+ ] }),
1475
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
1476
+ /* @__PURE__ */ jsxRuntime.jsx(
1477
+ "div",
1478
+ {
1479
+ className: "absolute left-[1.125rem] top-3 bottom-3 w-px bg-border/60 hidden sm:block",
1480
+ "aria-hidden": "true"
1481
+ }
1482
+ ),
1483
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-8", children: pageItems.map((entry) => /* @__PURE__ */ jsxRuntime.jsxs(
1484
+ "article",
1485
+ {
1486
+ className: "relative sm:pl-12",
1487
+ children: [
1488
+ /* @__PURE__ */ jsxRuntime.jsx(
1489
+ "div",
1490
+ {
1491
+ 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",
1492
+ "aria-hidden": "true",
1493
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-2 w-2 rounded-full bg-primary-foreground" })
1494
+ }
1495
+ ),
1496
+ /* @__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: [
1497
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2 mb-3", children: [
1498
+ /* @__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 }),
1499
+ /* @__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: [
1500
+ "v",
1501
+ entry.version
1502
+ ] }),
1503
+ /* @__PURE__ */ jsxRuntime.jsx(
1504
+ "time",
1505
+ {
1506
+ className: "text-xs text-muted-foreground",
1507
+ dateTime: entry.date,
1508
+ children: formatDate(entry.date)
1509
+ }
1510
+ )
1511
+ ] }),
1512
+ 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(
1513
+ "span",
1514
+ {
1515
+ className: "text-[10px] uppercase tracking-wide text-muted-foreground bg-background border border-border/40 px-1.5 py-0.5 rounded",
1516
+ children: tag
1517
+ },
1518
+ tag
1519
+ )) }),
1520
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-xl sm:text-2xl font-bold text-foreground mb-4", children: entry.title }),
1521
+ /* @__PURE__ */ jsxRuntime.jsx(EntryContent, { content: entry.content })
1522
+ ] }) })
1523
+ ]
1524
+ },
1525
+ `${entry.version}-${entry.date}`
1526
+ )) })
1527
+ ] }),
1528
+ 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." }) }),
1529
+ totalPages > 1 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-10 flex items-center justify-between", children: [
1530
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-muted-foreground", children: [
1531
+ "Page ",
1532
+ safePage,
1533
+ " of ",
1534
+ totalPages,
1535
+ " \xB7 ",
1536
+ filtered.length,
1537
+ " entries"
1538
+ ] }),
1539
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
1540
+ /* @__PURE__ */ jsxRuntime.jsx(
1541
+ Button,
1542
+ {
1543
+ variant: "outline",
1544
+ size: "sm",
1545
+ onClick: () => setPage((p) => Math.max(1, p - 1)),
1546
+ disabled: safePage <= 1,
1547
+ "aria-label": "Previous page",
1548
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { className: "h-4 w-4" })
1549
+ }
1550
+ ),
1551
+ /* @__PURE__ */ jsxRuntime.jsx(
1552
+ Button,
1553
+ {
1554
+ variant: "outline",
1555
+ size: "sm",
1556
+ onClick: () => setPage((p) => Math.min(totalPages, p + 1)),
1557
+ disabled: safePage >= totalPages,
1558
+ "aria-label": "Next page",
1559
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "h-4 w-4" })
1560
+ }
1561
+ )
1562
+ ] })
1563
+ ] }),
1564
+ productName && pageItems.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("script", { type: "application/ld+json", children: JSON.stringify({
1565
+ "@context": "https://schema.org",
1566
+ "@type": "ItemList",
1567
+ name: `${productName} Changelog`,
1568
+ itemListElement: pageItems.map((entry, index) => ({
1569
+ "@type": "ListItem",
1570
+ position: (safePage - 1) * pageSize + index + 1,
1571
+ name: entry.title,
1572
+ description: typeof entry.content === "string" ? entry.content.slice(0, 200) : void 0,
1573
+ datePublished: entry.date
1574
+ }))
1575
+ }) })
1576
+ ] });
1577
+ }
1578
+ function ChangelogPage(props) {
1579
+ const {
1580
+ productName,
1581
+ heroEyebrow = "What's new",
1582
+ heroTitle = "Changelog",
1583
+ heroSubtitle = "Platform updates, improvements, and announcements \u2014 newest first.",
1584
+ ctaTitle,
1585
+ ctaSubtitle,
1586
+ ctaHref,
1587
+ ctaLabel,
1588
+ className = "",
1589
+ seoTitle,
1590
+ seoDescription,
1591
+ seoKeywords,
1592
+ entries,
1593
+ pageSize = 10
1594
+ } = props;
1595
+ const { product } = useProduct();
1596
+ const resolvedProductName = productName || product?.name;
1597
+ const cms = useCmsChangelog();
1598
+ const showCta = Boolean(ctaHref && ctaLabel);
1599
+ const isPropDriven = entries != null;
1600
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `w-full ${className}`, children: [
1601
+ /* @__PURE__ */ jsxRuntime.jsx(
1602
+ PageHead,
1603
+ {
1604
+ title: seoTitle || heroTitle,
1605
+ description: seoDescription || heroSubtitle,
1606
+ productName: resolvedProductName,
1607
+ keywords: seoKeywords
1608
+ }
1609
+ ),
1610
+ /* @__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: [
1611
+ /* @__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: [
1612
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.History, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
1613
+ heroEyebrow
1614
+ ] }),
1615
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-4xl sm:text-5xl font-bold text-foreground mb-4", children: heroTitle }),
1616
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-lg text-muted-foreground", children: heroSubtitle })
1617
+ ] }) }),
1618
+ /* @__PURE__ */ jsxRuntime.jsx("section", { className: "px-4 sm:px-6 lg:px-8 pb-20", children: isPropDriven ? /* @__PURE__ */ jsxRuntime.jsx(
1619
+ EntriesTimeline,
1620
+ {
1621
+ entries,
1622
+ pageSize,
1623
+ productName: resolvedProductName
1624
+ }
1625
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
1626
+ CmsTimeline,
1627
+ {
1628
+ entries: cms.entries,
1629
+ loading: cms.loading,
1630
+ error: cms.error
1631
+ }
1632
+ ) }),
1633
+ 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: [
1634
+ ctaTitle && /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl font-bold text-foreground mb-3", children: ctaTitle }),
1635
+ ctaSubtitle && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground mb-7", children: ctaSubtitle }),
1636
+ /* @__PURE__ */ jsxRuntime.jsx(
1637
+ "a",
1638
+ {
1639
+ href: ctaHref,
1640
+ 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",
1641
+ children: ctaLabel
1642
+ }
1643
+ )
1644
+ ] }) })
1645
+ ] });
1646
+ }
1647
+
1648
+ // src/content/utils.ts
1649
+ function formatBytes(bytes) {
1650
+ if (bytes == null) return "";
1651
+ if (bytes < 1024) return `${bytes} B`;
1652
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)} KB`;
1653
+ return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
1654
+ }
1655
+ function fileExt(file) {
1656
+ const name = file.fileName || file.fileUrl.split("/").pop()?.split("?")[0] || "";
1657
+ const ext = name.split(".").pop();
1658
+ return ext && ext !== name ? ext.toUpperCase() : "FILE";
1659
+ }
1660
+ async function downloadFile(e, file) {
1661
+ e.preventDefault();
1662
+ const openInTab = () => window.open(file.fileUrl, "_blank", "noopener,noreferrer");
1663
+ const name = file.fileName || file.fileUrl.split("/").pop()?.split("?")[0] || file.title || "download";
1664
+ try {
1665
+ const res = await fetch(file.fileUrl, { mode: "cors" });
1666
+ if (!res.ok) {
1667
+ openInTab();
1668
+ return;
1669
+ }
1670
+ const blob = await res.blob();
1671
+ const objUrl = URL.createObjectURL(blob);
1672
+ const a = document.createElement("a");
1673
+ a.href = objUrl;
1674
+ a.download = name;
1675
+ document.body.appendChild(a);
1676
+ a.click();
1677
+ a.remove();
1678
+ URL.revokeObjectURL(objUrl);
1679
+ } catch {
1680
+ openInTab();
1681
+ }
1682
+ }
1683
+ var PRESS_KIT_QUERY = `
1684
+ query PublicPressKit($productId: String!, $limit: Int, $offset: Int) {
1685
+ publicPressKitAssets(productId: $productId, limit: $limit, offset: $offset) {
1686
+ items {
1687
+ id
1688
+ category
1689
+ title
1690
+ slug
1691
+ description
1692
+ fileUrl
1693
+ fileName
1694
+ fileSize
1695
+ mimeType
1696
+ thumbnailUrl
1697
+ version
1698
+ tags
1699
+ sortOrder
1700
+ updatedAt
1701
+ }
1702
+ total
1703
+ hasMore
1704
+ }
1705
+ }
1706
+ `;
1707
+ var PRESS_RELEASES_QUERY = `
1708
+ query PublicPressReleases($productId: String!, $limit: Int, $offset: Int) {
1709
+ publicContentPages(productId: $productId, limit: $limit, offset: $offset) {
1710
+ items {
1711
+ id
1712
+ title
1713
+ slug
1714
+ lastUpdated
1715
+ tags
1716
+ seoDescription
1717
+ excerpt
1718
+ thumbnail
1719
+ author
1720
+ publishedAt
1721
+ readingTime
1722
+ featured
1723
+ category
1724
+ }
1725
+ total
1726
+ hasMore
1727
+ }
1728
+ }
1729
+ `;
1730
+ var CATEGORY_ORDER = [
1731
+ "LOGO",
1732
+ "BANNER",
1733
+ "SCREENSHOT",
1734
+ "BRAND_GUIDE",
1735
+ "BROCHURE",
1736
+ "DOCUMENT",
1737
+ "PRESS_RELEASE"
1738
+ ];
1739
+ var CATEGORY_LABELS = {
1740
+ LOGO: "Logos",
1741
+ BANNER: "Banners",
1742
+ SCREENSHOT: "Screenshots",
1743
+ BRAND_GUIDE: "Brand Guides",
1744
+ BROCHURE: "Brochures",
1745
+ DOCUMENT: "Documents",
1746
+ PRESS_RELEASE: "Press Releases"
1747
+ };
1748
+ function isImageAsset(a) {
1749
+ return (a.mimeType || "").startsWith("image/");
1750
+ }
1751
+ function PressKitLightbox({
1752
+ asset,
1753
+ onClose
1754
+ }) {
1755
+ React.useEffect(() => {
1756
+ if (!asset) return;
1757
+ const onKey = (e) => {
1758
+ if (e.key === "Escape") onClose();
1759
+ };
1760
+ document.addEventListener("keydown", onKey);
1761
+ const prevOverflow = document.body.style.overflow;
1762
+ document.body.style.overflow = "hidden";
1763
+ return () => {
1764
+ document.removeEventListener("keydown", onKey);
1765
+ document.body.style.overflow = prevOverflow;
1766
+ };
1767
+ }, [asset, onClose]);
1768
+ if (!asset) return null;
1769
+ const ext = (asset.mimeType || "file").split("/").pop()?.toUpperCase() || "FILE";
1770
+ const size = formatBytes(asset.fileSize);
1771
+ return /* @__PURE__ */ jsxRuntime.jsx(
1772
+ "div",
1773
+ {
1774
+ role: "dialog",
1775
+ "aria-modal": "true",
1776
+ "aria-label": `${asset.title} preview`,
1777
+ onClick: onClose,
1778
+ className: "fixed inset-0 z-50 flex items-center justify-center bg-black/80 p-4 sm:p-8",
1779
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
1780
+ "div",
1781
+ {
1782
+ onClick: (e) => e.stopPropagation(),
1783
+ className: "relative flex max-h-full w-full max-w-4xl flex-col overflow-hidden rounded-xl border bg-card shadow-2xl",
1784
+ children: [
1785
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-4 border-b p-4", children: [
1786
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
1787
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "truncate text-sm font-semibold text-foreground", children: asset.title }),
1788
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground", children: [
1789
+ ext,
1790
+ size ? ` \xB7 ${size}` : ""
1791
+ ] })
1792
+ ] }),
1793
+ /* @__PURE__ */ jsxRuntime.jsx(
1794
+ "button",
1795
+ {
1796
+ type: "button",
1797
+ onClick: onClose,
1798
+ "aria-label": "Close preview",
1799
+ className: "shrink-0 rounded-md p-1.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
1800
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1801
+ "svg",
1802
+ {
1803
+ className: "h-5 w-5",
1804
+ fill: "none",
1805
+ viewBox: "0 0 24 24",
1806
+ stroke: "currentColor",
1807
+ strokeWidth: 2,
1808
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1809
+ "path",
1810
+ {
1811
+ strokeLinecap: "round",
1812
+ strokeLinejoin: "round",
1813
+ d: "M6 18L18 6M6 6l12 12"
1814
+ }
1815
+ )
1816
+ }
1817
+ )
1818
+ }
1819
+ )
1820
+ ] }),
1821
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-1 items-center justify-center overflow-auto bg-muted/20 p-4", children: /* @__PURE__ */ jsxRuntime.jsx(
1822
+ "img",
1823
+ {
1824
+ src: asset.fileUrl,
1825
+ alt: asset.title,
1826
+ className: "max-h-[70vh] w-auto max-w-full object-contain"
1827
+ }
1828
+ ) }),
1829
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-end border-t p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(
1830
+ "a",
1831
+ {
1832
+ href: asset.fileUrl,
1833
+ target: "_blank",
1834
+ rel: "noopener noreferrer",
1835
+ download: true,
1836
+ onClick: (e) => {
1837
+ void downloadFile(e, asset);
1838
+ },
1839
+ 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",
1840
+ children: [
1841
+ /* @__PURE__ */ jsxRuntime.jsx(
1842
+ "svg",
1843
+ {
1844
+ className: "h-4 w-4",
1845
+ fill: "none",
1846
+ viewBox: "0 0 24 24",
1847
+ stroke: "currentColor",
1848
+ strokeWidth: 2,
1849
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1850
+ "path",
1851
+ {
1852
+ strokeLinecap: "round",
1853
+ strokeLinejoin: "round",
1854
+ d: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
1855
+ }
1856
+ )
1857
+ }
1858
+ ),
1859
+ "Download"
1860
+ ]
1861
+ }
1862
+ ) })
1863
+ ]
1864
+ }
1865
+ )
1866
+ }
1867
+ );
1868
+ }
1869
+ function AssetCard({
1870
+ asset,
1871
+ onPreview
1872
+ }) {
1873
+ const date = asset.updatedAt ? new Date(asset.updatedAt).toLocaleDateString("en-US", {
1874
+ year: "numeric",
1875
+ month: "short",
1876
+ day: "numeric"
1877
+ }) : "";
1878
+ const ext = (asset.mimeType || "file").split("/").pop()?.toUpperCase() || "FILE";
1879
+ const size = formatBytes(asset.fileSize);
1880
+ const [imgError, setImgError] = React.useState(false);
1881
+ const thumb = asset.thumbnailUrl && !imgError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] overflow-hidden bg-muted/30", children: /* @__PURE__ */ jsxRuntime.jsx(
1882
+ "img",
1883
+ {
1884
+ src: asset.thumbnailUrl,
1885
+ alt: asset.title,
1886
+ className: "w-full h-full object-contain group-hover:scale-105 transition-transform duration-300",
1887
+ loading: "lazy",
1888
+ onError: () => setImgError(true)
1889
+ }
1890
+ ) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] bg-muted/50 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(
1891
+ "svg",
1892
+ {
1893
+ className: "w-12 h-12 text-muted-foreground/20",
1894
+ fill: "none",
1895
+ viewBox: "0 0 24 24",
1896
+ stroke: "currentColor",
1897
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1898
+ "path",
1899
+ {
1900
+ strokeLinecap: "round",
1901
+ strokeLinejoin: "round",
1902
+ strokeWidth: 1.5,
1903
+ 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"
1904
+ }
1905
+ )
1906
+ }
1907
+ ) });
1908
+ 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: [
1909
+ onPreview && isImageAsset(asset) ? /* @__PURE__ */ jsxRuntime.jsx(
1910
+ "button",
1911
+ {
1912
+ type: "button",
1913
+ onClick: () => onPreview(asset),
1914
+ "aria-label": `Preview ${asset.title}`,
1915
+ className: "block w-full text-left cursor-zoom-in",
1916
+ children: thumb
1917
+ }
1918
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
1919
+ "a",
1920
+ {
1921
+ href: asset.fileUrl,
1922
+ target: "_blank",
1923
+ rel: "noopener noreferrer",
1924
+ "aria-label": `Open ${asset.title}`,
1925
+ className: "block",
1926
+ children: thumb
1927
+ }
1928
+ ),
1929
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col flex-1 p-5", children: [
1930
+ /* @__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 }),
1931
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-base font-semibold text-foreground mb-1 line-clamp-2", children: asset.title }),
1932
+ asset.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground mb-3 line-clamp-2", children: asset.description }),
1933
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-auto flex items-center justify-between pt-3", children: [
1934
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
1935
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: ext }),
1936
+ size && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1937
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
1938
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: size })
1939
+ ] }),
1940
+ date && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1941
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
1942
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: date })
1943
+ ] })
1944
+ ] }),
1945
+ /* @__PURE__ */ jsxRuntime.jsxs(
1946
+ "a",
1947
+ {
1948
+ href: asset.fileUrl,
1949
+ target: "_blank",
1950
+ rel: "noopener noreferrer",
1951
+ download: true,
1952
+ onClick: (e) => {
1953
+ void downloadFile(e, asset);
1954
+ },
1955
+ className: "inline-flex items-center gap-1.5 text-sm font-medium text-primary hover:underline",
1956
+ children: [
1957
+ /* @__PURE__ */ jsxRuntime.jsx(
1958
+ "svg",
1959
+ {
1960
+ className: "w-4 h-4",
1961
+ fill: "none",
1962
+ viewBox: "0 0 24 24",
1963
+ stroke: "currentColor",
1964
+ strokeWidth: 2,
1965
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1966
+ "path",
1967
+ {
1968
+ strokeLinecap: "round",
1969
+ strokeLinejoin: "round",
1970
+ d: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
1971
+ }
1972
+ )
1973
+ }
1974
+ ),
1975
+ "Download"
1976
+ ]
1977
+ }
1978
+ )
1979
+ ] })
1980
+ ] })
1981
+ ] });
1982
+ }
1983
+ function PressReleaseCard({
1984
+ release,
1985
+ basePath
1986
+ }) {
1987
+ const date = release.publishedAt || release.lastUpdated;
1988
+ const formattedDate = date ? new Date(date).toLocaleDateString("en-US", {
1989
+ year: "numeric",
1990
+ month: "long",
1991
+ day: "numeric"
1992
+ }) : "";
1993
+ const [imgError, setImgError] = React.useState(false);
1994
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1995
+ "a",
1996
+ {
1997
+ href: `${basePath}/${release.slug}`,
1998
+ className: "group flex flex-col bg-card border rounded-xl overflow-hidden hover:shadow-lg hover:border-primary/30 transition-all duration-300",
1999
+ children: [
2000
+ release.thumbnail && !imgError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] overflow-hidden bg-muted/30", children: /* @__PURE__ */ jsxRuntime.jsx(
2001
+ "img",
2002
+ {
2003
+ src: release.thumbnail,
2004
+ alt: release.title,
2005
+ className: "w-full h-full object-cover group-hover:scale-105 transition-transform duration-300",
2006
+ loading: "lazy",
2007
+ onError: () => setImgError(true)
2008
+ }
2009
+ ) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] bg-muted/50 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(
2010
+ "svg",
2011
+ {
2012
+ className: "w-12 h-12 text-muted-foreground/20",
2013
+ fill: "none",
2014
+ viewBox: "0 0 24 24",
2015
+ stroke: "currentColor",
2016
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2017
+ "path",
2018
+ {
2019
+ strokeLinecap: "round",
2020
+ strokeLinejoin: "round",
2021
+ strokeWidth: 1.5,
2022
+ 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"
2023
+ }
2024
+ )
2025
+ }
2026
+ ) }),
2027
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col flex-1 p-5 sm:p-6", children: [
2028
+ 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 }),
2029
+ /* @__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 }),
2030
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground mb-4 line-clamp-3", children: release.excerpt || release.seoDescription || "" }),
2031
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-auto flex items-center gap-3 text-xs text-muted-foreground", children: [
2032
+ release.author && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: release.author }),
2033
+ release.author && formattedDate && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
2034
+ formattedDate && /* @__PURE__ */ jsxRuntime.jsx("span", { children: formattedDate }),
2035
+ release.readingTime ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2036
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
2037
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
2038
+ release.readingTime,
2039
+ " min read"
2040
+ ] })
2041
+ ] }) : null
2042
+ ] })
2043
+ ] })
2044
+ ]
2045
+ }
2046
+ );
2047
+ }
2048
+ function PressKitPage(props) {
2049
+ const {
2050
+ productName,
2051
+ heroTitle = "Press & Media Kit",
2052
+ heroSubtitle = "Official logos, brand assets, and media resources. Everything you need to write about us.",
2053
+ mediaContactEmail,
2054
+ brandKitUrl,
2055
+ className = "",
2056
+ seoTitle,
2057
+ seoDescription,
2058
+ seoKeywords,
2059
+ showPressReleases = true,
2060
+ pressReleaseBasePath = "/press",
2061
+ pressReleasesTitle = "Press Releases"
2062
+ } = props;
2063
+ const client = useWebSDK();
2064
+ const config = useWebSDKConfig();
2065
+ const { product } = useProduct();
2066
+ const resolvedProductId = product?.id || config.productId;
2067
+ const contactEmail = mediaContactEmail || product?.contactEmail || void 0;
2068
+ const [assets, setAssets] = React.useState([]);
2069
+ const [loading, setLoading] = React.useState(true);
2070
+ const [error, setError] = React.useState(null);
2071
+ const [activeCategory, setActiveCategory] = React.useState("ALL");
2072
+ const [releases, setReleases] = React.useState([]);
2073
+ const [releasesLoading, setReleasesLoading] = React.useState(true);
2074
+ const [preview, setPreview] = React.useState(null);
2075
+ const fetchAssets = React.useCallback(async () => {
2076
+ if (!resolvedProductId) {
2077
+ setLoading(false);
2078
+ return;
2079
+ }
2080
+ setLoading(true);
2081
+ try {
2082
+ const PAGE_SIZE = 100;
2083
+ const MAX_PAGES = 10;
2084
+ const all = [];
2085
+ let offset = 0;
2086
+ for (let pageNum = 0; pageNum < MAX_PAGES; pageNum++) {
2087
+ const result = await client.query(PRESS_KIT_QUERY, {
2088
+ productId: resolvedProductId,
2089
+ limit: PAGE_SIZE,
2090
+ offset
2091
+ });
2092
+ if (result.errors?.length) {
2093
+ setError(result.errors[0]?.message ?? "Failed to load press kit");
2094
+ return;
2095
+ }
2096
+ const page = result.data?.publicPressKitAssets;
2097
+ const items = page?.items || [];
2098
+ all.push(...items);
2099
+ if (!page?.hasMore || items.length === 0) break;
2100
+ offset += PAGE_SIZE;
2101
+ }
2102
+ setAssets(all);
2103
+ setError(null);
2104
+ } catch {
2105
+ setError("Failed to load press kit");
2106
+ } finally {
2107
+ setLoading(false);
2108
+ }
2109
+ }, [client, resolvedProductId]);
2110
+ React.useEffect(() => {
2111
+ fetchAssets();
2112
+ }, [fetchAssets]);
2113
+ const fetchReleases = React.useCallback(async () => {
2114
+ if (!resolvedProductId || !showPressReleases) {
2115
+ setReleasesLoading(false);
2116
+ return;
2117
+ }
2118
+ setReleasesLoading(true);
2119
+ try {
2120
+ const PAGE_SIZE = 100;
2121
+ const MAX_PAGES = 10;
2122
+ const all = [];
2123
+ let offset = 0;
2124
+ for (let pageNum = 0; pageNum < MAX_PAGES; pageNum++) {
2125
+ const result = await client.query(PRESS_RELEASES_QUERY, {
2126
+ productId: resolvedProductId,
2127
+ limit: PAGE_SIZE,
2128
+ offset
2129
+ });
2130
+ if (result.errors?.length) {
2131
+ setReleases([]);
2132
+ return;
2133
+ }
2134
+ const page = result.data?.publicContentPages;
2135
+ const items = page?.items || [];
2136
+ all.push(...items);
2137
+ if (!page?.hasMore || items.length === 0) break;
2138
+ offset += PAGE_SIZE;
2139
+ }
2140
+ const pressOnly = all.filter((p) => p.tags?.includes("press")).sort((a, b) => {
2141
+ const da = new Date(a.publishedAt || a.lastUpdated || 0).getTime();
2142
+ const db = new Date(b.publishedAt || b.lastUpdated || 0).getTime();
2143
+ return db - da;
2144
+ });
2145
+ setReleases(pressOnly);
2146
+ } catch {
2147
+ setReleases([]);
2148
+ } finally {
2149
+ setReleasesLoading(false);
2150
+ }
2151
+ }, [client, resolvedProductId, showPressReleases]);
2152
+ React.useEffect(() => {
2153
+ fetchReleases();
2154
+ }, [fetchReleases]);
2155
+ const presentCategories = CATEGORY_ORDER.filter(
2156
+ (c) => assets.some((a) => a.category === c)
2157
+ );
2158
+ const visibleAssets = activeCategory === "ALL" ? assets : assets.filter((a) => a.category === activeCategory);
2159
+ const grouped = (activeCategory === "ALL" ? presentCategories : [activeCategory]).map((cat) => ({
2160
+ cat,
2161
+ items: visibleAssets.filter((a) => a.category === cat).sort((a, b) => a.sortOrder - b.sortOrder)
2162
+ })).filter((g) => g.items.length > 0);
2163
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `w-full ${className}`, children: [
2164
+ seoTitle && /* @__PURE__ */ jsxRuntime.jsx(
2165
+ PageHead,
2166
+ {
2167
+ title: seoTitle,
2168
+ description: seoDescription || heroSubtitle,
2169
+ productName,
2170
+ keywords: seoKeywords
2171
+ }
2172
+ ),
2173
+ /* @__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: [
2174
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-3xl sm:text-4xl md:text-5xl font-bold tracking-tight text-foreground", children: heroTitle }),
2175
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-4 text-lg text-muted-foreground", children: heroSubtitle }),
2176
+ (brandKitUrl || contactEmail) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-8 flex flex-wrap items-center justify-center gap-4", children: [
2177
+ brandKitUrl && /* @__PURE__ */ jsxRuntime.jsx(
2178
+ "a",
2179
+ {
2180
+ href: brandKitUrl,
2181
+ target: "_blank",
2182
+ rel: "noopener noreferrer",
2183
+ 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",
2184
+ children: "Download brand kit"
2185
+ }
2186
+ ),
2187
+ contactEmail && /* @__PURE__ */ jsxRuntime.jsx(
2188
+ "a",
2189
+ {
2190
+ href: `mailto:${contactEmail}`,
2191
+ className: "inline-flex items-center gap-2 px-6 py-3 rounded-lg border font-medium text-foreground hover:bg-muted transition-colors",
2192
+ children: "Media inquiries"
2193
+ }
2194
+ )
2195
+ ] })
2196
+ ] }) }) }),
2197
+ 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: [
2198
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-xl font-semibold text-foreground mb-6", children: pressReleasesTitle }),
2199
+ /* @__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(
2200
+ "div",
2201
+ {
2202
+ className: "bg-card border rounded-xl overflow-hidden animate-pulse",
2203
+ children: [
2204
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] bg-muted" }),
2205
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-5 space-y-3", children: [
2206
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-1/4" }),
2207
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-5 bg-muted rounded w-3/4" }),
2208
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-1/2" })
2209
+ ] })
2210
+ ]
2211
+ },
2212
+ i
2213
+ )) : releases.map((r) => /* @__PURE__ */ jsxRuntime.jsx(
2214
+ PressReleaseCard,
2215
+ {
2216
+ release: r,
2217
+ basePath: pressReleaseBasePath
2218
+ },
2219
+ r.id
2220
+ )) })
2221
+ ] }) }),
2222
+ /* @__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(
2223
+ "div",
2224
+ {
2225
+ className: "bg-card border rounded-xl overflow-hidden animate-pulse",
2226
+ children: [
2227
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[16/9] bg-muted" }),
2228
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-5 space-y-3", children: [
2229
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-1/4" }),
2230
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-5 bg-muted rounded w-3/4" }),
2231
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 bg-muted rounded w-1/2" })
2232
+ ] })
2233
+ ]
2234
+ },
2235
+ i
2236
+ )) }) : 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: [
2237
+ "Press materials are coming soon. For media inquiries",
2238
+ contactEmail ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2239
+ ", contact ",
2240
+ /* @__PURE__ */ jsxRuntime.jsx(
2241
+ "a",
2242
+ {
2243
+ href: `mailto:${contactEmail}`,
2244
+ className: "text-primary hover:underline",
2245
+ children: contactEmail
2246
+ }
2247
+ )
2248
+ ] }) : " please reach out to our team",
2249
+ "."
2250
+ ] }) }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2251
+ presentCategories.length > 1 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-2 mb-10 justify-center", children: ["ALL", ...presentCategories].map((c) => {
2252
+ const active = activeCategory === c;
2253
+ return /* @__PURE__ */ jsxRuntime.jsx(
2254
+ "button",
2255
+ {
2256
+ type: "button",
2257
+ onClick: () => setActiveCategory(c),
2258
+ 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"}`,
2259
+ children: c === "ALL" ? "All" : CATEGORY_LABELS[c] ?? c
2260
+ },
2261
+ c
2262
+ );
2263
+ }) }),
2264
+ grouped.map((group) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-12", children: [
2265
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-xl font-semibold text-foreground mb-5", children: CATEGORY_LABELS[group.cat] ?? group.cat }),
2266
+ /* @__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)) })
2267
+ ] }, group.cat)),
2268
+ contactEmail && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-8 border-t pt-10 text-center", children: [
2269
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-semibold text-foreground mb-2", children: "Media Contact" }),
2270
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-muted-foreground", children: [
2271
+ "For interviews, partnerships, and press inquiries:",
2272
+ " ",
2273
+ /* @__PURE__ */ jsxRuntime.jsx(
2274
+ "a",
2275
+ {
2276
+ href: `mailto:${contactEmail}`,
2277
+ className: "text-primary hover:underline",
2278
+ children: contactEmail
2279
+ }
2280
+ )
2281
+ ] })
2282
+ ] })
2283
+ ] }) }) }),
2284
+ /* @__PURE__ */ jsxRuntime.jsx(PressKitLightbox, { asset: preview, onClose: () => setPreview(null) })
2285
+ ] });
2286
+ }
2287
+ var CONTRACTS_QUERY = `
2288
+ query PublicContracts($productId: String!, $limit: Int, $offset: Int) {
2289
+ publicPressKitAssets(productId: $productId, category: CONTRACT, limit: $limit, offset: $offset) {
2290
+ items {
2291
+ id
2292
+ title
2293
+ slug
2294
+ description
2295
+ fileUrl
2296
+ fileName
2297
+ fileSize
2298
+ mimeType
2299
+ version
2300
+ tags
2301
+ sortOrder
2302
+ updatedAt
2303
+ }
2304
+ hasMore
2305
+ }
2306
+ }
2307
+ `;
2308
+ function ContractsPage(props) {
2309
+ const {
2310
+ productName,
2311
+ heroTitle = "Contracts & Legal Agreements",
2312
+ heroSubtitle = "Download our standard legal agreements and policies.",
2313
+ className = "",
2314
+ seoTitle,
2315
+ seoDescription,
2316
+ seoKeywords
2317
+ } = props;
2318
+ const client = useWebSDK();
2319
+ const config = useWebSDKConfig();
2320
+ const { product } = useProduct();
2321
+ const resolvedProductId = product?.id || config.productId;
2322
+ const displayName = productName ?? product?.name ?? config.productId;
2323
+ const [contracts, setContracts] = React.useState([]);
2324
+ const [loading, setLoading] = React.useState(true);
2325
+ const [error, setError] = React.useState(null);
2326
+ const fetchContracts = React.useCallback(async () => {
2327
+ if (!resolvedProductId) {
2328
+ setLoading(false);
2329
+ return;
2330
+ }
2331
+ setLoading(true);
2332
+ try {
2333
+ const PAGE_SIZE = 100;
2334
+ const MAX_PAGES = 10;
2335
+ const all = [];
2336
+ let offset = 0;
2337
+ for (let pageNum = 0; pageNum < MAX_PAGES; pageNum++) {
2338
+ const result = await client.query(CONTRACTS_QUERY, {
2339
+ productId: resolvedProductId,
2340
+ limit: PAGE_SIZE,
2341
+ offset
2342
+ });
2343
+ if (result.errors?.length) {
2344
+ setError(result.errors[0]?.message ?? "Failed to load contracts");
2345
+ return;
2346
+ }
2347
+ const page = result.data?.publicPressKitAssets;
2348
+ const items = page?.items || [];
2349
+ all.push(...items);
2350
+ if (!page?.hasMore || items.length === 0) break;
2351
+ offset += PAGE_SIZE;
2352
+ }
2353
+ all.sort(
2354
+ (a, b) => a.sortOrder - b.sortOrder || a.title.localeCompare(b.title)
2355
+ );
2356
+ setContracts(all);
2357
+ setError(null);
2358
+ } catch {
2359
+ setError("Failed to load contracts");
2360
+ } finally {
2361
+ setLoading(false);
2362
+ }
2363
+ }, [client, resolvedProductId]);
2364
+ React.useEffect(() => {
2365
+ fetchContracts();
2366
+ }, [fetchContracts]);
2367
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `min-h-screen bg-background ${className}`, children: [
2368
+ /* @__PURE__ */ jsxRuntime.jsx(
2369
+ PageHead,
2370
+ {
2371
+ title: seoTitle || `Contracts \u2014 ${displayName}`,
2372
+ description: seoDescription || heroSubtitle,
2373
+ productName,
2374
+ keywords: seoKeywords
2375
+ }
2376
+ ),
2377
+ /* @__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: [
2378
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-4xl md:text-5xl font-bold text-foreground mb-4", children: heroTitle }),
2379
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-lg text-muted-foreground max-w-2xl mx-auto", children: heroSubtitle })
2380
+ ] }) }),
2381
+ /* @__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) => {
2382
+ const ext = fileExt(c);
2383
+ const size = formatBytes(c.fileSize);
2384
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2385
+ "li",
2386
+ {
2387
+ className: "flex items-center justify-between gap-4 p-5 hover:bg-muted/40 transition-colors",
2388
+ children: [
2389
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
2390
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [
2391
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-base font-semibold text-foreground truncate", children: c.title }),
2392
+ 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 })
2393
+ ] }),
2394
+ c.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground mt-1 line-clamp-2", children: c.description }),
2395
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1 flex items-center gap-2 text-xs text-muted-foreground", children: [
2396
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: ext }),
2397
+ size && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2398
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/40", children: "\xB7" }),
2399
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: size })
2400
+ ] })
2401
+ ] })
2402
+ ] }),
2403
+ /* @__PURE__ */ jsxRuntime.jsxs(
2404
+ "a",
2405
+ {
2406
+ href: c.fileUrl,
2407
+ target: "_blank",
2408
+ rel: "noopener noreferrer",
2409
+ download: true,
2410
+ "aria-label": `Download ${c.title}`,
2411
+ onClick: (e) => {
2412
+ void downloadFile(e, c);
2413
+ },
2414
+ 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",
2415
+ children: [
2416
+ /* @__PURE__ */ jsxRuntime.jsx(
2417
+ "svg",
2418
+ {
2419
+ className: "w-4 h-4",
2420
+ fill: "none",
2421
+ viewBox: "0 0 24 24",
2422
+ stroke: "currentColor",
2423
+ "aria-hidden": "true",
2424
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2425
+ "path",
2426
+ {
2427
+ strokeLinecap: "round",
2428
+ strokeLinejoin: "round",
2429
+ strokeWidth: 2,
2430
+ d: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
2431
+ }
2432
+ )
2433
+ }
2434
+ ),
2435
+ "Download"
2436
+ ]
2437
+ }
2438
+ )
2439
+ ]
2440
+ },
2441
+ c.id
2442
+ );
2443
+ }) }) })
2444
+ ] });
2445
+ }
2446
+
2447
+ exports.BlogListPage = BlogListPage;
2448
+ exports.BlogPostPage = BlogPostPage;
2449
+ exports.CaseStudiesListPage = CaseStudiesListPage;
2450
+ exports.CaseStudyPage = CaseStudyPage;
2451
+ exports.ChangelogPage = ChangelogPage;
2452
+ exports.ContentRenderer = ContentRenderer;
2453
+ exports.ContractsPage = ContractsPage;
2454
+ exports.PressKitPage = PressKitPage;
2455
+ exports.useContentPage = useContentPage;
2456
+ exports.useContentPages = useContentPages;
2457
+ //# sourceMappingURL=index.js.map
2458
+ //# sourceMappingURL=index.js.map