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