@burdenoff/website-sdk 2026.830.1 → 2026.922.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +114 -3
  2. package/dist/components/contact.js +4 -1
  3. package/dist/components/contact.js.map +1 -1
  4. package/dist/components/contact.mjs +4 -1
  5. package/dist/components/contact.mjs.map +1 -1
  6. package/dist/components/explore.d.mts +24 -3
  7. package/dist/components/explore.d.ts +24 -3
  8. package/dist/components/explore.js +690 -236
  9. package/dist/components/explore.js.map +1 -1
  10. package/dist/components/explore.mjs +690 -238
  11. package/dist/components/explore.mjs.map +1 -1
  12. package/dist/components/newsletter.js +4 -1
  13. package/dist/components/newsletter.js.map +1 -1
  14. package/dist/components/newsletter.mjs +4 -1
  15. package/dist/components/newsletter.mjs.map +1 -1
  16. package/dist/components/partners.js +23 -13
  17. package/dist/components/partners.js.map +1 -1
  18. package/dist/components/partners.mjs +23 -13
  19. package/dist/components/partners.mjs.map +1 -1
  20. package/dist/components/pricing.js +4 -1
  21. package/dist/components/pricing.js.map +1 -1
  22. package/dist/components/pricing.mjs +4 -1
  23. package/dist/components/pricing.mjs.map +1 -1
  24. package/dist/components/unsubscribe.js +4 -1
  25. package/dist/components/unsubscribe.js.map +1 -1
  26. package/dist/components/unsubscribe.mjs +4 -1
  27. package/dist/components/unsubscribe.mjs.map +1 -1
  28. package/dist/components/waitlist.js +4 -1
  29. package/dist/components/waitlist.js.map +1 -1
  30. package/dist/components/waitlist.mjs +4 -1
  31. package/dist/components/waitlist.mjs.map +1 -1
  32. package/dist/content/index.js +23 -13
  33. package/dist/content/index.js.map +1 -1
  34. package/dist/content/index.mjs +23 -13
  35. package/dist/content/index.mjs.map +1 -1
  36. package/dist/{use-explore-chat-B8xKGid5.d.mts → explore-types-CLw19R76.d.mts} +1 -143
  37. package/dist/{use-explore-chat-B8xKGid5.d.ts → explore-types-CLw19R76.d.ts} +1 -143
  38. package/dist/hooks/index.d.mts +2 -1
  39. package/dist/hooks/index.d.ts +2 -1
  40. package/dist/hooks/index.js +4 -1
  41. package/dist/hooks/index.js.map +1 -1
  42. package/dist/hooks/index.mjs +4 -1
  43. package/dist/hooks/index.mjs.map +1 -1
  44. package/dist/index.d.mts +141 -2
  45. package/dist/index.d.ts +141 -2
  46. package/dist/index.js +2025 -400
  47. package/dist/index.js.map +1 -1
  48. package/dist/index.mjs +2021 -403
  49. package/dist/index.mjs.map +1 -1
  50. package/dist/seo/index.js +4 -1
  51. package/dist/seo/index.js.map +1 -1
  52. package/dist/seo/index.mjs +4 -1
  53. package/dist/seo/index.mjs.map +1 -1
  54. package/dist/store/index.js +23 -13
  55. package/dist/store/index.js.map +1 -1
  56. package/dist/store/index.mjs +23 -13
  57. package/dist/store/index.mjs.map +1 -1
  58. package/dist/use-explore-chat-CvW-kyIm.d.mts +145 -0
  59. package/dist/use-explore-chat-DdGBimiz.d.ts +145 -0
  60. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -507,6 +507,9 @@ function resolveImageUrl(image, base) {
507
507
  return image;
508
508
  }
509
509
  }
510
+ function serializeJsonLd(data) {
511
+ return JSON.stringify(data).replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
512
+ }
510
513
  function PageHead({
511
514
  title,
512
515
  description,
@@ -548,7 +551,7 @@ function PageHead({
548
551
  fullImageUrl && /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "twitter:image", content: fullImageUrl }),
549
552
  twitterHandle && /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "twitter:creator", content: twitterHandle }),
550
553
  /* @__PURE__ */ jsxRuntime.jsx("meta", { name: "theme-color", content: themeColor }),
551
- structuredDataItems.map((data, index) => /* @__PURE__ */ jsxRuntime.jsx("script", { type: "application/ld+json", children: JSON.stringify(data) }, `ld-${index}`)),
554
+ structuredDataItems.map((data, index) => /* @__PURE__ */ jsxRuntime.jsx("script", { type: "application/ld+json", children: serializeJsonLd(data) }, `ld-${index}`)),
552
555
  additionalMeta?.map((meta, index) => /* @__PURE__ */ jsxRuntime.jsx(
553
556
  "meta",
554
557
  {
@@ -1108,19 +1111,26 @@ function ContactPage({
1108
1111
  ] }) })
1109
1112
  ] });
1110
1113
  }
1114
+ function MarkdownLink({
1115
+ href,
1116
+ children,
1117
+ // react-markdown passes the hast node to every override; it is not a DOM attribute.
1118
+ node: _node,
1119
+ ...props
1120
+ }) {
1121
+ const isExternal = typeof href === "string" && (href.startsWith("http://") || href.startsWith("https://"));
1122
+ return /* @__PURE__ */ jsxRuntime.jsx(
1123
+ "a",
1124
+ {
1125
+ href,
1126
+ ...props,
1127
+ ...isExternal ? { target: "_blank", rel: "noopener noreferrer" } : { rel: "noopener noreferrer" },
1128
+ children
1129
+ }
1130
+ );
1131
+ }
1111
1132
  var defaultComponents = {
1112
- a: ({ href, children, ...props }) => {
1113
- const isExternal = typeof href === "string" && (href.startsWith("http://") || href.startsWith("https://"));
1114
- return /* @__PURE__ */ jsxRuntime.jsx(
1115
- "a",
1116
- {
1117
- href,
1118
- ...props,
1119
- ...isExternal ? { target: "_blank", rel: "noopener noreferrer" } : { rel: "noopener noreferrer" },
1120
- children
1121
- }
1122
- );
1123
- }
1133
+ a: MarkdownLink
1124
1134
  };
1125
1135
  function Markdown({
1126
1136
  children,
@@ -4766,171 +4776,1343 @@ function ElectronDownloadLink({
4766
4776
  );
4767
4777
  }
4768
4778
 
4769
- // src/components/explore-i18n.ts
4770
- var EXPLORE_LOCALES = ["en", "zh", "hi", "es", "ar", "ta"];
4771
- var RTL_LOCALES = /* @__PURE__ */ new Set(["ar"]);
4772
- function isRtlLocale(locale) {
4773
- return RTL_LOCALES.has(locale);
4779
+ // src/components/concepts-types.ts
4780
+ var CONCEPT_ILLUSTRATION_NOTICE = "These images are illustrations of the concept, not screenshots of the actual product.";
4781
+ var CONCEPTS_PATH = "/concepts";
4782
+ var CONCEPT_PATH_PREFIX = "/concept";
4783
+ function conceptPath(slug) {
4784
+ return `${CONCEPT_PATH_PREFIX}/${encodeURIComponent(slug)}`;
4785
+ }
4786
+ function getConceptBySlug(concepts, slug) {
4787
+ if (!slug) return void 0;
4788
+ const wanted = slug.trim().toLowerCase();
4789
+ if (!wanted) return void 0;
4790
+ return concepts.find((concept) => concept.slug === wanted);
4791
+ }
4792
+ var NAV_BUTTON = "inline-flex h-11 w-11 items-center justify-center rounded-full border border-border bg-card text-foreground shadow-md transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring";
4793
+ function ConceptLightbox({
4794
+ images,
4795
+ index,
4796
+ conceptTitle,
4797
+ onIndexChange,
4798
+ onClose
4799
+ }) {
4800
+ const dialogRef = React.useRef(null);
4801
+ const closeRef = React.useRef(null);
4802
+ const nativeModalRef = React.useRef(false);
4803
+ const titleId = React.useId();
4804
+ const count = images.length;
4805
+ const position = count > 0 ? (index % count + count) % count : 0;
4806
+ const image = images[position];
4807
+ React.useEffect(() => {
4808
+ const dialog = dialogRef.current;
4809
+ if (!dialog) return;
4810
+ if (typeof dialog.showModal === "function") {
4811
+ if (!dialog.open) dialog.showModal();
4812
+ nativeModalRef.current = true;
4813
+ } else {
4814
+ dialog.setAttribute("open", "");
4815
+ nativeModalRef.current = false;
4816
+ }
4817
+ closeRef.current?.focus();
4818
+ return () => {
4819
+ if (dialog.open && typeof dialog.close === "function") dialog.close();
4820
+ };
4821
+ }, []);
4822
+ const go = React.useCallback(
4823
+ (delta) => {
4824
+ if (count < 2) return;
4825
+ onIndexChange((position + delta + count) % count);
4826
+ },
4827
+ [count, onIndexChange, position]
4828
+ );
4829
+ const handleClose = React.useCallback(
4830
+ (event) => {
4831
+ if (event.currentTarget.open) return;
4832
+ onClose();
4833
+ },
4834
+ [onClose]
4835
+ );
4836
+ const handleKeyDown = React.useCallback(
4837
+ (event) => {
4838
+ if (event.key === "ArrowRight") {
4839
+ event.preventDefault();
4840
+ go(1);
4841
+ } else if (event.key === "ArrowLeft") {
4842
+ event.preventDefault();
4843
+ go(-1);
4844
+ } else if (event.key === "Escape" && !nativeModalRef.current) {
4845
+ event.preventDefault();
4846
+ onClose();
4847
+ }
4848
+ },
4849
+ [go, onClose]
4850
+ );
4851
+ const handleStageClick = React.useCallback(
4852
+ (event) => {
4853
+ if (event.target === event.currentTarget) onClose();
4854
+ },
4855
+ [onClose]
4856
+ );
4857
+ if (!image) return null;
4858
+ return /* @__PURE__ */ jsxRuntime.jsx(
4859
+ "dialog",
4860
+ {
4861
+ ref: dialogRef,
4862
+ "data-concepts": "lightbox",
4863
+ "aria-labelledby": titleId,
4864
+ onClose: handleClose,
4865
+ onKeyDown: handleKeyDown,
4866
+ className: "fixed inset-0 z-50 m-0 h-full max-h-none w-full max-w-none overflow-y-auto overscroll-contain border-0 bg-background/95 p-0 text-foreground backdrop-blur-sm",
4867
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-h-full flex-col", children: [
4868
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-4 border-b border-border px-4 py-3 sm:px-6", children: [
4869
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
4870
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "truncate text-xs font-medium uppercase tracking-wide text-muted-foreground", children: [
4871
+ conceptTitle,
4872
+ count > 1 ? ` \xB7 ${position + 1} / ${count}` : null
4873
+ ] }),
4874
+ /* @__PURE__ */ jsxRuntime.jsx(
4875
+ "h2",
4876
+ {
4877
+ id: titleId,
4878
+ className: "truncate text-base font-semibold text-foreground sm:text-lg",
4879
+ children: image.title
4880
+ }
4881
+ )
4882
+ ] }),
4883
+ /* @__PURE__ */ jsxRuntime.jsx(
4884
+ "button",
4885
+ {
4886
+ ref: closeRef,
4887
+ type: "button",
4888
+ onClick: onClose,
4889
+ "aria-label": "Close",
4890
+ "data-concepts": "lightbox-close",
4891
+ className: "inline-flex h-10 w-10 shrink-0 items-center justify-center rounded-full border border-border bg-card text-foreground transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
4892
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { "aria-hidden": "true", className: "h-5 w-5" })
4893
+ }
4894
+ )
4895
+ ] }),
4896
+ /* @__PURE__ */ jsxRuntime.jsxs(
4897
+ "div",
4898
+ {
4899
+ className: "relative flex flex-1 items-center justify-center px-4 py-4 sm:px-20",
4900
+ onClick: handleStageClick,
4901
+ children: [
4902
+ /* @__PURE__ */ jsxRuntime.jsx(
4903
+ "img",
4904
+ {
4905
+ src: image.src,
4906
+ alt: image.alt,
4907
+ width: image.width,
4908
+ height: image.height,
4909
+ decoding: "async",
4910
+ className: "h-auto max-h-[calc(100vh-14rem)] w-auto max-w-full rounded-lg border border-border bg-muted object-contain shadow-lg"
4911
+ },
4912
+ image.key
4913
+ ),
4914
+ count > 1 ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4915
+ /* @__PURE__ */ jsxRuntime.jsx(
4916
+ "button",
4917
+ {
4918
+ type: "button",
4919
+ onClick: () => go(-1),
4920
+ "aria-label": "Previous illustration",
4921
+ "data-concepts": "lightbox-prev",
4922
+ className: `absolute left-2 top-1/2 -translate-y-1/2 sm:left-4 ${NAV_BUTTON}`,
4923
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { "aria-hidden": "true", className: "h-5 w-5" })
4924
+ }
4925
+ ),
4926
+ /* @__PURE__ */ jsxRuntime.jsx(
4927
+ "button",
4928
+ {
4929
+ type: "button",
4930
+ onClick: () => go(1),
4931
+ "aria-label": "Next illustration",
4932
+ "data-concepts": "lightbox-next",
4933
+ className: `absolute right-2 top-1/2 -translate-y-1/2 sm:right-4 ${NAV_BUTTON}`,
4934
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { "aria-hidden": "true", className: "h-5 w-5" })
4935
+ }
4936
+ )
4937
+ ] }) : null
4938
+ ]
4939
+ }
4940
+ ),
4941
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t border-border px-4 py-3 sm:px-6", children: [
4942
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm leading-relaxed text-foreground", children: image.caption }),
4943
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mt-1.5 flex items-start gap-1.5 text-xs leading-relaxed text-muted-foreground", children: [
4944
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Info, { "aria-hidden": "true", className: "mt-0.5 h-3.5 w-3.5 shrink-0" }),
4945
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: CONCEPT_ILLUSTRATION_NOTICE })
4946
+ ] })
4947
+ ] })
4948
+ ] })
4949
+ }
4950
+ );
4774
4951
  }
4775
- var EN = {
4776
- badge: "AI answers, grounded in our documentation",
4777
- welcomeTitle: "Ask anything about our products",
4778
- welcomeBody: "Describe what you are trying to do and I will point you at the product, the page and the next step.",
4779
- tryAsking: "Try asking",
4780
- focusProduct: "Focus on a product",
4781
- allProducts: "All products",
4782
- showAll: "All",
4783
- showLess: "Show less",
4784
- sources: "Sources",
4785
- nextYouCouldAsk: "Next you could ask",
4786
- placeholder: "Ask anything about our products\u2026",
4787
- placeholderProduct: "Ask about {product}\u2026",
4788
- send: "Send",
4789
- stop: "Stop",
4790
- newChat: "New chat",
4791
- history: "History",
4792
- clearHistory: "Clear history",
4793
- share: "Share",
4794
- copied: "Copied",
4795
- copying: "Copying\u2026",
4796
- shareUnavailable: "Unavailable",
4797
- disclaimer: "Disclaimer",
4798
- disclaimerShort: "Grounded in our docs \u2014 check anything important.",
4799
- dictate: "Dictate your question",
4800
- stopDictating: "Stop dictating",
4801
- attach: "Attach files",
4802
- attachHint: "Up to {{max}} files, {{size}} MB each",
4803
- removeAttachment: "Remove {{name}}",
4804
- attachmentNameOnly: "name and type only",
4805
- attachmentTooLarge: "{{name}} is larger than {{size}} MB and was not attached.",
4806
- attachmentTooMany: "Only {{max}} files can be attached, so {{name}} was skipped.",
4807
- noAnswer: "I couldn't find this in our documentation, so the answer below is not grounded in a source. Try rephrasing, or",
4808
- answerDidNotArrive: "That answer didn't come through. Try asking again.",
4809
- askHuman: "ask a human",
4810
- prevSuggestion: "Previous suggestion",
4811
- nextSuggestion: "Next suggestion",
4812
- capabilities: [
4813
- "Grounded in our own docs",
4814
- "Compare across the ecosystem",
4815
- "Straight to the right page"
4816
- ],
4817
- prompts: [
4818
- "What does this product actually do?",
4819
- "Which product fits my team?",
4820
- "How do I get started, step by step?",
4821
- "How much does it cost?"
4822
- ]
4823
- };
4824
- var ZH = {
4825
- badge: "\u57FA\u4E8E\u6211\u4EEC\u6587\u6863\u7684 AI \u56DE\u7B54",
4826
- welcomeTitle: "\u5173\u4E8E\u6211\u4EEC\u7684\u4EA7\u54C1\uFF0C\u968F\u4FBF\u95EE",
4827
- welcomeBody: "\u63CF\u8FF0\u4F60\u60F3\u505A\u7684\u4E8B\uFF0C\u6211\u4F1A\u4E3A\u4F60\u6307\u51FA\u5BF9\u5E94\u7684\u4EA7\u54C1\u3001\u9875\u9762\u548C\u4E0B\u4E00\u6B65\u3002",
4828
- tryAsking: "\u8BD5\u7740\u95EE",
4829
- focusProduct: "\u805A\u7126\u67D0\u4E2A\u4EA7\u54C1",
4830
- allProducts: "\u5168\u90E8\u4EA7\u54C1",
4831
- showAll: "\u5168\u90E8",
4832
- showLess: "\u6536\u8D77",
4833
- sources: "\u6765\u6E90",
4834
- nextYouCouldAsk: "\u4F60\u8FD8\u53EF\u4EE5\u95EE",
4835
- placeholder: "\u5173\u4E8E\u6211\u4EEC\u7684\u4EA7\u54C1\uFF0C\u968F\u4FBF\u95EE\u2026",
4836
- placeholderProduct: "\u8BE2\u95EE\u5173\u4E8E {product} \u7684\u95EE\u9898\u2026",
4837
- send: "\u53D1\u9001",
4838
- stop: "\u505C\u6B62",
4839
- newChat: "\u65B0\u5BF9\u8BDD",
4840
- history: "\u5386\u53F2",
4841
- clearHistory: "\u6E05\u9664\u5386\u53F2",
4842
- share: "\u5206\u4EAB",
4843
- copied: "\u5DF2\u590D\u5236",
4844
- copying: "\u590D\u5236\u4E2D\u2026",
4845
- shareUnavailable: "\u4E0D\u53EF\u7528",
4846
- disclaimer: "\u514D\u8D23\u58F0\u660E",
4847
- disclaimerShort: "\u5185\u5BB9\u57FA\u4E8E\u6211\u4EEC\u7684\u6587\u6863\uFF0C\u91CD\u8981\u4FE1\u606F\u8BF7\u81EA\u884C\u6838\u5BF9\u3002",
4848
- dictate: "\u8BED\u97F3\u8F93\u5165",
4849
- stopDictating: "\u505C\u6B62\u8BED\u97F3\u8F93\u5165",
4850
- attach: "\u6DFB\u52A0\u6587\u4EF6",
4851
- attachHint: "\u6700\u591A {{max}} \u4E2A\u6587\u4EF6\uFF0C\u6BCF\u4E2A\u4E0D\u8D85\u8FC7 {{size}} MB",
4852
- removeAttachment: "\u79FB\u9664 {{name}}",
4853
- attachmentNameOnly: "\u4EC5\u6587\u4EF6\u540D\u548C\u7C7B\u578B",
4854
- attachmentTooLarge: "{{name}} \u8D85\u8FC7 {{size}} MB\uFF0C\u672A\u80FD\u6DFB\u52A0\u3002",
4855
- attachmentTooMany: "\u6700\u591A\u53EA\u80FD\u6DFB\u52A0 {{max}} \u4E2A\u6587\u4EF6\uFF0C\u5DF2\u8DF3\u8FC7 {{name}}\u3002",
4856
- noAnswer: "\u6211\u5728\u6587\u6863\u4E2D\u6CA1\u6709\u627E\u5230\u76F8\u5173\u5185\u5BB9\uFF0C\u4E0B\u9762\u7684\u56DE\u7B54\u6CA1\u6709\u6765\u6E90\u652F\u6301\u3002\u8BF7\u6362\u4E2A\u8BF4\u6CD5\uFF0C\u6216",
4857
- answerDidNotArrive: "\u8FD9\u6761\u56DE\u7B54\u6CA1\u6709\u8FD4\u56DE\u3002\u8BF7\u518D\u95EE\u4E00\u6B21\u3002",
4858
- askHuman: "\u8054\u7CFB\u6211\u4EEC",
4859
- prevSuggestion: "\u4E0A\u4E00\u4E2A\u5EFA\u8BAE",
4860
- nextSuggestion: "\u4E0B\u4E00\u4E2A\u5EFA\u8BAE",
4861
- capabilities: ["\u57FA\u4E8E\u6211\u4EEC\u81EA\u5DF1\u7684\u6587\u6863", "\u8DE8\u4EA7\u54C1\u5BF9\u6BD4", "\u76F4\u8FBE\u6B63\u786E\u7684\u9875\u9762"],
4862
- prompts: [
4863
- "\u8FD9\u4E2A\u4EA7\u54C1\u5230\u5E95\u80FD\u505A\u4EC0\u4E48\uFF1F",
4864
- "\u54EA\u4E2A\u4EA7\u54C1\u9002\u5408\u6211\u7684\u56E2\u961F\uFF1F",
4865
- "\u5982\u4F55\u4E00\u6B65\u6B65\u5F00\u59CB\u4F7F\u7528\uFF1F",
4866
- "\u4EF7\u683C\u662F\u591A\u5C11\uFF1F"
4867
- ]
4868
- };
4869
- var HI = {
4870
- badge: "\u0939\u092E\u093E\u0930\u0947 \u0926\u0938\u094D\u0924\u093E\u0935\u0947\u091C\u093C\u094B\u0902 \u092A\u0930 \u0906\u0927\u093E\u0930\u093F\u0924 AI \u0909\u0924\u094D\u0924\u0930",
4871
- welcomeTitle: "\u0939\u092E\u093E\u0930\u0947 \u0909\u0924\u094D\u092A\u093E\u0926\u094B\u0902 \u0915\u0947 \u092C\u093E\u0930\u0947 \u092E\u0947\u0902 \u0915\u0941\u091B \u092D\u0940 \u092A\u0942\u091B\u0947\u0902",
4872
- welcomeBody: "\u092C\u0924\u093E\u0907\u090F \u0906\u092A \u0915\u094D\u092F\u093E \u0915\u0930\u0928\u093E \u091A\u093E\u0939\u0924\u0947 \u0939\u0948\u0902, \u0914\u0930 \u092E\u0948\u0902 \u0906\u092A\u0915\u094B \u0938\u0939\u0940 \u0909\u0924\u094D\u092A\u093E\u0926, \u092A\u0947\u091C \u0914\u0930 \u0905\u0917\u0932\u093E \u0915\u0926\u092E \u092C\u0924\u093E\u090A\u0901\u0917\u093E\u0964",
4873
- tryAsking: "\u092F\u0939 \u092A\u0942\u091B\u0915\u0930 \u0926\u0947\u0916\u0947\u0902",
4874
- focusProduct: "\u0915\u093F\u0938\u0940 \u0909\u0924\u094D\u092A\u093E\u0926 \u092A\u0930 \u0927\u094D\u092F\u093E\u0928 \u0926\u0947\u0902",
4875
- allProducts: "\u0938\u092D\u0940 \u0909\u0924\u094D\u092A\u093E\u0926",
4876
- showAll: "\u0938\u092D\u0940",
4877
- showLess: "\u0915\u092E \u0926\u093F\u0916\u093E\u090F\u0901",
4878
- sources: "\u0938\u094D\u0930\u094B\u0924",
4879
- nextYouCouldAsk: "\u0906\u0917\u0947 \u0906\u092A \u092A\u0942\u091B \u0938\u0915\u0924\u0947 \u0939\u0948\u0902",
4880
- placeholder: "\u0939\u092E\u093E\u0930\u0947 \u0909\u0924\u094D\u092A\u093E\u0926\u094B\u0902 \u0915\u0947 \u092C\u093E\u0930\u0947 \u092E\u0947\u0902 \u0915\u0941\u091B \u092D\u0940 \u092A\u0942\u091B\u0947\u0902\u2026",
4881
- placeholderProduct: "{product} \u0915\u0947 \u092C\u093E\u0930\u0947 \u092E\u0947\u0902 \u092A\u0942\u091B\u0947\u0902\u2026",
4882
- send: "\u092D\u0947\u091C\u0947\u0902",
4883
- stop: "\u0930\u094B\u0915\u0947\u0902",
4884
- newChat: "\u0928\u0908 \u092C\u093E\u0924\u091A\u0940\u0924",
4885
- history: "\u0907\u0924\u093F\u0939\u093E\u0938",
4886
- clearHistory: "\u0907\u0924\u093F\u0939\u093E\u0938 \u092E\u093F\u091F\u093E\u090F\u0901",
4887
- share: "\u0938\u093E\u091D\u093E \u0915\u0930\u0947\u0902",
4888
- copied: "\u0915\u0949\u092A\u0940 \u0939\u094B \u0917\u092F\u093E",
4889
- copying: "\u0915\u0949\u092A\u0940 \u0939\u094B \u0930\u0939\u093E \u0939\u0948\u2026",
4890
- shareUnavailable: "\u0909\u092A\u0932\u092C\u094D\u0927 \u0928\u0939\u0940\u0902",
4891
- disclaimer: "\u0905\u0938\u094D\u0935\u0940\u0915\u0930\u0923",
4892
- disclaimerShort: "\u092F\u0939 \u0939\u092E\u093E\u0930\u0947 \u0926\u0938\u094D\u0924\u093E\u0935\u0947\u091C\u093C\u094B\u0902 \u092A\u0930 \u0906\u0927\u093E\u0930\u093F\u0924 \u0939\u0948 \u2014 \u092E\u0939\u0924\u094D\u0935\u092A\u0942\u0930\u094D\u0923 \u092C\u093E\u0924\u0947\u0902 \u091C\u093C\u0930\u0942\u0930 \u091C\u093E\u0901\u091A \u0932\u0947\u0902\u0964",
4893
- dictate: "\u092C\u094B\u0932\u0915\u0930 \u092A\u0942\u091B\u0947\u0902",
4894
- stopDictating: "\u092C\u094B\u0932\u0928\u093E \u092C\u0902\u0926 \u0915\u0930\u0947\u0902",
4895
- attach: "\u092B\u093C\u093E\u0907\u0932\u0947\u0902 \u091C\u094B\u0921\u093C\u0947\u0902",
4896
- attachHint: "\u0905\u0927\u093F\u0915\u0924\u092E {{max}} \u092B\u093C\u093E\u0907\u0932\u0947\u0902, \u092A\u094D\u0930\u0924\u094D\u092F\u0947\u0915 {{size}} MB \u0924\u0915",
4897
- removeAttachment: "{{name}} \u0939\u091F\u093E\u090F\u0901",
4898
- attachmentNameOnly: "\u0915\u0947\u0935\u0932 \u0928\u093E\u092E \u0914\u0930 \u092A\u094D\u0930\u0915\u093E\u0930",
4899
- attachmentTooLarge: "{{name}} {{size}} MB \u0938\u0947 \u092C\u0921\u093C\u0940 \u0939\u0948, \u0907\u0938\u0932\u093F\u090F \u091C\u094B\u0921\u093C\u0940 \u0928\u0939\u0940\u0902 \u0917\u0908\u0964",
4900
- attachmentTooMany: "\u0915\u0947\u0935\u0932 {{max}} \u092B\u093C\u093E\u0907\u0932\u0947\u0902 \u091C\u094B\u0921\u093C\u0940 \u091C\u093E \u0938\u0915\u0924\u0940 \u0939\u0948\u0902, \u0907\u0938\u0932\u093F\u090F {{name}} \u091B\u094B\u0921\u093C \u0926\u0940 \u0917\u0908\u0964",
4901
- noAnswer: "\u092F\u0939 \u092E\u0941\u091D\u0947 \u0939\u092E\u093E\u0930\u0947 \u0926\u0938\u094D\u0924\u093E\u0935\u0947\u091C\u093C\u094B\u0902 \u092E\u0947\u0902 \u0928\u0939\u0940\u0902 \u092E\u093F\u0932\u093E, \u0907\u0938\u0932\u093F\u090F \u0928\u0940\u091A\u0947 \u0926\u093F\u092F\u093E \u0909\u0924\u094D\u0924\u0930 \u0915\u093F\u0938\u0940 \u0938\u094D\u0930\u094B\u0924 \u092A\u0930 \u0906\u0927\u093E\u0930\u093F\u0924 \u0928\u0939\u0940\u0902 \u0939\u0948\u0964 \u0926\u0942\u0938\u0930\u0947 \u0936\u092C\u094D\u0926\u094B\u0902 \u092E\u0947\u0902 \u092A\u0942\u091B\u0947\u0902, \u092F\u093E",
4902
- answerDidNotArrive: "\u0935\u0939 \u0909\u0924\u094D\u0924\u0930 \u0928\u0939\u0940\u0902 \u0906 \u0938\u0915\u093E\u0964 \u0915\u0943\u092A\u092F\u093E \u0926\u094B\u092C\u093E\u0930\u093E \u092A\u0942\u091B\u0947\u0902\u0964",
4903
- askHuman: "\u0939\u092E\u0938\u0947 \u0938\u0902\u092A\u0930\u094D\u0915 \u0915\u0930\u0947\u0902",
4904
- prevSuggestion: "\u092A\u093F\u091B\u0932\u093E \u0938\u0941\u091D\u093E\u0935",
4905
- nextSuggestion: "\u0905\u0917\u0932\u093E \u0938\u0941\u091D\u093E\u0935",
4906
- capabilities: [
4907
- "\u0939\u092E\u093E\u0930\u0947 \u0905\u092A\u0928\u0947 \u0926\u0938\u094D\u0924\u093E\u0935\u0947\u091C\u093C\u094B\u0902 \u092A\u0930 \u0906\u0927\u093E\u0930\u093F\u0924",
4908
- "\u0938\u092D\u0940 \u0909\u0924\u094D\u092A\u093E\u0926\u094B\u0902 \u0915\u0940 \u0924\u0941\u0932\u0928\u093E \u0915\u0930\u0947\u0902",
4909
- "\u0938\u0940\u0927\u0947 \u0938\u0939\u0940 \u092A\u0947\u091C \u092A\u0930"
4910
- ],
4911
- prompts: [
4912
- "\u092F\u0939 \u0909\u0924\u094D\u092A\u093E\u0926 \u0905\u0938\u0932 \u092E\u0947\u0902 \u0915\u094D\u092F\u093E \u0915\u0930\u0924\u093E \u0939\u0948?",
4913
- "\u092E\u0947\u0930\u0940 \u091F\u0940\u092E \u0915\u0947 \u0932\u093F\u090F \u0915\u094C\u0928 \u0938\u093E \u0909\u0924\u094D\u092A\u093E\u0926 \u0938\u0939\u0940 \u0939\u0948?",
4914
- "\u0936\u0941\u0930\u0941\u0906\u0924 \u0915\u0948\u0938\u0947 \u0915\u0930\u0947\u0902, \u091A\u0930\u0923 \u0926\u0930 \u091A\u0930\u0923?",
4915
- "\u0907\u0938\u0915\u0940 \u0915\u0940\u092E\u0924 \u0915\u093F\u0924\u0928\u0940 \u0939\u0948?"
4916
- ]
4917
- };
4918
- var ES = {
4919
- badge: "Respuestas de IA basadas en nuestra documentaci\xF3n",
4920
- welcomeTitle: "Pregunta lo que quieras sobre nuestros productos",
4921
- welcomeBody: "Cu\xE9ntame qu\xE9 necesitas hacer y te indicar\xE9 el producto, la p\xE1gina y el siguiente paso.",
4922
- tryAsking: "Prueba a preguntar",
4923
- focusProduct: "Centrarse en un producto",
4924
- allProducts: "Todos los productos",
4925
- showAll: "Todos",
4926
- showLess: "Ver menos",
4927
- sources: "Fuentes",
4928
- nextYouCouldAsk: "Tambi\xE9n puedes preguntar",
4929
- placeholder: "Pregunta lo que quieras sobre nuestros productos\u2026",
4930
- placeholderProduct: "Pregunta sobre {product}\u2026",
4931
- send: "Enviar",
4932
- stop: "Detener",
4933
- newChat: "Nueva conversaci\xF3n",
4952
+ var META_DESCRIPTION_LIMIT = 160;
4953
+ var RELATED_LIMIT = 4;
4954
+ var HERO_FETCH_PRIORITY = Number.parseInt(React.version, 10) >= 19 ? { fetchPriority: "high" } : { fetchpriority: "high" };
4955
+ function normaliseSiteUrl(siteUrl) {
4956
+ return siteUrl.trim().replace(/\/+$/, "");
4957
+ }
4958
+ function absoluteUrl(base, path) {
4959
+ if (/^https?:\/\//i.test(path)) return path;
4960
+ return `${base}${path.startsWith("/") ? path : `/${path}`}`;
4961
+ }
4962
+ function sortByOrder(concepts) {
4963
+ return [...concepts].sort(
4964
+ (a, b) => a.order - b.order || a.slug.localeCompare(b.slug)
4965
+ );
4966
+ }
4967
+ function plural(count, singular, pluralForm) {
4968
+ return `${count} ${count === 1 ? singular : pluralForm}`;
4969
+ }
4970
+ function illustrationCount(count) {
4971
+ return plural(count, "illustration", "illustrations");
4972
+ }
4973
+ function listSentence(items) {
4974
+ if (items.length <= 1) return items.join("");
4975
+ return `${items.slice(0, -1).join(", ")} and ${items[items.length - 1]}`;
4976
+ }
4977
+ function clampText(text, limit) {
4978
+ if (text.length <= limit) return text;
4979
+ const cut = text.slice(0, limit - 1);
4980
+ const lastSpace = cut.lastIndexOf(" ");
4981
+ return `${(lastSpace > 0 ? cut.slice(0, lastSpace) : cut).replace(/[\s,;:—–-]+$/, "")}\u2026`;
4982
+ }
4983
+ function defaultLandingDescription(productName, conceptTotal, imageTotal, categoryNames) {
4984
+ if (conceptTotal === 0) {
4985
+ return clampText(
4986
+ `Concepts of how ${productName} works, each a feature or flow explained with illustrations.`,
4987
+ META_DESCRIPTION_LIMIT
4988
+ );
4989
+ }
4990
+ const head = `Explore ${plural(conceptTotal, `${productName} concept`, `${productName} concepts`)} with ${illustrationCount(imageTotal)}`;
4991
+ for (let take = Math.min(categoryNames.length, 4); take >= 1; take -= 1) {
4992
+ const more = categoryNames.length > take ? " and more" : "";
4993
+ const text = `${head} across ${listSentence(categoryNames.slice(0, take))}${more}.`;
4994
+ if (text.length <= META_DESCRIPTION_LIMIT) return text;
4995
+ }
4996
+ return clampText(`${head} of how the product works.`, META_DESCRIPTION_LIMIT);
4997
+ }
4998
+ function slugifyAnchor(value) {
4999
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
5000
+ }
5001
+ function groupByCategory(ordered, categories) {
5002
+ const byName = /* @__PURE__ */ new Map();
5003
+ for (const concept of ordered) {
5004
+ const list = byName.get(concept.category);
5005
+ if (list) list.push(concept);
5006
+ else byName.set(concept.category, [concept]);
5007
+ }
5008
+ const described = [];
5009
+ for (const category of categories ?? []) {
5010
+ const list = byName.get(category.name);
5011
+ if (!list) continue;
5012
+ described.push({
5013
+ name: category.name,
5014
+ description: category.description,
5015
+ concepts: list
5016
+ });
5017
+ byName.delete(category.name);
5018
+ }
5019
+ for (const [name, list] of byName) {
5020
+ described.push({ name, description: "", concepts: list });
5021
+ }
5022
+ const used = /* @__PURE__ */ new Set();
5023
+ return described.map((group) => {
5024
+ const base = `concepts-${slugifyAnchor(group.name) || "category"}`;
5025
+ let anchor = base;
5026
+ for (let n = 2; used.has(anchor); n += 1) anchor = `${base}-${n}`;
5027
+ used.add(anchor);
5028
+ return { ...group, anchor };
5029
+ });
5030
+ }
5031
+ function searchTokens(query) {
5032
+ return query.toLowerCase().split(/\s+/).filter(Boolean);
5033
+ }
5034
+ function matchesSearch(concept, tokens) {
5035
+ if (tokens.length === 0) return true;
5036
+ const haystack = [
5037
+ concept.title,
5038
+ concept.tagline,
5039
+ concept.category,
5040
+ ...concept.keywords
5041
+ ].join(" ").toLowerCase();
5042
+ return tokens.every((token) => haystack.includes(token));
5043
+ }
5044
+ function isPortrait(width, height) {
5045
+ return height > width;
5046
+ }
5047
+ function copyKey(index, text) {
5048
+ return `${index}-${text}`;
5049
+ }
5050
+ function DefaultConceptLink({
5051
+ href,
5052
+ className,
5053
+ children,
5054
+ "aria-label": ariaLabel
5055
+ }) {
5056
+ return /* @__PURE__ */ jsxRuntime.jsx("a", { href, className, "aria-label": ariaLabel, children });
5057
+ }
5058
+ function IllustrationNotice({ className }) {
5059
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5060
+ "div",
5061
+ {
5062
+ role: "note",
5063
+ "data-concepts": "notice",
5064
+ "data-concepts-notice": "",
5065
+ className: cn(
5066
+ "flex items-start gap-3 rounded-xl border border-primary/30 bg-primary/10 px-4 py-3 text-sm leading-relaxed text-foreground",
5067
+ className
5068
+ ),
5069
+ children: [
5070
+ /* @__PURE__ */ jsxRuntime.jsx(
5071
+ lucideReact.Info,
5072
+ {
5073
+ "aria-hidden": "true",
5074
+ className: "mt-0.5 h-4 w-4 shrink-0 text-primary"
5075
+ }
5076
+ ),
5077
+ /* @__PURE__ */ jsxRuntime.jsx("p", { children: CONCEPT_ILLUSTRATION_NOTICE })
5078
+ ]
5079
+ }
5080
+ );
5081
+ }
5082
+ function IllustrationBadge({ className }) {
5083
+ return /* @__PURE__ */ jsxRuntime.jsx(
5084
+ "span",
5085
+ {
5086
+ "aria-hidden": "true",
5087
+ className: cn(
5088
+ "pointer-events-none absolute left-2 top-2 inline-flex items-center rounded-full border border-border bg-background/90 px-2 py-0.5 text-[11px] font-semibold uppercase tracking-wide text-foreground shadow-sm",
5089
+ className
5090
+ ),
5091
+ children: "Illustration"
5092
+ }
5093
+ );
5094
+ }
5095
+ function ConceptCard({
5096
+ concept,
5097
+ Link
5098
+ }) {
5099
+ const image = concept.images[0];
5100
+ const portrait = image ? isPortrait(image.thumbWidth, image.thumbHeight) : false;
5101
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5102
+ Link,
5103
+ {
5104
+ href: conceptPath(concept.slug),
5105
+ className: "group flex h-full flex-col overflow-hidden rounded-2xl border border-border bg-card text-card-foreground shadow-sm transition hover:-translate-y-0.5 hover:border-primary/40 hover:shadow-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring motion-reduce:transform-none motion-reduce:transition-none",
5106
+ children: [
5107
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative aspect-[16/10] overflow-hidden border-b border-border bg-muted", children: [
5108
+ image ? /* @__PURE__ */ jsxRuntime.jsx(
5109
+ "img",
5110
+ {
5111
+ src: image.thumb,
5112
+ alt: "",
5113
+ width: image.thumbWidth,
5114
+ height: image.thumbHeight,
5115
+ loading: "lazy",
5116
+ decoding: "async",
5117
+ className: cn(
5118
+ "h-full w-full",
5119
+ portrait ? "object-contain p-3" : "object-cover object-top"
5120
+ )
5121
+ }
5122
+ ) : null,
5123
+ /* @__PURE__ */ jsxRuntime.jsx(IllustrationBadge, {})
5124
+ ] }),
5125
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col gap-2 p-4", children: [
5126
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-base font-semibold leading-snug text-card-foreground transition-colors group-hover:text-primary", children: concept.title }),
5127
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "line-clamp-3 text-sm leading-relaxed text-muted-foreground", children: concept.tagline }),
5128
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "mt-auto inline-flex items-center gap-1.5 pt-1 text-xs font-medium text-muted-foreground", children: [
5129
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ImageIcon, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
5130
+ illustrationCount(concept.images.length)
5131
+ ] })
5132
+ ] })
5133
+ ]
5134
+ }
5135
+ );
5136
+ }
5137
+ function FilterChip({
5138
+ label,
5139
+ count,
5140
+ active,
5141
+ category,
5142
+ onSelect
5143
+ }) {
5144
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5145
+ "button",
5146
+ {
5147
+ type: "button",
5148
+ "aria-pressed": active,
5149
+ "data-category": category,
5150
+ onClick: onSelect,
5151
+ className: cn(
5152
+ "inline-flex items-center gap-2 rounded-full border px-3 py-1.5 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
5153
+ active ? "border-primary bg-primary text-primary-foreground" : "border-border bg-card text-foreground hover:border-primary/40 hover:bg-muted"
5154
+ ),
5155
+ children: [
5156
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: label }),
5157
+ /* @__PURE__ */ jsxRuntime.jsx(
5158
+ "span",
5159
+ {
5160
+ className: cn(
5161
+ "rounded-full px-1.5 text-xs tabular-nums",
5162
+ active ? "bg-primary-foreground/20 text-primary-foreground" : "bg-muted text-muted-foreground"
5163
+ ),
5164
+ children: count
5165
+ }
5166
+ )
5167
+ ]
5168
+ }
5169
+ );
5170
+ }
5171
+ function ConceptsPage({
5172
+ productName,
5173
+ siteUrl,
5174
+ concepts,
5175
+ categories,
5176
+ cta,
5177
+ linkComponent,
5178
+ title,
5179
+ description,
5180
+ className
5181
+ }) {
5182
+ const Link = linkComponent ?? DefaultConceptLink;
5183
+ const base = normaliseSiteUrl(siteUrl);
5184
+ const searchId = React.useId();
5185
+ const ordered = React.useMemo(() => sortByOrder(concepts), [concepts]);
5186
+ const groups = React.useMemo(
5187
+ () => groupByCategory(ordered, categories),
5188
+ [ordered, categories]
5189
+ );
5190
+ const imageTotal = React.useMemo(
5191
+ () => ordered.reduce((sum, concept) => sum + concept.images.length, 0),
5192
+ [ordered]
5193
+ );
5194
+ const [query, setQuery] = React.useState("");
5195
+ const [activeCategory, setActiveCategory] = React.useState(null);
5196
+ const hasConcepts = ordered.length > 0;
5197
+ const tokens = React.useMemo(() => searchTokens(query), [query]);
5198
+ const searched = React.useMemo(
5199
+ () => ordered.filter((concept) => matchesSearch(concept, tokens)),
5200
+ [ordered, tokens]
5201
+ );
5202
+ const countsByCategory = React.useMemo(() => {
5203
+ const counts = /* @__PURE__ */ new Map();
5204
+ for (const concept of searched) {
5205
+ counts.set(concept.category, (counts.get(concept.category) ?? 0) + 1);
5206
+ }
5207
+ return counts;
5208
+ }, [searched]);
5209
+ const visibleGroups = React.useMemo(() => {
5210
+ const visible = new Set(
5211
+ searched.filter(
5212
+ (concept) => activeCategory === null || concept.category === activeCategory
5213
+ )
5214
+ );
5215
+ return groups.map((group) => ({
5216
+ ...group,
5217
+ concepts: group.concepts.filter((concept) => visible.has(concept))
5218
+ })).filter((group) => group.concepts.length > 0);
5219
+ }, [groups, searched, activeCategory]);
5220
+ const visibleTotal = visibleGroups.reduce(
5221
+ (sum, group) => sum + group.concepts.length,
5222
+ 0
5223
+ );
5224
+ const filtering = tokens.length > 0 || activeCategory !== null;
5225
+ const clearFilters = React.useCallback(() => {
5226
+ setQuery("");
5227
+ setActiveCategory(null);
5228
+ }, []);
5229
+ const heading = title ?? `${productName} concepts`;
5230
+ const metaDescription = description ?? defaultLandingDescription(
5231
+ productName,
5232
+ ordered.length,
5233
+ imageTotal,
5234
+ groups.map((group) => group.name)
5235
+ );
5236
+ const lead = description ?? `A guided tour of ${productName}, one concept at a time. Each concept groups the illustrations of a feature or flow and explains what it does, how it works and who it is for.`;
5237
+ const canonical = `${base}${CONCEPTS_PATH}`;
5238
+ const firstImage = ordered.find((concept) => concept.images[0])?.images[0];
5239
+ const structuredData = React.useMemo(
5240
+ () => [
5241
+ {
5242
+ "@context": "https://schema.org",
5243
+ "@type": "CollectionPage",
5244
+ name: heading,
5245
+ description: metaDescription,
5246
+ url: canonical,
5247
+ isPartOf: { "@type": "WebSite", name: productName, url: `${base}/` },
5248
+ mainEntity: {
5249
+ "@type": "ItemList",
5250
+ numberOfItems: ordered.length,
5251
+ itemListElement: ordered.map((concept, index) => {
5252
+ const image = concept.images[0];
5253
+ return {
5254
+ "@type": "ListItem",
5255
+ position: index + 1,
5256
+ url: absoluteUrl(base, conceptPath(concept.slug)),
5257
+ name: concept.title,
5258
+ ...image ? { image: absoluteUrl(base, image.src) } : {}
5259
+ };
5260
+ })
5261
+ }
5262
+ },
5263
+ {
5264
+ "@context": "https://schema.org",
5265
+ "@type": "BreadcrumbList",
5266
+ itemListElement: [
5267
+ { "@type": "ListItem", position: 1, name: "Home", item: `${base}/` },
5268
+ {
5269
+ "@type": "ListItem",
5270
+ position: 2,
5271
+ name: "Concepts",
5272
+ item: canonical
5273
+ }
5274
+ ]
5275
+ }
5276
+ ],
5277
+ [base, canonical, heading, metaDescription, ordered, productName]
5278
+ );
5279
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5280
+ "div",
5281
+ {
5282
+ "data-concepts": "landing",
5283
+ className: cn("bg-background text-foreground", className),
5284
+ children: [
5285
+ /* @__PURE__ */ jsxRuntime.jsx(
5286
+ PageHead,
5287
+ {
5288
+ title: "Concepts",
5289
+ description: metaDescription,
5290
+ productName,
5291
+ keywords: [
5292
+ `${productName} concepts`,
5293
+ `${productName} illustrations`,
5294
+ ...groups.map((group) => group.name)
5295
+ ].join(", "),
5296
+ image: firstImage ? absoluteUrl(base, firstImage.src) : void 0,
5297
+ url: canonical,
5298
+ structuredData
5299
+ }
5300
+ ),
5301
+ /* @__PURE__ */ jsxRuntime.jsx("section", { className: "border-b border-border bg-muted/30", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto max-w-6xl px-4 py-14 sm:px-6 sm:py-20 lg:px-8", children: [
5302
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs font-semibold uppercase tracking-[0.2em] text-primary", children: "Concepts" }),
5303
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "mt-3 max-w-3xl text-4xl font-bold tracking-tight text-foreground sm:text-5xl", children: heading }),
5304
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-4 max-w-2xl text-lg leading-relaxed text-muted-foreground", children: lead }),
5305
+ hasConcepts ? /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mt-6 flex flex-wrap gap-x-6 gap-y-2 text-sm text-muted-foreground", children: [
5306
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
5307
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { className: "text-lg font-semibold text-foreground", children: ordered.length }),
5308
+ " ",
5309
+ ordered.length === 1 ? "concept" : "concepts"
5310
+ ] }),
5311
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
5312
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { className: "text-lg font-semibold text-foreground", children: imageTotal }),
5313
+ " ",
5314
+ imageTotal === 1 ? "illustration" : "illustrations"
5315
+ ] })
5316
+ ] }) : null,
5317
+ /* @__PURE__ */ jsxRuntime.jsx(IllustrationNotice, { className: "mt-6 max-w-2xl" }),
5318
+ cta ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-8 flex flex-wrap gap-3", children: cta }) : null
5319
+ ] }) }),
5320
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-auto max-w-6xl px-4 pb-20 sm:px-6 lg:px-8", children: hasConcepts ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5321
+ /* @__PURE__ */ jsxRuntime.jsxs(
5322
+ "div",
5323
+ {
5324
+ role: "search",
5325
+ "data-concepts": "filter",
5326
+ className: "flex flex-col gap-4 py-8",
5327
+ children: [
5328
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full max-w-md", children: [
5329
+ /* @__PURE__ */ jsxRuntime.jsx(
5330
+ lucideReact.Search,
5331
+ {
5332
+ "aria-hidden": "true",
5333
+ className: "pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground"
5334
+ }
5335
+ ),
5336
+ /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: searchId, className: "sr-only", children: "Search concepts" }),
5337
+ /* @__PURE__ */ jsxRuntime.jsx(
5338
+ "input",
5339
+ {
5340
+ id: searchId,
5341
+ type: "search",
5342
+ value: query,
5343
+ onChange: (event) => setQuery(event.target.value),
5344
+ placeholder: "Search concepts",
5345
+ autoComplete: "off",
5346
+ className: "h-10 w-full rounded-lg border border-border bg-background pl-9 pr-3 text-sm text-foreground shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
5347
+ }
5348
+ )
5349
+ ] }),
5350
+ /* @__PURE__ */ jsxRuntime.jsxs(
5351
+ "div",
5352
+ {
5353
+ role: "group",
5354
+ "aria-label": "Filter by category",
5355
+ className: "flex flex-wrap gap-2",
5356
+ children: [
5357
+ /* @__PURE__ */ jsxRuntime.jsx(
5358
+ FilterChip,
5359
+ {
5360
+ label: "All",
5361
+ category: "all",
5362
+ count: searched.length,
5363
+ active: activeCategory === null,
5364
+ onSelect: () => setActiveCategory(null)
5365
+ }
5366
+ ),
5367
+ groups.map((group) => /* @__PURE__ */ jsxRuntime.jsx(
5368
+ FilterChip,
5369
+ {
5370
+ label: group.name,
5371
+ category: group.name,
5372
+ count: countsByCategory.get(group.name) ?? 0,
5373
+ active: activeCategory === group.name,
5374
+ onSelect: () => setActiveCategory(
5375
+ (current) => current === group.name ? null : group.name
5376
+ )
5377
+ },
5378
+ group.anchor
5379
+ ))
5380
+ ]
5381
+ }
5382
+ ),
5383
+ /* @__PURE__ */ jsxRuntime.jsxs(
5384
+ "p",
5385
+ {
5386
+ "aria-live": "polite",
5387
+ className: filtering ? "text-sm text-muted-foreground" : "sr-only",
5388
+ children: [
5389
+ "Showing ",
5390
+ visibleTotal,
5391
+ " of ",
5392
+ ordered.length,
5393
+ " ",
5394
+ ordered.length === 1 ? "concept" : "concepts"
5395
+ ]
5396
+ }
5397
+ )
5398
+ ]
5399
+ }
5400
+ ),
5401
+ visibleTotal === 0 ? /* @__PURE__ */ jsxRuntime.jsxs(
5402
+ "div",
5403
+ {
5404
+ "data-concepts": "empty",
5405
+ className: "rounded-2xl border border-dashed border-border bg-muted/30 px-6 py-14 text-center",
5406
+ children: [
5407
+ /* @__PURE__ */ jsxRuntime.jsx(
5408
+ lucideReact.Search,
5409
+ {
5410
+ "aria-hidden": "true",
5411
+ className: "mx-auto h-8 w-8 text-muted-foreground"
5412
+ }
5413
+ ),
5414
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "mt-4 text-lg font-semibold text-foreground", children: "No concepts match your filters" }),
5415
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: "Try a different search, or show every category." }),
5416
+ /* @__PURE__ */ jsxRuntime.jsx(
5417
+ "button",
5418
+ {
5419
+ type: "button",
5420
+ onClick: clearFilters,
5421
+ className: "mt-6 inline-flex items-center rounded-full border border-border bg-card px-4 py-2 text-sm font-medium text-foreground transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
5422
+ children: "Clear filters"
5423
+ }
5424
+ )
5425
+ ]
5426
+ }
5427
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-14", children: visibleGroups.map((group) => /* @__PURE__ */ jsxRuntime.jsxs("section", { "aria-labelledby": group.anchor, children: [
5428
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-3xl", children: [
5429
+ /* @__PURE__ */ jsxRuntime.jsx(
5430
+ "h2",
5431
+ {
5432
+ id: group.anchor,
5433
+ className: "scroll-mt-24 text-2xl font-semibold tracking-tight text-foreground",
5434
+ children: group.name
5435
+ }
5436
+ ),
5437
+ group.description ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 leading-relaxed text-muted-foreground", children: group.description }) : null
5438
+ ] }),
5439
+ /* @__PURE__ */ jsxRuntime.jsx(
5440
+ "ul",
5441
+ {
5442
+ role: "list",
5443
+ className: "mt-6 grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-3",
5444
+ children: group.concepts.map((concept) => /* @__PURE__ */ jsxRuntime.jsx(
5445
+ "li",
5446
+ {
5447
+ "data-concepts": "card",
5448
+ "data-slug": concept.slug,
5449
+ children: /* @__PURE__ */ jsxRuntime.jsx(ConceptCard, { concept, Link })
5450
+ },
5451
+ concept.slug
5452
+ ))
5453
+ }
5454
+ )
5455
+ ] }, group.anchor)) })
5456
+ ] }) : (
5457
+ // Nothing published yet: no filter bar with an "All 0" chip, and no empty state
5458
+ // that blames filters the visitor never set.
5459
+ /* @__PURE__ */ jsxRuntime.jsxs(
5460
+ "div",
5461
+ {
5462
+ "data-concepts": "none",
5463
+ className: "mt-10 rounded-2xl border border-dashed border-border bg-muted/30 px-6 py-14 text-center",
5464
+ children: [
5465
+ /* @__PURE__ */ jsxRuntime.jsx(
5466
+ lucideReact.ImageIcon,
5467
+ {
5468
+ "aria-hidden": "true",
5469
+ className: "mx-auto h-8 w-8 text-muted-foreground"
5470
+ }
5471
+ ),
5472
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "mt-4 text-lg font-semibold text-foreground", children: "No concepts published yet" }),
5473
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mt-2 text-sm text-muted-foreground", children: [
5474
+ productName,
5475
+ " concepts will appear here as they are published."
5476
+ ] })
5477
+ ]
5478
+ }
5479
+ )
5480
+ ) })
5481
+ ]
5482
+ }
5483
+ );
5484
+ }
5485
+ var SECTION_HEADING = "scroll-mt-24 text-2xl font-semibold tracking-tight text-foreground";
5486
+ var CHIP = "inline-flex items-center rounded-full border border-border bg-muted/50 px-3 py-1 text-sm text-foreground";
5487
+ function IllustrationFigure({
5488
+ image,
5489
+ onOpen
5490
+ }) {
5491
+ const portrait = isPortrait(image.width, image.height);
5492
+ return (
5493
+ // The block the llms extractor reads: title, image line, caption, description — in
5494
+ // that order and with nothing in between. The description sits outside <figure>
5495
+ // because a <figcaption> must be the figure's first or last child.
5496
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
5497
+ /* @__PURE__ */ jsxRuntime.jsxs("figure", { id: `illustration-${image.key}`, className: "scroll-mt-24", children: [
5498
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-foreground", children: image.title }),
5499
+ /* @__PURE__ */ jsxRuntime.jsxs(
5500
+ "button",
5501
+ {
5502
+ type: "button",
5503
+ onClick: (event) => onOpen(event.currentTarget),
5504
+ "data-concepts": "illustration",
5505
+ className: cn(
5506
+ "group relative mt-3 block w-full overflow-hidden rounded-2xl border border-border shadow-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
5507
+ portrait ? "bg-muted p-4 sm:p-6" : "bg-muted"
5508
+ ),
5509
+ children: [
5510
+ /* @__PURE__ */ jsxRuntime.jsx(
5511
+ "img",
5512
+ {
5513
+ "data-llms-image": "",
5514
+ src: image.src,
5515
+ alt: image.alt,
5516
+ width: image.width,
5517
+ height: image.height,
5518
+ loading: "lazy",
5519
+ decoding: "async",
5520
+ className: "mx-auto block h-auto max-h-[80vh] w-auto max-w-full"
5521
+ }
5522
+ ),
5523
+ /* @__PURE__ */ jsxRuntime.jsx(IllustrationBadge, { className: "left-3 top-3" }),
5524
+ /* @__PURE__ */ jsxRuntime.jsx(
5525
+ "span",
5526
+ {
5527
+ "aria-hidden": "true",
5528
+ className: "absolute bottom-3 right-3 inline-flex h-8 w-8 items-center justify-center rounded-full border border-border bg-background/90 text-foreground shadow-sm transition-opacity group-hover:opacity-100 sm:opacity-80",
5529
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Maximize2, { className: "h-4 w-4" })
5530
+ }
5531
+ ),
5532
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Enlarge illustration" })
5533
+ ]
5534
+ }
5535
+ ),
5536
+ /* @__PURE__ */ jsxRuntime.jsx("figcaption", { className: "mt-3 text-sm font-medium text-foreground", children: image.caption })
5537
+ ] }),
5538
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "leading-relaxed text-muted-foreground", children: image.description })
5539
+ ] })
5540
+ );
5541
+ }
5542
+ function ConceptDetailPage(props) {
5543
+ return /* @__PURE__ */ jsxRuntime.jsx(ConceptDetail, { ...props }, props.concept.slug);
5544
+ }
5545
+ function ConceptDetail({
5546
+ productName,
5547
+ siteUrl,
5548
+ concept,
5549
+ concepts,
5550
+ categories,
5551
+ cta,
5552
+ linkComponent,
5553
+ className
5554
+ }) {
5555
+ const Link = linkComponent ?? DefaultConceptLink;
5556
+ const base = normaliseSiteUrl(siteUrl);
5557
+ const headingId = React.useId();
5558
+ const ordered = React.useMemo(() => sortByOrder(concepts), [concepts]);
5559
+ const position = ordered.findIndex((item) => item.slug === concept.slug);
5560
+ const previous = position > 0 ? ordered[position - 1] : void 0;
5561
+ const next = position >= 0 && position < ordered.length - 1 ? ordered[position + 1] : void 0;
5562
+ const related = React.useMemo(() => {
5563
+ const bySlug = new Map(ordered.map((item) => [item.slug, item]));
5564
+ const curated = [...new Set(concept.related)].map((slug) => bySlug.get(slug)).filter(
5565
+ (item) => item !== void 0 && item.slug !== concept.slug
5566
+ );
5567
+ const list = curated.length > 0 ? curated : ordered.filter(
5568
+ (item) => item.category === concept.category && item.slug !== concept.slug
5569
+ );
5570
+ return list.slice(0, RELATED_LIMIT);
5571
+ }, [concept, ordered]);
5572
+ const relatedIsCurated = concept.related.some(
5573
+ (slug) => related.some((item) => item.slug === slug)
5574
+ );
5575
+ const categoryDescription = categories?.find(
5576
+ (category) => category.name === concept.category
5577
+ )?.description;
5578
+ const [lightboxIndex, setLightboxIndex] = React.useState(null);
5579
+ const triggerRef = React.useRef(null);
5580
+ const openLightbox = React.useCallback((index, trigger) => {
5581
+ triggerRef.current = trigger;
5582
+ setLightboxIndex(index);
5583
+ }, []);
5584
+ const closeLightbox = React.useCallback(() => setLightboxIndex(null), []);
5585
+ React.useEffect(() => {
5586
+ if (lightboxIndex !== null) return;
5587
+ const trigger = triggerRef.current;
5588
+ if (!trigger) return;
5589
+ triggerRef.current = null;
5590
+ if (trigger.isConnected) trigger.focus();
5591
+ }, [lightboxIndex]);
5592
+ const canonical = absoluteUrl(base, conceptPath(concept.slug));
5593
+ const hero = concept.images[0];
5594
+ const imageTotal = concept.images.length;
5595
+ const structuredData = React.useMemo(
5596
+ () => [
5597
+ {
5598
+ "@context": "https://schema.org",
5599
+ "@type": "ImageGallery",
5600
+ name: concept.title,
5601
+ description: concept.metaDescription,
5602
+ url: canonical,
5603
+ keywords: concept.keywords.join(", "),
5604
+ about: { "@type": "Thing", name: productName },
5605
+ image: concept.images.map((image) => ({
5606
+ "@type": "ImageObject",
5607
+ contentUrl: absoluteUrl(base, image.src),
5608
+ thumbnailUrl: absoluteUrl(base, image.thumb),
5609
+ name: image.title,
5610
+ caption: image.caption,
5611
+ description: image.description,
5612
+ width: image.width,
5613
+ height: image.height
5614
+ }))
5615
+ },
5616
+ {
5617
+ "@context": "https://schema.org",
5618
+ "@type": "BreadcrumbList",
5619
+ itemListElement: [
5620
+ { "@type": "ListItem", position: 1, name: "Home", item: `${base}/` },
5621
+ {
5622
+ "@type": "ListItem",
5623
+ position: 2,
5624
+ name: "Concepts",
5625
+ item: `${base}${CONCEPTS_PATH}`
5626
+ },
5627
+ {
5628
+ "@type": "ListItem",
5629
+ position: 3,
5630
+ name: concept.title,
5631
+ item: canonical
5632
+ }
5633
+ ]
5634
+ }
5635
+ ],
5636
+ [base, canonical, concept, productName]
5637
+ );
5638
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5639
+ "article",
5640
+ {
5641
+ "data-concepts": "detail",
5642
+ "data-slug": concept.slug,
5643
+ "aria-labelledby": headingId,
5644
+ className: cn("bg-background text-foreground", className),
5645
+ children: [
5646
+ /* @__PURE__ */ jsxRuntime.jsx(
5647
+ PageHead,
5648
+ {
5649
+ title: `${concept.title} \u2014 Concept`,
5650
+ description: concept.metaDescription,
5651
+ productName,
5652
+ keywords: concept.keywords.join(", "),
5653
+ image: hero ? absoluteUrl(base, hero.src) : void 0,
5654
+ url: canonical,
5655
+ structuredData
5656
+ }
5657
+ ),
5658
+ /* @__PURE__ */ jsxRuntime.jsx("header", { className: "border-b border-border bg-muted/30", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto max-w-6xl px-4 pb-10 pt-8 sm:px-6 lg:px-8", children: [
5659
+ /* @__PURE__ */ jsxRuntime.jsx("nav", { "aria-label": "Breadcrumb", children: /* @__PURE__ */ jsxRuntime.jsxs("ol", { className: "flex flex-wrap items-center gap-1.5 text-sm text-muted-foreground", children: [
5660
+ /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "inline-flex items-center gap-1.5", children: [
5661
+ /* @__PURE__ */ jsxRuntime.jsx(
5662
+ Link,
5663
+ {
5664
+ href: "/",
5665
+ className: "rounded hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
5666
+ children: "Home"
5667
+ }
5668
+ ),
5669
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { "aria-hidden": "true", className: "h-3.5 w-3.5" })
5670
+ ] }),
5671
+ /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "inline-flex items-center gap-1.5", children: [
5672
+ /* @__PURE__ */ jsxRuntime.jsx(
5673
+ Link,
5674
+ {
5675
+ href: CONCEPTS_PATH,
5676
+ className: "rounded hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
5677
+ children: "Concepts"
5678
+ }
5679
+ ),
5680
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { "aria-hidden": "true", className: "h-3.5 w-3.5" })
5681
+ ] }),
5682
+ /* @__PURE__ */ jsxRuntime.jsx(
5683
+ "li",
5684
+ {
5685
+ "aria-current": "page",
5686
+ className: "min-w-0 truncate text-foreground",
5687
+ children: concept.title
5688
+ }
5689
+ )
5690
+ ] }) }),
5691
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-6 flex flex-wrap items-center gap-2", children: [
5692
+ /* @__PURE__ */ jsxRuntime.jsx(
5693
+ "span",
5694
+ {
5695
+ "data-concepts": "category",
5696
+ title: categoryDescription,
5697
+ className: "inline-flex items-center rounded-full border border-primary/30 bg-primary/10 px-3 py-1 text-xs font-semibold text-primary",
5698
+ children: concept.category
5699
+ }
5700
+ ),
5701
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1.5 rounded-full border border-border bg-background px-3 py-1 text-xs font-medium text-muted-foreground", children: [
5702
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ImageIcon, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
5703
+ illustrationCount(imageTotal)
5704
+ ] })
5705
+ ] }),
5706
+ /* @__PURE__ */ jsxRuntime.jsx(
5707
+ "h1",
5708
+ {
5709
+ id: headingId,
5710
+ className: "mt-4 max-w-4xl text-3xl font-bold tracking-tight text-foreground sm:text-4xl lg:text-5xl",
5711
+ children: concept.title
5712
+ }
5713
+ ),
5714
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-4 max-w-3xl text-lg leading-relaxed text-muted-foreground", children: concept.tagline }),
5715
+ /* @__PURE__ */ jsxRuntime.jsx(IllustrationNotice, { className: "mt-6 max-w-3xl" })
5716
+ ] }) }),
5717
+ hero ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-auto max-w-6xl px-4 pt-10 sm:px-6 lg:px-8", children: /* @__PURE__ */ jsxRuntime.jsx("figure", { "data-concepts": "hero", children: /* @__PURE__ */ jsxRuntime.jsxs(
5718
+ "button",
5719
+ {
5720
+ type: "button",
5721
+ onClick: (event) => openLightbox(0, event.currentTarget),
5722
+ className: cn(
5723
+ "group relative block w-full overflow-hidden rounded-2xl border border-border bg-muted shadow-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
5724
+ isPortrait(hero.width, hero.height) && "p-4 sm:p-6"
5725
+ ),
5726
+ children: [
5727
+ /* @__PURE__ */ jsxRuntime.jsx(
5728
+ "img",
5729
+ {
5730
+ src: hero.src,
5731
+ alt: hero.alt,
5732
+ width: hero.width,
5733
+ height: hero.height,
5734
+ loading: "eager",
5735
+ ...HERO_FETCH_PRIORITY,
5736
+ className: "mx-auto block h-auto max-h-[75vh] w-auto max-w-full"
5737
+ }
5738
+ ),
5739
+ /* @__PURE__ */ jsxRuntime.jsx(IllustrationBadge, { className: "left-3 top-3" }),
5740
+ /* @__PURE__ */ jsxRuntime.jsxs(
5741
+ "span",
5742
+ {
5743
+ "aria-hidden": "true",
5744
+ className: "absolute bottom-3 right-3 inline-flex items-center gap-1.5 rounded-full border border-border bg-background/90 px-3 py-1 text-xs font-medium text-foreground shadow-sm",
5745
+ children: [
5746
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Maximize2, { className: "h-3.5 w-3.5" }),
5747
+ "Enlarge"
5748
+ ]
5749
+ }
5750
+ ),
5751
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Enlarge illustration" })
5752
+ ]
5753
+ }
5754
+ ) }) }) : null,
5755
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto max-w-6xl px-4 py-14 sm:px-6 lg:px-8", children: [
5756
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-3xl space-y-12", children: [
5757
+ concept.overview.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("section", { "aria-labelledby": `${headingId}-overview`, children: [
5758
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { id: `${headingId}-overview`, className: SECTION_HEADING, children: "Overview" }),
5759
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-4 space-y-4 text-base leading-relaxed text-foreground", children: concept.overview.map((paragraph, index) => /* @__PURE__ */ jsxRuntime.jsx("p", { children: paragraph }, copyKey(index, paragraph))) })
5760
+ ] }) : null,
5761
+ concept.highlights.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("section", { "aria-labelledby": `${headingId}-highlights`, children: [
5762
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { id: `${headingId}-highlights`, className: SECTION_HEADING, children: "What this concept shows" }),
5763
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { role: "list", className: "mt-4 space-y-3", children: concept.highlights.map((highlight, index) => /* @__PURE__ */ jsxRuntime.jsxs(
5764
+ "li",
5765
+ {
5766
+ className: "flex items-start gap-3 leading-relaxed text-foreground",
5767
+ children: [
5768
+ /* @__PURE__ */ jsxRuntime.jsx(
5769
+ "span",
5770
+ {
5771
+ "aria-hidden": "true",
5772
+ className: "mt-0.5 inline-flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-primary/10 text-primary",
5773
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "h-3.5 w-3.5" })
5774
+ }
5775
+ ),
5776
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: highlight })
5777
+ ]
5778
+ },
5779
+ copyKey(index, highlight)
5780
+ )) })
5781
+ ] }) : null,
5782
+ concept.workflow.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("section", { "aria-labelledby": `${headingId}-workflow`, children: [
5783
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { id: `${headingId}-workflow`, className: SECTION_HEADING, children: "How it works" }),
5784
+ /* @__PURE__ */ jsxRuntime.jsx("ol", { role: "list", className: "mt-4 space-y-4", children: concept.workflow.map((step, index) => /* @__PURE__ */ jsxRuntime.jsxs(
5785
+ "li",
5786
+ {
5787
+ className: "flex items-start gap-4",
5788
+ children: [
5789
+ /* @__PURE__ */ jsxRuntime.jsx(
5790
+ "span",
5791
+ {
5792
+ "aria-hidden": "true",
5793
+ className: "inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-full border border-border bg-card text-sm font-semibold tabular-nums text-foreground",
5794
+ children: index + 1
5795
+ }
5796
+ ),
5797
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pt-0.5 leading-relaxed text-foreground", children: step })
5798
+ ]
5799
+ },
5800
+ copyKey(index, step)
5801
+ )) })
5802
+ ] }) : null,
5803
+ concept.audience.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("section", { "aria-labelledby": `${headingId}-audience`, children: [
5804
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { id: `${headingId}-audience`, className: SECTION_HEADING, children: "Who it's for" }),
5805
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { role: "list", className: "mt-4 flex flex-wrap gap-2", children: concept.audience.map((who, index) => /* @__PURE__ */ jsxRuntime.jsx("li", { className: CHIP, children: who }, copyKey(index, who))) })
5806
+ ] }) : null
5807
+ ] }),
5808
+ imageTotal > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(
5809
+ "section",
5810
+ {
5811
+ "aria-labelledby": `${headingId}-illustrations`,
5812
+ className: "mt-16",
5813
+ children: [
5814
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-3xl", children: [
5815
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { id: `${headingId}-illustrations`, className: SECTION_HEADING, children: "Illustrations" }),
5816
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mt-2 text-muted-foreground", children: [
5817
+ illustrationCount(imageTotal),
5818
+ " of this concept. Select one to view it full size."
5819
+ ] })
5820
+ ] }),
5821
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-8 space-y-14", children: concept.images.map((image, index) => /* @__PURE__ */ jsxRuntime.jsx(
5822
+ IllustrationFigure,
5823
+ {
5824
+ image,
5825
+ onOpen: (trigger) => openLightbox(index, trigger)
5826
+ },
5827
+ image.key
5828
+ )) })
5829
+ ]
5830
+ }
5831
+ ) : null,
5832
+ concept.keywords.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("section", { "aria-labelledby": `${headingId}-topics`, className: "mt-16", children: [
5833
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { id: `${headingId}-topics`, className: SECTION_HEADING, children: "Topics" }),
5834
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { role: "list", className: "mt-4 flex flex-wrap gap-2", children: concept.keywords.map((keyword, index) => /* @__PURE__ */ jsxRuntime.jsx("li", { className: CHIP, children: keyword }, copyKey(index, keyword))) })
5835
+ ] }) : null,
5836
+ related.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(
5837
+ "section",
5838
+ {
5839
+ "aria-labelledby": `${headingId}-related`,
5840
+ "data-concepts": "related",
5841
+ className: "mt-16",
5842
+ children: [
5843
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { id: `${headingId}-related`, className: SECTION_HEADING, children: relatedIsCurated ? "Related concepts" : `More in ${concept.category}` }),
5844
+ /* @__PURE__ */ jsxRuntime.jsx(
5845
+ "ul",
5846
+ {
5847
+ role: "list",
5848
+ className: cn(
5849
+ "mt-6 grid grid-cols-1 gap-5 sm:grid-cols-2",
5850
+ related.length >= 4 ? "lg:grid-cols-4" : "lg:grid-cols-3"
5851
+ ),
5852
+ children: related.map((item) => /* @__PURE__ */ jsxRuntime.jsx("li", { "data-concepts": "card", "data-slug": item.slug, children: /* @__PURE__ */ jsxRuntime.jsx(ConceptCard, { concept: item, Link }) }, item.slug))
5853
+ }
5854
+ )
5855
+ ]
5856
+ }
5857
+ ) : null,
5858
+ previous || next ? /* @__PURE__ */ jsxRuntime.jsxs(
5859
+ "nav",
5860
+ {
5861
+ "aria-label": "Previous and next concepts",
5862
+ "data-concepts": "pager",
5863
+ className: "mt-16 grid gap-4 sm:grid-cols-2",
5864
+ children: [
5865
+ previous ? /* @__PURE__ */ jsxRuntime.jsxs(
5866
+ Link,
5867
+ {
5868
+ href: conceptPath(previous.slug),
5869
+ className: "group flex items-center gap-3 rounded-2xl border border-border bg-card p-4 text-card-foreground transition-colors hover:border-primary/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
5870
+ children: [
5871
+ /* @__PURE__ */ jsxRuntime.jsx(
5872
+ lucideReact.ArrowLeft,
5873
+ {
5874
+ "aria-hidden": "true",
5875
+ className: "h-5 w-5 shrink-0 text-muted-foreground transition-colors group-hover:text-primary"
5876
+ }
5877
+ ),
5878
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "min-w-0", children: [
5879
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block text-xs font-medium uppercase tracking-wide text-muted-foreground", children: "Previous concept" }),
5880
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block truncate font-semibold", children: previous.title })
5881
+ ] })
5882
+ ]
5883
+ }
5884
+ ) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden sm:block" }),
5885
+ next ? /* @__PURE__ */ jsxRuntime.jsxs(
5886
+ Link,
5887
+ {
5888
+ href: conceptPath(next.slug),
5889
+ className: "group flex items-center justify-end gap-3 rounded-2xl border border-border bg-card p-4 text-right text-card-foreground transition-colors hover:border-primary/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
5890
+ children: [
5891
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "min-w-0", children: [
5892
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block text-xs font-medium uppercase tracking-wide text-muted-foreground", children: "Next concept" }),
5893
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block truncate font-semibold", children: next.title })
5894
+ ] }),
5895
+ /* @__PURE__ */ jsxRuntime.jsx(
5896
+ lucideReact.ArrowRight,
5897
+ {
5898
+ "aria-hidden": "true",
5899
+ className: "h-5 w-5 shrink-0 text-muted-foreground transition-colors group-hover:text-primary"
5900
+ }
5901
+ )
5902
+ ]
5903
+ }
5904
+ ) : null
5905
+ ]
5906
+ }
5907
+ ) : null,
5908
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-10", children: /* @__PURE__ */ jsxRuntime.jsxs(
5909
+ Link,
5910
+ {
5911
+ href: CONCEPTS_PATH,
5912
+ className: "inline-flex items-center gap-2 rounded text-sm font-medium text-primary hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
5913
+ children: [
5914
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowLeft, { "aria-hidden": "true", className: "h-4 w-4" }),
5915
+ "All ",
5916
+ productName,
5917
+ " concepts"
5918
+ ]
5919
+ }
5920
+ ) })
5921
+ ] }),
5922
+ cta ? /* @__PURE__ */ jsxRuntime.jsx(
5923
+ "section",
5924
+ {
5925
+ "data-concepts": "cta",
5926
+ className: "border-t border-border bg-muted/30",
5927
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-auto flex max-w-6xl flex-wrap items-center gap-4 px-4 py-12 sm:px-6 lg:px-8", children: cta })
5928
+ }
5929
+ ) : null,
5930
+ lightboxIndex !== null ? /* @__PURE__ */ jsxRuntime.jsx(
5931
+ ConceptLightbox,
5932
+ {
5933
+ images: concept.images,
5934
+ index: lightboxIndex,
5935
+ conceptTitle: concept.title,
5936
+ onIndexChange: setLightboxIndex,
5937
+ onClose: closeLightbox
5938
+ }
5939
+ ) : null
5940
+ ]
5941
+ }
5942
+ );
5943
+ }
5944
+
5945
+ // src/components/explore-i18n.ts
5946
+ var EXPLORE_LOCALES = ["en", "zh", "hi", "es", "ar", "ta"];
5947
+ var RTL_LOCALES = /* @__PURE__ */ new Set(["ar"]);
5948
+ function isRtlLocale(locale) {
5949
+ return RTL_LOCALES.has(locale);
5950
+ }
5951
+ var EN = {
5952
+ badge: "AI answers, grounded in our documentation",
5953
+ welcomeTitle: "Ask anything about our products",
5954
+ welcomeBody: "Describe what you are trying to do and I will point you at the product, the page and the next step.",
5955
+ tryAsking: "Try asking",
5956
+ focusProduct: "Focus on a product",
5957
+ allProducts: "All products",
5958
+ showAll: "All",
5959
+ showLess: "Show less",
5960
+ sources: "Sources",
5961
+ nextYouCouldAsk: "Next you could ask",
5962
+ placeholder: "Ask anything about our products\u2026",
5963
+ placeholderProduct: "Ask about {product}\u2026",
5964
+ send: "Send",
5965
+ stop: "Stop",
5966
+ newChat: "New chat",
5967
+ history: "History",
5968
+ clearHistory: "Clear history",
5969
+ share: "Share",
5970
+ copied: "Copied",
5971
+ copying: "Copying\u2026",
5972
+ shareUnavailable: "Unavailable",
5973
+ disclaimer: "Disclaimer",
5974
+ disclaimerShort: "Grounded in our docs \u2014 check anything important.",
5975
+ dictate: "Dictate your question",
5976
+ stopDictating: "Stop dictating",
5977
+ attach: "Attach files",
5978
+ attachHint: "Up to {{max}} files, {{size}} MB each",
5979
+ removeAttachment: "Remove {{name}}",
5980
+ attachmentNameOnly: "name and type only",
5981
+ attachmentTooLarge: "{{name}} is larger than {{size}} MB and was not attached.",
5982
+ attachmentTooMany: "Only {{max}} files can be attached, so {{name}} was skipped.",
5983
+ noAnswer: "I couldn't find this in our documentation, so the answer below is not grounded in a source. Try rephrasing, or",
5984
+ answerDidNotArrive: "That answer didn't come through. Try asking again.",
5985
+ askHuman: "ask a human",
5986
+ prevSuggestion: "Previous suggestion",
5987
+ nextSuggestion: "Next suggestion",
5988
+ conceptIllustration: "Concept illustration",
5989
+ opensInNewTab: "opens in a new tab",
5990
+ capabilities: [
5991
+ "Grounded in our own docs",
5992
+ "Compare across the ecosystem",
5993
+ "Straight to the right page"
5994
+ ],
5995
+ prompts: [
5996
+ "What does this product actually do?",
5997
+ "Which product fits my team?",
5998
+ "How do I get started, step by step?",
5999
+ "How much does it cost?"
6000
+ ]
6001
+ };
6002
+ var ZH = {
6003
+ badge: "\u57FA\u4E8E\u6211\u4EEC\u6587\u6863\u7684 AI \u56DE\u7B54",
6004
+ welcomeTitle: "\u5173\u4E8E\u6211\u4EEC\u7684\u4EA7\u54C1\uFF0C\u968F\u4FBF\u95EE",
6005
+ welcomeBody: "\u63CF\u8FF0\u4F60\u60F3\u505A\u7684\u4E8B\uFF0C\u6211\u4F1A\u4E3A\u4F60\u6307\u51FA\u5BF9\u5E94\u7684\u4EA7\u54C1\u3001\u9875\u9762\u548C\u4E0B\u4E00\u6B65\u3002",
6006
+ tryAsking: "\u8BD5\u7740\u95EE",
6007
+ focusProduct: "\u805A\u7126\u67D0\u4E2A\u4EA7\u54C1",
6008
+ allProducts: "\u5168\u90E8\u4EA7\u54C1",
6009
+ showAll: "\u5168\u90E8",
6010
+ showLess: "\u6536\u8D77",
6011
+ sources: "\u6765\u6E90",
6012
+ nextYouCouldAsk: "\u4F60\u8FD8\u53EF\u4EE5\u95EE",
6013
+ placeholder: "\u5173\u4E8E\u6211\u4EEC\u7684\u4EA7\u54C1\uFF0C\u968F\u4FBF\u95EE\u2026",
6014
+ placeholderProduct: "\u8BE2\u95EE\u5173\u4E8E {product} \u7684\u95EE\u9898\u2026",
6015
+ send: "\u53D1\u9001",
6016
+ stop: "\u505C\u6B62",
6017
+ newChat: "\u65B0\u5BF9\u8BDD",
6018
+ history: "\u5386\u53F2",
6019
+ clearHistory: "\u6E05\u9664\u5386\u53F2",
6020
+ share: "\u5206\u4EAB",
6021
+ copied: "\u5DF2\u590D\u5236",
6022
+ copying: "\u590D\u5236\u4E2D\u2026",
6023
+ shareUnavailable: "\u4E0D\u53EF\u7528",
6024
+ disclaimer: "\u514D\u8D23\u58F0\u660E",
6025
+ disclaimerShort: "\u5185\u5BB9\u57FA\u4E8E\u6211\u4EEC\u7684\u6587\u6863\uFF0C\u91CD\u8981\u4FE1\u606F\u8BF7\u81EA\u884C\u6838\u5BF9\u3002",
6026
+ dictate: "\u8BED\u97F3\u8F93\u5165",
6027
+ stopDictating: "\u505C\u6B62\u8BED\u97F3\u8F93\u5165",
6028
+ attach: "\u6DFB\u52A0\u6587\u4EF6",
6029
+ attachHint: "\u6700\u591A {{max}} \u4E2A\u6587\u4EF6\uFF0C\u6BCF\u4E2A\u4E0D\u8D85\u8FC7 {{size}} MB",
6030
+ removeAttachment: "\u79FB\u9664 {{name}}",
6031
+ attachmentNameOnly: "\u4EC5\u6587\u4EF6\u540D\u548C\u7C7B\u578B",
6032
+ attachmentTooLarge: "{{name}} \u8D85\u8FC7 {{size}} MB\uFF0C\u672A\u80FD\u6DFB\u52A0\u3002",
6033
+ attachmentTooMany: "\u6700\u591A\u53EA\u80FD\u6DFB\u52A0 {{max}} \u4E2A\u6587\u4EF6\uFF0C\u5DF2\u8DF3\u8FC7 {{name}}\u3002",
6034
+ noAnswer: "\u6211\u5728\u6587\u6863\u4E2D\u6CA1\u6709\u627E\u5230\u76F8\u5173\u5185\u5BB9\uFF0C\u4E0B\u9762\u7684\u56DE\u7B54\u6CA1\u6709\u6765\u6E90\u652F\u6301\u3002\u8BF7\u6362\u4E2A\u8BF4\u6CD5\uFF0C\u6216",
6035
+ answerDidNotArrive: "\u8FD9\u6761\u56DE\u7B54\u6CA1\u6709\u8FD4\u56DE\u3002\u8BF7\u518D\u95EE\u4E00\u6B21\u3002",
6036
+ askHuman: "\u8054\u7CFB\u6211\u4EEC",
6037
+ prevSuggestion: "\u4E0A\u4E00\u4E2A\u5EFA\u8BAE",
6038
+ nextSuggestion: "\u4E0B\u4E00\u4E2A\u5EFA\u8BAE",
6039
+ conceptIllustration: "\u6982\u5FF5\u793A\u610F\u56FE",
6040
+ opensInNewTab: "\u5728\u65B0\u6807\u7B7E\u9875\u4E2D\u6253\u5F00",
6041
+ capabilities: ["\u57FA\u4E8E\u6211\u4EEC\u81EA\u5DF1\u7684\u6587\u6863", "\u8DE8\u4EA7\u54C1\u5BF9\u6BD4", "\u76F4\u8FBE\u6B63\u786E\u7684\u9875\u9762"],
6042
+ prompts: [
6043
+ "\u8FD9\u4E2A\u4EA7\u54C1\u5230\u5E95\u80FD\u505A\u4EC0\u4E48\uFF1F",
6044
+ "\u54EA\u4E2A\u4EA7\u54C1\u9002\u5408\u6211\u7684\u56E2\u961F\uFF1F",
6045
+ "\u5982\u4F55\u4E00\u6B65\u6B65\u5F00\u59CB\u4F7F\u7528\uFF1F",
6046
+ "\u4EF7\u683C\u662F\u591A\u5C11\uFF1F"
6047
+ ]
6048
+ };
6049
+ var HI = {
6050
+ badge: "\u0939\u092E\u093E\u0930\u0947 \u0926\u0938\u094D\u0924\u093E\u0935\u0947\u091C\u093C\u094B\u0902 \u092A\u0930 \u0906\u0927\u093E\u0930\u093F\u0924 AI \u0909\u0924\u094D\u0924\u0930",
6051
+ welcomeTitle: "\u0939\u092E\u093E\u0930\u0947 \u0909\u0924\u094D\u092A\u093E\u0926\u094B\u0902 \u0915\u0947 \u092C\u093E\u0930\u0947 \u092E\u0947\u0902 \u0915\u0941\u091B \u092D\u0940 \u092A\u0942\u091B\u0947\u0902",
6052
+ welcomeBody: "\u092C\u0924\u093E\u0907\u090F \u0906\u092A \u0915\u094D\u092F\u093E \u0915\u0930\u0928\u093E \u091A\u093E\u0939\u0924\u0947 \u0939\u0948\u0902, \u0914\u0930 \u092E\u0948\u0902 \u0906\u092A\u0915\u094B \u0938\u0939\u0940 \u0909\u0924\u094D\u092A\u093E\u0926, \u092A\u0947\u091C \u0914\u0930 \u0905\u0917\u0932\u093E \u0915\u0926\u092E \u092C\u0924\u093E\u090A\u0901\u0917\u093E\u0964",
6053
+ tryAsking: "\u092F\u0939 \u092A\u0942\u091B\u0915\u0930 \u0926\u0947\u0916\u0947\u0902",
6054
+ focusProduct: "\u0915\u093F\u0938\u0940 \u0909\u0924\u094D\u092A\u093E\u0926 \u092A\u0930 \u0927\u094D\u092F\u093E\u0928 \u0926\u0947\u0902",
6055
+ allProducts: "\u0938\u092D\u0940 \u0909\u0924\u094D\u092A\u093E\u0926",
6056
+ showAll: "\u0938\u092D\u0940",
6057
+ showLess: "\u0915\u092E \u0926\u093F\u0916\u093E\u090F\u0901",
6058
+ sources: "\u0938\u094D\u0930\u094B\u0924",
6059
+ nextYouCouldAsk: "\u0906\u0917\u0947 \u0906\u092A \u092A\u0942\u091B \u0938\u0915\u0924\u0947 \u0939\u0948\u0902",
6060
+ placeholder: "\u0939\u092E\u093E\u0930\u0947 \u0909\u0924\u094D\u092A\u093E\u0926\u094B\u0902 \u0915\u0947 \u092C\u093E\u0930\u0947 \u092E\u0947\u0902 \u0915\u0941\u091B \u092D\u0940 \u092A\u0942\u091B\u0947\u0902\u2026",
6061
+ placeholderProduct: "{product} \u0915\u0947 \u092C\u093E\u0930\u0947 \u092E\u0947\u0902 \u092A\u0942\u091B\u0947\u0902\u2026",
6062
+ send: "\u092D\u0947\u091C\u0947\u0902",
6063
+ stop: "\u0930\u094B\u0915\u0947\u0902",
6064
+ newChat: "\u0928\u0908 \u092C\u093E\u0924\u091A\u0940\u0924",
6065
+ history: "\u0907\u0924\u093F\u0939\u093E\u0938",
6066
+ clearHistory: "\u0907\u0924\u093F\u0939\u093E\u0938 \u092E\u093F\u091F\u093E\u090F\u0901",
6067
+ share: "\u0938\u093E\u091D\u093E \u0915\u0930\u0947\u0902",
6068
+ copied: "\u0915\u0949\u092A\u0940 \u0939\u094B \u0917\u092F\u093E",
6069
+ copying: "\u0915\u0949\u092A\u0940 \u0939\u094B \u0930\u0939\u093E \u0939\u0948\u2026",
6070
+ shareUnavailable: "\u0909\u092A\u0932\u092C\u094D\u0927 \u0928\u0939\u0940\u0902",
6071
+ disclaimer: "\u0905\u0938\u094D\u0935\u0940\u0915\u0930\u0923",
6072
+ disclaimerShort: "\u092F\u0939 \u0939\u092E\u093E\u0930\u0947 \u0926\u0938\u094D\u0924\u093E\u0935\u0947\u091C\u093C\u094B\u0902 \u092A\u0930 \u0906\u0927\u093E\u0930\u093F\u0924 \u0939\u0948 \u2014 \u092E\u0939\u0924\u094D\u0935\u092A\u0942\u0930\u094D\u0923 \u092C\u093E\u0924\u0947\u0902 \u091C\u093C\u0930\u0942\u0930 \u091C\u093E\u0901\u091A \u0932\u0947\u0902\u0964",
6073
+ dictate: "\u092C\u094B\u0932\u0915\u0930 \u092A\u0942\u091B\u0947\u0902",
6074
+ stopDictating: "\u092C\u094B\u0932\u0928\u093E \u092C\u0902\u0926 \u0915\u0930\u0947\u0902",
6075
+ attach: "\u092B\u093C\u093E\u0907\u0932\u0947\u0902 \u091C\u094B\u0921\u093C\u0947\u0902",
6076
+ attachHint: "\u0905\u0927\u093F\u0915\u0924\u092E {{max}} \u092B\u093C\u093E\u0907\u0932\u0947\u0902, \u092A\u094D\u0930\u0924\u094D\u092F\u0947\u0915 {{size}} MB \u0924\u0915",
6077
+ removeAttachment: "{{name}} \u0939\u091F\u093E\u090F\u0901",
6078
+ attachmentNameOnly: "\u0915\u0947\u0935\u0932 \u0928\u093E\u092E \u0914\u0930 \u092A\u094D\u0930\u0915\u093E\u0930",
6079
+ attachmentTooLarge: "{{name}} {{size}} MB \u0938\u0947 \u092C\u0921\u093C\u0940 \u0939\u0948, \u0907\u0938\u0932\u093F\u090F \u091C\u094B\u0921\u093C\u0940 \u0928\u0939\u0940\u0902 \u0917\u0908\u0964",
6080
+ attachmentTooMany: "\u0915\u0947\u0935\u0932 {{max}} \u092B\u093C\u093E\u0907\u0932\u0947\u0902 \u091C\u094B\u0921\u093C\u0940 \u091C\u093E \u0938\u0915\u0924\u0940 \u0939\u0948\u0902, \u0907\u0938\u0932\u093F\u090F {{name}} \u091B\u094B\u0921\u093C \u0926\u0940 \u0917\u0908\u0964",
6081
+ noAnswer: "\u092F\u0939 \u092E\u0941\u091D\u0947 \u0939\u092E\u093E\u0930\u0947 \u0926\u0938\u094D\u0924\u093E\u0935\u0947\u091C\u093C\u094B\u0902 \u092E\u0947\u0902 \u0928\u0939\u0940\u0902 \u092E\u093F\u0932\u093E, \u0907\u0938\u0932\u093F\u090F \u0928\u0940\u091A\u0947 \u0926\u093F\u092F\u093E \u0909\u0924\u094D\u0924\u0930 \u0915\u093F\u0938\u0940 \u0938\u094D\u0930\u094B\u0924 \u092A\u0930 \u0906\u0927\u093E\u0930\u093F\u0924 \u0928\u0939\u0940\u0902 \u0939\u0948\u0964 \u0926\u0942\u0938\u0930\u0947 \u0936\u092C\u094D\u0926\u094B\u0902 \u092E\u0947\u0902 \u092A\u0942\u091B\u0947\u0902, \u092F\u093E",
6082
+ answerDidNotArrive: "\u0935\u0939 \u0909\u0924\u094D\u0924\u0930 \u0928\u0939\u0940\u0902 \u0906 \u0938\u0915\u093E\u0964 \u0915\u0943\u092A\u092F\u093E \u0926\u094B\u092C\u093E\u0930\u093E \u092A\u0942\u091B\u0947\u0902\u0964",
6083
+ askHuman: "\u0939\u092E\u0938\u0947 \u0938\u0902\u092A\u0930\u094D\u0915 \u0915\u0930\u0947\u0902",
6084
+ prevSuggestion: "\u092A\u093F\u091B\u0932\u093E \u0938\u0941\u091D\u093E\u0935",
6085
+ nextSuggestion: "\u0905\u0917\u0932\u093E \u0938\u0941\u091D\u093E\u0935",
6086
+ conceptIllustration: "\u0905\u0935\u0927\u093E\u0930\u0923\u093E \u091A\u093F\u0924\u094D\u0930\u0923",
6087
+ opensInNewTab: "\u0928\u090F \u091F\u0948\u092C \u092E\u0947\u0902 \u0916\u0941\u0932\u0924\u093E \u0939\u0948",
6088
+ capabilities: [
6089
+ "\u0939\u092E\u093E\u0930\u0947 \u0905\u092A\u0928\u0947 \u0926\u0938\u094D\u0924\u093E\u0935\u0947\u091C\u093C\u094B\u0902 \u092A\u0930 \u0906\u0927\u093E\u0930\u093F\u0924",
6090
+ "\u0938\u092D\u0940 \u0909\u0924\u094D\u092A\u093E\u0926\u094B\u0902 \u0915\u0940 \u0924\u0941\u0932\u0928\u093E \u0915\u0930\u0947\u0902",
6091
+ "\u0938\u0940\u0927\u0947 \u0938\u0939\u0940 \u092A\u0947\u091C \u092A\u0930"
6092
+ ],
6093
+ prompts: [
6094
+ "\u092F\u0939 \u0909\u0924\u094D\u092A\u093E\u0926 \u0905\u0938\u0932 \u092E\u0947\u0902 \u0915\u094D\u092F\u093E \u0915\u0930\u0924\u093E \u0939\u0948?",
6095
+ "\u092E\u0947\u0930\u0940 \u091F\u0940\u092E \u0915\u0947 \u0932\u093F\u090F \u0915\u094C\u0928 \u0938\u093E \u0909\u0924\u094D\u092A\u093E\u0926 \u0938\u0939\u0940 \u0939\u0948?",
6096
+ "\u0936\u0941\u0930\u0941\u0906\u0924 \u0915\u0948\u0938\u0947 \u0915\u0930\u0947\u0902, \u091A\u0930\u0923 \u0926\u0930 \u091A\u0930\u0923?",
6097
+ "\u0907\u0938\u0915\u0940 \u0915\u0940\u092E\u0924 \u0915\u093F\u0924\u0928\u0940 \u0939\u0948?"
6098
+ ]
6099
+ };
6100
+ var ES = {
6101
+ badge: "Respuestas de IA basadas en nuestra documentaci\xF3n",
6102
+ welcomeTitle: "Pregunta lo que quieras sobre nuestros productos",
6103
+ welcomeBody: "Cu\xE9ntame qu\xE9 necesitas hacer y te indicar\xE9 el producto, la p\xE1gina y el siguiente paso.",
6104
+ tryAsking: "Prueba a preguntar",
6105
+ focusProduct: "Centrarse en un producto",
6106
+ allProducts: "Todos los productos",
6107
+ showAll: "Todos",
6108
+ showLess: "Ver menos",
6109
+ sources: "Fuentes",
6110
+ nextYouCouldAsk: "Tambi\xE9n puedes preguntar",
6111
+ placeholder: "Pregunta lo que quieras sobre nuestros productos\u2026",
6112
+ placeholderProduct: "Pregunta sobre {product}\u2026",
6113
+ send: "Enviar",
6114
+ stop: "Detener",
6115
+ newChat: "Nueva conversaci\xF3n",
4934
6116
  history: "Historial",
4935
6117
  clearHistory: "Borrar historial",
4936
6118
  share: "Compartir",
@@ -4952,6 +6134,8 @@ var ES = {
4952
6134
  askHuman: "habla con una persona",
4953
6135
  prevSuggestion: "Sugerencia anterior",
4954
6136
  nextSuggestion: "Sugerencia siguiente",
6137
+ conceptIllustration: "Ilustraci\xF3n del concepto",
6138
+ opensInNewTab: "se abre en una pesta\xF1a nueva",
4955
6139
  capabilities: [
4956
6140
  "Basado en nuestra documentaci\xF3n",
4957
6141
  "Compara todo el ecosistema",
@@ -5001,6 +6185,8 @@ var AR = {
5001
6185
  askHuman: "\u062A\u062D\u062F\u0651\u062B \u0625\u0644\u0649 \u0634\u062E\u0635",
5002
6186
  prevSuggestion: "\u0627\u0644\u0627\u0642\u062A\u0631\u0627\u062D \u0627\u0644\u0633\u0627\u0628\u0642",
5003
6187
  nextSuggestion: "\u0627\u0644\u0627\u0642\u062A\u0631\u0627\u062D \u0627\u0644\u062A\u0627\u0644\u064A",
6188
+ conceptIllustration: "\u0631\u0633\u0645 \u062A\u0648\u0636\u064A\u062D\u064A \u0644\u0644\u0645\u0641\u0647\u0648\u0645",
6189
+ opensInNewTab: "\u064A\u064F\u0641\u062A\u062D \u0641\u064A \u0639\u0644\u0627\u0645\u0629 \u062A\u0628\u0648\u064A\u0628 \u062C\u062F\u064A\u062F\u0629",
5004
6190
  capabilities: [
5005
6191
  "\u0645\u0628\u0646\u064A \u0639\u0644\u0649 \u0648\u062B\u0627\u0626\u0642\u0646\u0627",
5006
6192
  "\u0642\u0627\u0631\u0646 \u0639\u0628\u0631 \u0627\u0644\u0645\u0646\u0638\u0648\u0645\u0629",
@@ -5050,6 +6236,8 @@ var TA = {
5050
6236
  askHuman: "\u0B8E\u0B99\u0BCD\u0B95\u0BB3\u0BC8\u0BA4\u0BCD \u0BA4\u0BCA\u0B9F\u0BB0\u0BCD\u0BAA\u0BC1 \u0B95\u0BCA\u0BB3\u0BCD\u0BB3\u0BC1\u0B99\u0BCD\u0B95\u0BB3\u0BCD",
5051
6237
  prevSuggestion: "\u0BAE\u0BC1\u0BA8\u0BCD\u0BA4\u0BC8\u0BAF \u0BAA\u0BB0\u0BBF\u0BA8\u0BCD\u0BA4\u0BC1\u0BB0\u0BC8",
5052
6238
  nextSuggestion: "\u0B85\u0B9F\u0BC1\u0BA4\u0BCD\u0BA4 \u0BAA\u0BB0\u0BBF\u0BA8\u0BCD\u0BA4\u0BC1\u0BB0\u0BC8",
6239
+ conceptIllustration: "\u0B95\u0BB0\u0BC1\u0BA4\u0BCD\u0BA4\u0BC1 \u0BB5\u0BBF\u0BB3\u0B95\u0BCD\u0B95\u0BAA\u0BCD\u0BAA\u0B9F\u0BAE\u0BCD",
6240
+ opensInNewTab: "\u0BAA\u0BC1\u0BA4\u0BBF\u0BAF \u0BA4\u0BBE\u0BB5\u0BB2\u0BBF\u0BB2\u0BCD \u0BA4\u0BBF\u0BB1\u0B95\u0BCD\u0B95\u0BC1\u0BAE\u0BCD",
5053
6241
  capabilities: [
5054
6242
  "\u0B8E\u0B99\u0BCD\u0B95\u0BB3\u0BCD \u0B9A\u0BCA\u0BA8\u0BCD\u0BA4 \u0B86\u0BB5\u0BA3\u0B99\u0BCD\u0B95\u0BB3\u0BBF\u0BA9\u0BCD \u0B85\u0B9F\u0BBF\u0BAA\u0BCD\u0BAA\u0B9F\u0BC8\u0BAF\u0BBF\u0BB2\u0BCD",
5055
6243
  "\u0BA4\u0BAF\u0BBE\u0BB0\u0BBF\u0BAA\u0BCD\u0BAA\u0BC1\u0B95\u0BB3\u0BC8 \u0B92\u0BAA\u0BCD\u0BAA\u0BBF\u0B9F\u0BC1\u0B99\u0BCD\u0B95\u0BB3\u0BCD",
@@ -5408,10 +6596,13 @@ var ECOSYSTEM_PRODUCTS = [
5408
6596
  name: "VibeControls",
5409
6597
  websiteUrl: "https://vibecontrols.com"
5410
6598
  },
6599
+ // FluidGrids ships on www.fluidgrids.ai — fluidgrids.com only redirects there. The explore
6600
+ // catalog and the site's own sitemap use this origin, and so must the fallback catalog
6601
+ // built from this list, or /explore rejects FluidGrids concept images until it loads.
5411
6602
  {
5412
6603
  slug: "fluidgrids",
5413
6604
  name: "FluidGrids",
5414
- websiteUrl: "https://fluidgrids.com"
6605
+ websiteUrl: "https://www.fluidgrids.ai"
5415
6606
  },
5416
6607
  // Botlit ships on botlit.ai — botlit.com does not resolve.
5417
6608
  { slug: "botlit", name: "Botlit", websiteUrl: "https://www.botlit.ai" },
@@ -6838,6 +8029,7 @@ function toPayload(staged) {
6838
8029
  textContent
6839
8030
  }));
6840
8031
  }
8032
+ var SHORT_VIEWPORT_PX = 420;
6841
8033
  var EXPLORE_CSS = `
6842
8034
  @keyframes boff-explore-pulse {
6843
8035
  0%, 80%, 100% { opacity: 0.25; transform: translateY(0); }
@@ -6872,6 +8064,12 @@ var EXPLORE_CSS = `
6872
8064
  .boff-explore-scroll {
6873
8065
  scrollbar-width: thin;
6874
8066
  scrollbar-color: hsl(var(--border)) transparent;
8067
+ /* Stop the transcript handing its scroll to the page. Without this, the first
8068
+ swipe that exhausts the transcript chains to the document \u2014 and on a host site
8069
+ that is 65-70% footer, which is how a phone ends up showing nothing but the
8070
+ footer. Engines that do not support this property ignore the line and keep
8071
+ today's behaviour; the scroll correction above covers them. */
8072
+ overscroll-behavior: contain;
6875
8073
  }
6876
8074
  .boff-explore-scroll::-webkit-scrollbar { width: 10px; }
6877
8075
  .boff-explore-scroll::-webkit-scrollbar-track { background: transparent; }
@@ -6989,6 +8187,68 @@ var EXPLORE_CSS = `
6989
8187
  .boff-explore-busy::after { animation: none; }
6990
8188
  .boff-explore-rise { animation: none; opacity: 1; transform: none; }
6991
8189
  }
8190
+
8191
+ /* ---- Pane height ----------------------------------------------------------------------
8192
+ This used to be one inline declaration, \`height: calc(100dvh - 64px)\`, and it carried two
8193
+ defects. An inline style cannot carry a fallback, so every engine without \`dvh\` (iOS
8194
+ Safari < 15.4, Chrome/WebView < 108, Samsung Internet < 21, Firefox < 101) dropped it
8195
+ whole; and the 64px came from a prop hard-coded to 64 on all 41 host pages that matches
8196
+ none of them \u2014 the burdenoff/algoshred chrome is 80px, the sticky-header sites are 65.
8197
+
8198
+ A class plus custom properties lets the cascade do the work: \`vh\` is the floor and \`dvh\`
8199
+ an upgrade where it exists, while the two measured values stay *lengths*, never a height.
8200
+ That distinction is the whole prerender contract. The host sites snapshot \`/explore\` by
8201
+ loading the built SPA in headless Chromium and writing the POST-HYDRATION DOM to
8202
+ dist/explore/index.html, so every attribute and inline value this component sets after
8203
+ mount IS the static HTML CloudFront serves. A measured pixel *height* would therefore
8204
+ hand every phone the prerender viewport's height until the bundle executes; a measured
8205
+ *offset* is a site-header height, which is the same on every device to within a few
8206
+ pixels, and the height built from it stays viewport-relative on the visitor's own screen.
8207
+
8208
+ --boff-explore-offset chrome above the pane (measured root top; the prop pre-measure)
8209
+ --boff-explore-inset the slice of the layout viewport hidden below the visible area \u2014
8210
+ the on-screen keyboard, which \`dvh\` does not track. Zero unless
8211
+ something is actually covering the bottom, so a snapshot taken
8212
+ with no keyboard open never carries it. */
8213
+ .boff-explore-viewport {
8214
+ height: calc(100vh - var(--boff-explore-offset, 0px) - var(--boff-explore-inset, 0px));
8215
+ }
8216
+ @supports (height: 100dvh) {
8217
+ .boff-explore-viewport {
8218
+ height: calc(100dvh - var(--boff-explore-offset, 0px) - var(--boff-explore-inset, 0px));
8219
+ }
8220
+ }
8221
+
8222
+ /* Landscape phones and other short viewports: a one-screen pane leaves a ~124px transcript
8223
+ sliver under ~610px of chrome and content. At or below the threshold the pane stops
8224
+ pretending to be a window and lets the page's own scrolling carry the transcript. The
8225
+ min-height is the available height rather than a fixed rem value, so the fallback can
8226
+ never be taller than the screen it is rescuing. */
8227
+ @media (max-height: ${SHORT_VIEWPORT_PX}px) {
8228
+ .boff-explore-viewport {
8229
+ height: auto;
8230
+ min-height: calc(100vh - var(--boff-explore-offset, 0px));
8231
+ }
8232
+ }
8233
+ @supports (height: 100dvh) {
8234
+ @media (max-height: ${SHORT_VIEWPORT_PX}px) {
8235
+ .boff-explore-viewport {
8236
+ min-height: calc(100dvh - var(--boff-explore-offset, 0px));
8237
+ }
8238
+ }
8239
+ }
8240
+
8241
+ /* ---- Composer dock --------------------------------------------------------------------
8242
+ Three declarations, each the fallback for the one after it: a plain 1rem where \`max()\`
8243
+ is unknown, then the measured overlap of any FOREIGN fixed chrome sitting over the
8244
+ composer (the host sites' cookie banner is 229px tall and lands exactly there), then the
8245
+ same plus the home-indicator inset. \`--boff-explore-chrome\` is set on the root only once
8246
+ an overlap has actually been measured, so everywhere else this collapses to 1rem. */
8247
+ .boff-explore-composer {
8248
+ padding-bottom: 1rem;
8249
+ padding-bottom: max(1rem, var(--boff-explore-chrome, 0px));
8250
+ padding-bottom: max(1rem, env(safe-area-inset-bottom), var(--boff-explore-chrome, 0px));
8251
+ }
6992
8252
  `;
6993
8253
  function ExploreStyles() {
6994
8254
  return /* @__PURE__ */ jsxRuntime.jsx("style", { dangerouslySetInnerHTML: { __html: EXPLORE_CSS } });
@@ -7010,6 +8270,8 @@ var CAPABILITIES = [
7010
8270
  body: "Get the sources and next steps \u2014 pricing, docs, a demo \u2014 without hunting."
7011
8271
  }
7012
8272
  ];
8273
+ var MIN_VISIBLE_FRACTION = 0.25;
8274
+ var SDK_OVERLAY_SELECTOR = "[data-boff-explore]";
7013
8275
  var CTA_VARIANTS = {
7014
8276
  CONTACT: "default",
7015
8277
  WAITLIST: "default",
@@ -7025,6 +8287,28 @@ function normalizeOffset(offset) {
7025
8287
  if (offset === void 0) return "0px";
7026
8288
  return typeof offset === "number" ? `${offset}px` : offset;
7027
8289
  }
8290
+ function isOwnElement(el, root) {
8291
+ if (el === document.body || el === document.documentElement) return true;
8292
+ if (root.contains(el)) return true;
8293
+ return el.closest(SDK_OVERLAY_SELECTOR) !== null;
8294
+ }
8295
+ function visualViewportHeight() {
8296
+ return window.visualViewport?.height ?? window.innerHeight;
8297
+ }
8298
+ function clamp(value, min, max) {
8299
+ return Math.min(Math.max(value, min), max);
8300
+ }
8301
+ function isRestoredNavigation() {
8302
+ const entries = window.performance?.getEntriesByType?.("navigation");
8303
+ const entry = entries?.[0];
8304
+ if (!entry) return true;
8305
+ const navigation = entry;
8306
+ return navigation.type === "reload" || navigation.type === "back_forward";
8307
+ }
8308
+ function pageOwnsScrolling(el) {
8309
+ if (window.innerHeight > SHORT_VIEWPORT_PX) return false;
8310
+ return el.scrollHeight - el.clientHeight <= 1;
8311
+ }
7028
8312
  function isInternalPath(url) {
7029
8313
  return url.startsWith("/") && !url.startsWith("//");
7030
8314
  }
@@ -7048,6 +8332,93 @@ function logoUrlOf(url) {
7048
8332
  return null;
7049
8333
  }
7050
8334
  }
8335
+ function inlineImageOrigins(products) {
8336
+ const origins = /* @__PURE__ */ new Set();
8337
+ for (const product of products) {
8338
+ let url;
8339
+ try {
8340
+ url = new URL(product.website);
8341
+ } catch {
8342
+ continue;
8343
+ }
8344
+ if (url.protocol !== "https:") continue;
8345
+ const apex = url.hostname.replace(/^www\./, "");
8346
+ const port = url.port ? `:${url.port}` : "";
8347
+ origins.add(`https://${apex}${port}`);
8348
+ origins.add(`https://www.${apex}${port}`);
8349
+ }
8350
+ return origins;
8351
+ }
8352
+ function isInlineImageAllowed(src, origins) {
8353
+ if (typeof src !== "string" || src === "") return false;
8354
+ let url;
8355
+ try {
8356
+ url = new URL(src);
8357
+ } catch {
8358
+ return false;
8359
+ }
8360
+ if (url.protocol !== "https:" || url.username || url.password) return false;
8361
+ return origins.has(url.origin);
8362
+ }
8363
+ var InlineImageScopeContext = React.createContext(null);
8364
+ var InsideLinkContext = React.createContext(false);
8365
+ function AnswerLink(props) {
8366
+ return /* @__PURE__ */ jsxRuntime.jsx(InsideLinkContext.Provider, { value: true, children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownLink, { ...props }) });
8367
+ }
8368
+ var INLINE_IMAGE_FRAME = "relative inline-block max-w-full overflow-hidden rounded-xl border border-border bg-muted align-top shadow-sm";
8369
+ function InlineConceptImage({ src, alt }) {
8370
+ const scope = React.useContext(InlineImageScopeContext);
8371
+ const insideLink = React.useContext(InsideLinkContext);
8372
+ if (!scope || !scope.enabled || !isInlineImageAllowed(src, scope.origins) || scope.failed.has(src)) {
8373
+ return null;
8374
+ }
8375
+ const { strings: T, onFailed } = scope;
8376
+ const picture = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
8377
+ /* @__PURE__ */ jsxRuntime.jsx(
8378
+ "img",
8379
+ {
8380
+ src,
8381
+ alt: alt ?? "",
8382
+ loading: "lazy",
8383
+ decoding: "async",
8384
+ onError: () => onFailed(src),
8385
+ className: "block h-auto max-h-80 w-auto max-w-full object-contain"
8386
+ }
8387
+ ),
8388
+ /* @__PURE__ */ jsxRuntime.jsx(
8389
+ "span",
8390
+ {
8391
+ "aria-hidden": "true",
8392
+ className: "pointer-events-none absolute left-2 top-2 rounded-full border border-border bg-background/90 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-foreground shadow-sm",
8393
+ children: T.conceptIllustration
8394
+ }
8395
+ )
8396
+ ] });
8397
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { "data-boff-explore": "inline-image", className: "my-3 block", children: insideLink ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: INLINE_IMAGE_FRAME, children: picture }) : /* @__PURE__ */ jsxRuntime.jsxs(
8398
+ "a",
8399
+ {
8400
+ href: src,
8401
+ target: "_blank",
8402
+ rel: "noopener noreferrer",
8403
+ className: cn(
8404
+ INLINE_IMAGE_FRAME,
8405
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
8406
+ ),
8407
+ children: [
8408
+ picture,
8409
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "sr-only", children: [
8410
+ "(",
8411
+ T.opensInNewTab,
8412
+ ")"
8413
+ ] })
8414
+ ]
8415
+ }
8416
+ ) });
8417
+ }
8418
+ var ANSWER_MARKDOWN_COMPONENTS = {
8419
+ a: AnswerLink,
8420
+ img: InlineConceptImage
8421
+ };
7051
8422
  function ReferenceCard({
7052
8423
  reference,
7053
8424
  onFollow
@@ -7230,6 +8601,7 @@ function AssistantBubble({
7230
8601
  strings: T,
7231
8602
  message,
7232
8603
  activity,
8604
+ imageOrigins,
7233
8605
  onNavigate,
7234
8606
  anchorRef,
7235
8607
  onFollow,
@@ -7238,6 +8610,25 @@ function AssistantBubble({
7238
8610
  const streaming = message.status === "PENDING" || message.status === "RUNNING";
7239
8611
  const body = message.content || message.partialContent || "";
7240
8612
  const failed = message.status === "FAILED";
8613
+ const completed = message.status === "COMPLETED";
8614
+ const [failedImages, setFailedImages] = React.useState(
8615
+ () => /* @__PURE__ */ new Set()
8616
+ );
8617
+ const markImageFailed = React.useCallback((src) => {
8618
+ setFailedImages(
8619
+ (current) => current.has(src) ? current : new Set(current).add(src)
8620
+ );
8621
+ }, []);
8622
+ const imageScope = React.useMemo(
8623
+ () => ({
8624
+ strings: T,
8625
+ enabled: completed,
8626
+ origins: imageOrigins,
8627
+ failed: failedImages,
8628
+ onFailed: markImageFailed
8629
+ }),
8630
+ [T, completed, imageOrigins, failedImages, markImageFailed]
8631
+ );
7241
8632
  return /* @__PURE__ */ jsxRuntime.jsxs(
7242
8633
  "div",
7243
8634
  {
@@ -7284,7 +8675,7 @@ function AssistantBubble({
7284
8675
  }
7285
8676
  ) : null,
7286
8677
  body ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-2xl rounded-tl-md border border-border bg-card px-4 py-3 shadow-sm", children: [
7287
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "boff-prose boff-prose-sm boff-explore-body min-w-0 text-card-foreground", children: /* @__PURE__ */ jsxRuntime.jsx(Markdown, { children: body }) }),
8678
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "boff-prose boff-prose-sm boff-explore-body min-w-0 text-card-foreground", children: /* @__PURE__ */ jsxRuntime.jsx(InlineImageScopeContext.Provider, { value: imageScope, children: /* @__PURE__ */ jsxRuntime.jsx(Markdown, { components: ANSWER_MARKDOWN_COMPONENTS, children: body }) }) }),
7288
8679
  streaming ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "boff-explore-caret", "aria-hidden": "true" }) : null
7289
8680
  ] }) : null,
7290
8681
  streaming ? /* @__PURE__ */ jsxRuntime.jsx(ThinkingIndicator, { activity }) : null,
@@ -7766,6 +9157,8 @@ function ExplorePage({
7766
9157
  const pickQueueRef = React.useRef(Promise.resolve());
7767
9158
  const [shareLabel, setShareLabel] = React.useState(T.share);
7768
9159
  const [shareUrl, setShareUrl] = React.useState(null);
9160
+ const rootRef = React.useRef(null);
9161
+ const composerRef = React.useRef(null);
7769
9162
  const scrollRef = React.useRef(null);
7770
9163
  const latestAssistantRef = React.useRef(null);
7771
9164
  const alignedForRef = React.useRef(null);
@@ -7783,17 +9176,206 @@ function ExplorePage({
7783
9176
  () => catalog.products.find((p) => p.slug === focusProduct)?.name ?? null,
7784
9177
  [catalog.products, focusProduct]
7785
9178
  );
9179
+ const imageOrigins = React.useMemo(
9180
+ () => inlineImageOrigins(catalog.products),
9181
+ [catalog.products]
9182
+ );
7786
9183
  React.useEffect(() => {
7787
9184
  const el = textareaRef.current;
7788
9185
  if (!el) return;
7789
9186
  el.style.height = "auto";
7790
9187
  el.style.height = `${Math.min(el.scrollHeight, 200)}px`;
7791
9188
  }, [draft]);
9189
+ const [paneOffset, setPaneOffset] = React.useState(null);
9190
+ const [bottomInset, setBottomInset] = React.useState(0);
9191
+ const paneOffsetRef = React.useRef(0);
9192
+ const measurePane = React.useCallback(() => {
9193
+ if (heightMode !== "viewport") return;
9194
+ const el = rootRef.current;
9195
+ if (!el) return;
9196
+ const layoutHeight = window.innerHeight;
9197
+ if (layoutHeight === 0) return;
9198
+ const top = Math.round(
9199
+ clamp(el.getBoundingClientRect().top, 0, layoutHeight)
9200
+ );
9201
+ const viewport = window.visualViewport;
9202
+ const inset = viewport ? Math.round(
9203
+ clamp(
9204
+ layoutHeight - viewport.height - viewport.offsetTop,
9205
+ 0,
9206
+ layoutHeight
9207
+ )
9208
+ ) : 0;
9209
+ paneOffsetRef.current = top;
9210
+ setPaneOffset((current) => current === top ? current : top);
9211
+ setBottomInset((current) => current === inset ? current : inset);
9212
+ }, [heightMode]);
9213
+ const [chromeOverlap, setChromeOverlap] = React.useState(0);
9214
+ const measureChrome = React.useCallback(() => {
9215
+ const root = rootRef.current;
9216
+ const dock = composerRef.current;
9217
+ if (!root || !dock) return;
9218
+ if (typeof document.elementFromPoint !== "function") return;
9219
+ const viewportHeight = visualViewportHeight();
9220
+ const rect = dock.getBoundingClientRect();
9221
+ if (rect.width === 0 || viewportHeight === 0) return;
9222
+ if (rect.bottom <= 0 || rect.top >= viewportHeight) return;
9223
+ const edge = Math.min(rect.bottom, viewportHeight);
9224
+ const hit = document.elementFromPoint(rect.left + rect.width / 2, edge - 1);
9225
+ if (!hit || isOwnElement(hit, root)) {
9226
+ setChromeOverlap(0);
9227
+ return;
9228
+ }
9229
+ const overlap = Math.round(edge - hit.getBoundingClientRect().top);
9230
+ setChromeOverlap(overlap > 0 && overlap < viewportHeight ? overlap : 0);
9231
+ }, []);
9232
+ const remeasure = React.useCallback(() => {
9233
+ measurePane();
9234
+ measureChrome();
9235
+ }, [measurePane, measureChrome]);
9236
+ React.useEffect(() => {
9237
+ if (typeof window === "undefined") return;
9238
+ let frame = 0;
9239
+ const run = () => {
9240
+ frame = 0;
9241
+ remeasure();
9242
+ };
9243
+ const schedule = () => {
9244
+ if (frame) return;
9245
+ if (typeof window.requestAnimationFrame !== "function") {
9246
+ remeasure();
9247
+ return;
9248
+ }
9249
+ frame = window.requestAnimationFrame(run);
9250
+ };
9251
+ remeasure();
9252
+ const viewport = window.visualViewport;
9253
+ window.addEventListener("resize", schedule);
9254
+ window.addEventListener("orientationchange", schedule);
9255
+ viewport?.addEventListener("resize", schedule);
9256
+ viewport?.addEventListener("scroll", schedule);
9257
+ const observer = typeof MutationObserver === "function" ? new MutationObserver((records) => {
9258
+ const el = rootRef.current;
9259
+ if (el && records.every((record) => el.contains(record.target)))
9260
+ return;
9261
+ schedule();
9262
+ }) : null;
9263
+ observer?.observe(document.body, { childList: true, subtree: true });
9264
+ return () => {
9265
+ if (frame) window.cancelAnimationFrame(frame);
9266
+ window.removeEventListener("resize", schedule);
9267
+ window.removeEventListener("orientationchange", schedule);
9268
+ viewport?.removeEventListener("resize", schedule);
9269
+ viewport?.removeEventListener("scroll", schedule);
9270
+ observer?.disconnect();
9271
+ setPaneOffset(null);
9272
+ setBottomInset(0);
9273
+ setChromeOverlap(0);
9274
+ };
9275
+ }, [remeasure]);
9276
+ React.useEffect(() => {
9277
+ measureChrome();
9278
+ }, [measureChrome, paneOffset, bottomInset]);
9279
+ React.useEffect(() => {
9280
+ if (heightMode !== "viewport" || typeof window === "undefined") return;
9281
+ const { history: browserHistory } = window;
9282
+ const previousRestoration = "scrollRestoration" in browserHistory ? browserHistory.scrollRestoration : null;
9283
+ if (previousRestoration !== null)
9284
+ browserHistory.scrollRestoration = "manual";
9285
+ let frame = 0;
9286
+ let visitorMoved = false;
9287
+ const onVisitorMove = () => {
9288
+ visitorMoved = true;
9289
+ };
9290
+ const align = () => {
9291
+ if (visitorMoved) return;
9292
+ const el = rootRef.current;
9293
+ if (!el) return;
9294
+ const viewportHeight = visualViewportHeight();
9295
+ const rect = el.getBoundingClientRect();
9296
+ if (rect.height === 0 || viewportHeight === 0) return;
9297
+ const visible = Math.min(rect.bottom, viewportHeight) - Math.max(rect.top, 0);
9298
+ const reachable = Math.min(rect.height, viewportHeight);
9299
+ if (visible >= reachable * MIN_VISIBLE_FRACTION) return;
9300
+ window.scrollTo({
9301
+ top: Math.max(0, window.scrollY + rect.top),
9302
+ left: window.scrollX,
9303
+ behavior: "auto"
9304
+ });
9305
+ if (typeof document.elementFromPoint === "function") {
9306
+ const probe = document.elementFromPoint(
9307
+ Math.max(0, rect.left + rect.width / 2),
9308
+ 1
9309
+ );
9310
+ if (probe && !isOwnElement(probe, el)) {
9311
+ const chrome = probe.getBoundingClientRect().bottom;
9312
+ if (chrome > 0 && chrome < viewportHeight / 2) {
9313
+ window.scrollBy({ top: -chrome, left: 0, behavior: "auto" });
9314
+ }
9315
+ }
9316
+ }
9317
+ remeasure();
9318
+ };
9319
+ const alignIfRestored = () => {
9320
+ if (!isRestoredNavigation()) return;
9321
+ align();
9322
+ };
9323
+ alignIfRestored();
9324
+ if (typeof window.requestAnimationFrame === "function") {
9325
+ frame = window.requestAnimationFrame(alignIfRestored);
9326
+ }
9327
+ const onPageShow = (event) => {
9328
+ if (!event.persisted) {
9329
+ alignIfRestored();
9330
+ return;
9331
+ }
9332
+ visitorMoved = false;
9333
+ align();
9334
+ };
9335
+ window.addEventListener("pageshow", onPageShow);
9336
+ window.addEventListener("wheel", onVisitorMove, { passive: true });
9337
+ window.addEventListener("touchmove", onVisitorMove, { passive: true });
9338
+ window.addEventListener("keydown", onVisitorMove, { passive: true });
9339
+ return () => {
9340
+ if (frame) window.cancelAnimationFrame(frame);
9341
+ window.removeEventListener("pageshow", onPageShow);
9342
+ window.removeEventListener("wheel", onVisitorMove);
9343
+ window.removeEventListener("touchmove", onVisitorMove);
9344
+ window.removeEventListener("keydown", onVisitorMove);
9345
+ if (previousRestoration !== null)
9346
+ browserHistory.scrollRestoration = previousRestoration;
9347
+ };
9348
+ }, [heightMode, remeasure]);
7792
9349
  const handleScroll = React.useCallback(() => {
7793
9350
  const el = scrollRef.current;
7794
9351
  if (!el) return;
7795
9352
  stickToBottomRef.current = el.scrollHeight - el.scrollTop - el.clientHeight < 80;
7796
9353
  }, []);
9354
+ React.useEffect(() => {
9355
+ if (typeof window === "undefined") return;
9356
+ let frame = 0;
9357
+ const run = () => {
9358
+ frame = 0;
9359
+ const el = scrollRef.current;
9360
+ const dock = composerRef.current;
9361
+ if (!el || !dock) return;
9362
+ if (!pageOwnsScrolling(el)) return;
9363
+ stickToBottomRef.current = dock.getBoundingClientRect().bottom - visualViewportHeight() < 80;
9364
+ };
9365
+ const schedule = () => {
9366
+ if (frame) return;
9367
+ if (typeof window.requestAnimationFrame !== "function") {
9368
+ run();
9369
+ return;
9370
+ }
9371
+ frame = window.requestAnimationFrame(run);
9372
+ };
9373
+ window.addEventListener("scroll", schedule, { passive: true });
9374
+ return () => {
9375
+ if (frame) window.cancelAnimationFrame(frame);
9376
+ window.removeEventListener("scroll", schedule);
9377
+ };
9378
+ }, []);
7797
9379
  const latestAssistantId = React.useMemo(() => {
7798
9380
  for (let i = messages.length - 1; i >= 0; i -= 1) {
7799
9381
  const message = messages[i];
@@ -7809,16 +9391,32 @@ function ExplorePage({
7809
9391
  if (!stickToBottomRef.current) return;
7810
9392
  if (anchor.offsetHeight === 0) return;
7811
9393
  alignedForRef.current = latestAssistantId;
7812
- const delta = anchor.getBoundingClientRect().top - el.getBoundingClientRect().top;
7813
- el.scrollTop = Math.max(0, el.scrollTop + delta - 12);
9394
+ if (!pageOwnsScrolling(el)) {
9395
+ const delta2 = anchor.getBoundingClientRect().top - el.getBoundingClientRect().top;
9396
+ el.scrollTop = Math.max(0, el.scrollTop + delta2 - 12);
9397
+ return;
9398
+ }
9399
+ const delta = Math.round(
9400
+ anchor.getBoundingClientRect().top - paneOffsetRef.current - 12
9401
+ );
9402
+ if (delta !== 0) window.scrollBy({ top: delta, left: 0, behavior: "auto" });
7814
9403
  }, [latestAssistantId, messages]);
7815
9404
  React.useEffect(() => {
9405
+ if (messages.length === 0) return;
7816
9406
  if (!stickToBottomRef.current) return;
7817
9407
  if (latestAssistantId && alignedForRef.current === latestAssistantId)
7818
9408
  return;
7819
9409
  const el = scrollRef.current;
7820
9410
  if (!el) return;
7821
- el.scrollTop = el.scrollHeight;
9411
+ if (!pageOwnsScrolling(el)) {
9412
+ el.scrollTop = el.scrollHeight;
9413
+ return;
9414
+ }
9415
+ const dock = composerRef.current ?? el;
9416
+ const delta = Math.round(
9417
+ dock.getBoundingClientRect().bottom - visualViewportHeight()
9418
+ );
9419
+ if (delta > 0) window.scrollBy({ top: delta, left: 0, behavior: "auto" });
7822
9420
  }, [activity, latestAssistantId, messages]);
7823
9421
  const handleShare = React.useCallback(async () => {
7824
9422
  setShareLabel(T.copying);
@@ -7916,16 +9514,27 @@ function ExplorePage({
7916
9514
  const captchaOn = Boolean(
7917
9515
  catalog.captchaRequired && catalog.recaptchaSiteKey
7918
9516
  );
7919
- const rootStyle = heightMode === "viewport" ? { height: `calc(100dvh - ${normalizeOffset(viewportOffset)})` } : void 0;
9517
+ const rootStyle = {};
9518
+ if (heightMode === "viewport") {
9519
+ rootStyle["--boff-explore-offset"] = paneOffset === null ? normalizeOffset(viewportOffset) : `${paneOffset}px`;
9520
+ if (bottomInset > 0) {
9521
+ rootStyle["--boff-explore-inset"] = `${bottomInset}px`;
9522
+ }
9523
+ }
9524
+ if (chromeOverlap > 0) {
9525
+ rootStyle["--boff-explore-chrome"] = `${chromeOverlap}px`;
9526
+ }
7920
9527
  return /* @__PURE__ */ jsxRuntime.jsxs(
7921
9528
  "div",
7922
9529
  {
9530
+ ref: rootRef,
7923
9531
  "data-boff-explore": "page",
7924
9532
  "data-phase": phase,
7925
9533
  "data-locale": activeLocale,
7926
9534
  dir: rtl ? "rtl" : void 0,
7927
9535
  className: cn(
7928
9536
  "flex w-full min-w-0 flex-col overflow-x-hidden bg-background text-foreground",
9537
+ heightMode === "viewport" && "boff-explore-viewport",
7929
9538
  heightMode === "auto" && "min-h-[70vh]",
7930
9539
  className
7931
9540
  ),
@@ -8113,6 +9722,7 @@ function ExplorePage({
8113
9722
  strings: T,
8114
9723
  message,
8115
9724
  activity,
9725
+ imageOrigins,
8116
9726
  onNavigate,
8117
9727
  anchorRef: message.id === latestAssistantId ? latestAssistantRef : void 0,
8118
9728
  onFollow: (kind, url) => recordClick(message.id, kind, url),
@@ -8158,249 +9768,257 @@ function ExplorePage({
8158
9768
  ]
8159
9769
  }
8160
9770
  ) : null,
8161
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t border-border bg-background/85 px-4 pb-[max(1rem,env(safe-area-inset-bottom))] pt-3 backdrop-blur-md", children: /* @__PURE__ */ jsxRuntime.jsxs(
8162
- "form",
9771
+ /* @__PURE__ */ jsxRuntime.jsx(
9772
+ "div",
8163
9773
  {
8164
- "data-boff-explore": "composer",
8165
- className: "mx-auto w-full max-w-3xl",
8166
- onSubmit: (event) => {
8167
- event.preventDefault();
8168
- submitDraft();
8169
- },
8170
- children: [
8171
- error ? /* @__PURE__ */ jsxRuntime.jsx(ErrorBanner, { error, onRetry: retry, onReset: reset }) : null,
8172
- staged.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
8173
- "ul",
8174
- {
8175
- "data-boff-explore": "attachments",
8176
- className: "mb-2 flex flex-wrap gap-1.5",
8177
- "aria-label": T.attach,
8178
- children: staged.map((entry) => /* @__PURE__ */ jsxRuntime.jsxs(
8179
- "li",
9774
+ ref: composerRef,
9775
+ "data-boff-explore": "composer-dock",
9776
+ className: "boff-explore-composer border-t border-border bg-background/85 px-4 pt-3 backdrop-blur-md",
9777
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
9778
+ "form",
9779
+ {
9780
+ "data-boff-explore": "composer",
9781
+ className: "mx-auto w-full max-w-3xl",
9782
+ onSubmit: (event) => {
9783
+ event.preventDefault();
9784
+ submitDraft();
9785
+ },
9786
+ children: [
9787
+ error ? /* @__PURE__ */ jsxRuntime.jsx(ErrorBanner, { error, onRetry: retry, onReset: reset }) : null,
9788
+ staged.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
9789
+ "ul",
9790
+ {
9791
+ "data-boff-explore": "attachments",
9792
+ className: "mb-2 flex flex-wrap gap-1.5",
9793
+ "aria-label": T.attach,
9794
+ children: staged.map((entry) => /* @__PURE__ */ jsxRuntime.jsxs(
9795
+ "li",
9796
+ {
9797
+ "data-boff-explore": "attachment",
9798
+ "data-text-extracted": entry.textExtracted ? "true" : "false",
9799
+ className: "flex max-w-full items-center gap-1.5 rounded-full border border-border bg-muted/60 py-1 pl-2.5 pr-1 text-xs",
9800
+ children: [
9801
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Paperclip, { className: "h-3 w-3 shrink-0 text-muted-foreground" }),
9802
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate font-medium", title: entry.name, children: entry.name }),
9803
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "shrink-0 text-muted-foreground", children: [
9804
+ formatBytes(entry.size),
9805
+ entry.textExtracted ? "" : ` \xB7 ${T.attachmentNameOnly}`
9806
+ ] }),
9807
+ /* @__PURE__ */ jsxRuntime.jsx(
9808
+ "button",
9809
+ {
9810
+ type: "button",
9811
+ "data-boff-explore": "attachment-remove",
9812
+ onClick: () => removeAttachment(entry.id),
9813
+ "aria-label": fill(T.removeAttachment, { name: entry.name }),
9814
+ className: "ml-0.5 flex h-5 w-5 shrink-0 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-background hover:text-foreground",
9815
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-3 w-3" })
9816
+ }
9817
+ )
9818
+ ]
9819
+ },
9820
+ entry.id
9821
+ ))
9822
+ }
9823
+ ) : null,
9824
+ attachmentNotice !== null ? /* @__PURE__ */ jsxRuntime.jsx(
9825
+ "p",
9826
+ {
9827
+ "data-boff-explore": "attachment-notice",
9828
+ role: "status",
9829
+ className: "mb-2 text-xs text-destructive",
9830
+ children: attachmentNotice
9831
+ }
9832
+ ) : null,
9833
+ /* @__PURE__ */ jsxRuntime.jsxs(
9834
+ "div",
8180
9835
  {
8181
- "data-boff-explore": "attachment",
8182
- "data-text-extracted": entry.textExtracted ? "true" : "false",
8183
- className: "flex max-w-full items-center gap-1.5 rounded-full border border-border bg-muted/60 py-1 pl-2.5 pr-1 text-xs",
9836
+ className: cn(
9837
+ "flex min-w-0 items-end gap-2 rounded-2xl border bg-card p-2 shadow-sm transition-all",
9838
+ busy && "boff-explore-busy",
9839
+ overLimit ? "border-destructive/60" : "border-border focus-within:border-primary/40 focus-within:ring-1 focus-within:ring-ring"
9840
+ ),
8184
9841
  children: [
8185
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Paperclip, { className: "h-3 w-3 shrink-0 text-muted-foreground" }),
8186
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate font-medium", title: entry.name, children: entry.name }),
8187
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "shrink-0 text-muted-foreground", children: [
8188
- formatBytes(entry.size),
8189
- entry.textExtracted ? "" : ` \xB7 ${T.attachmentNameOnly}`
8190
- ] }),
8191
9842
  /* @__PURE__ */ jsxRuntime.jsx(
8192
- "button",
9843
+ Textarea,
9844
+ {
9845
+ ref: textareaRef,
9846
+ value: draft,
9847
+ rows: 1,
9848
+ disabled: composerDisabled,
9849
+ onChange: (event) => setDraft(event.target.value),
9850
+ onKeyDown: handleKeyDown,
9851
+ onCompositionStart: () => {
9852
+ composingRef.current = true;
9853
+ },
9854
+ onCompositionEnd: () => {
9855
+ composingRef.current = false;
9856
+ },
9857
+ "aria-label": "Ask a question",
9858
+ placeholder: composerDisabled ? phase === "limited" ? "Message limit reached \u2014 please try again later" : "Explore is unavailable right now" : focusedProductName ? fill(T.placeholderProduct, {
9859
+ product: focusedProductName
9860
+ }) : T.placeholder,
9861
+ className: "max-h-[200px] min-h-[44px] w-full min-w-0 flex-1 resize-none border-0 bg-transparent px-2 py-2.5 text-base shadow-none focus-visible:ring-0 sm:text-sm"
9862
+ }
9863
+ ),
9864
+ /* @__PURE__ */ jsxRuntime.jsx(
9865
+ "input",
9866
+ {
9867
+ ref: fileInputRef,
9868
+ type: "file",
9869
+ multiple: true,
9870
+ className: "hidden",
9871
+ onChange: (event) => {
9872
+ void handleFilesPicked(event.target.files);
9873
+ event.target.value = "";
9874
+ }
9875
+ }
9876
+ ),
9877
+ /* @__PURE__ */ jsxRuntime.jsx(
9878
+ Button,
9879
+ {
9880
+ "data-boff-explore": "attach",
9881
+ type: "button",
9882
+ size: "icon",
9883
+ variant: "ghost",
9884
+ disabled: composerDisabled || staged.length >= MAX_ATTACHMENTS,
9885
+ "aria-label": T.attach,
9886
+ title: `${T.attach} \u2014 ${fill(T.attachHint, {
9887
+ max: String(MAX_ATTACHMENTS),
9888
+ size: String(MAX_ATTACHMENT_BYTES / (1024 * 1024))
9889
+ })}`,
9890
+ onClick: () => fileInputRef.current?.click(),
9891
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Paperclip, { className: "h-4 w-4" })
9892
+ }
9893
+ ),
9894
+ speech.supported ? /* @__PURE__ */ jsxRuntime.jsx(
9895
+ Button,
9896
+ {
9897
+ "data-boff-explore": "mic",
9898
+ "data-listening": speech.listening ? "true" : "false",
9899
+ type: "button",
9900
+ size: "icon",
9901
+ variant: speech.listening ? "default" : "ghost",
9902
+ disabled: composerDisabled,
9903
+ "aria-label": speech.listening ? T.stopDictating : T.dictate,
9904
+ "aria-pressed": speech.listening,
9905
+ title: speech.listening ? T.stopDictating : T.dictate,
9906
+ onClick: speech.toggle,
9907
+ className: cn(speech.listening && "animate-pulse"),
9908
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Mic, { className: "h-4 w-4" })
9909
+ }
9910
+ ) : null,
9911
+ busy ? /* @__PURE__ */ jsxRuntime.jsx(
9912
+ Button,
8193
9913
  {
9914
+ "data-boff-explore": "stop",
8194
9915
  type: "button",
8195
- "data-boff-explore": "attachment-remove",
8196
- onClick: () => removeAttachment(entry.id),
8197
- "aria-label": fill(T.removeAttachment, { name: entry.name }),
8198
- className: "ml-0.5 flex h-5 w-5 shrink-0 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-background hover:text-foreground",
8199
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-3 w-3" })
9916
+ size: "icon",
9917
+ variant: "secondary",
9918
+ "aria-label": T.stop,
9919
+ onClick: stop,
9920
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Square, { className: "h-4 w-4" })
9921
+ }
9922
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
9923
+ Button,
9924
+ {
9925
+ "data-boff-explore": "send",
9926
+ type: "submit",
9927
+ size: "icon",
9928
+ disabled: !canSubmit,
9929
+ "aria-label": T.send,
9930
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "h-4 w-4" })
8200
9931
  }
8201
9932
  )
8202
9933
  ]
8203
- },
8204
- entry.id
8205
- ))
8206
- }
8207
- ) : null,
8208
- attachmentNotice !== null ? /* @__PURE__ */ jsxRuntime.jsx(
8209
- "p",
8210
- {
8211
- "data-boff-explore": "attachment-notice",
8212
- role: "status",
8213
- className: "mb-2 text-xs text-destructive",
8214
- children: attachmentNotice
8215
- }
8216
- ) : null,
8217
- /* @__PURE__ */ jsxRuntime.jsxs(
8218
- "div",
8219
- {
8220
- className: cn(
8221
- "flex min-w-0 items-end gap-2 rounded-2xl border bg-card p-2 shadow-sm transition-all",
8222
- busy && "boff-explore-busy",
8223
- overLimit ? "border-destructive/60" : "border-border focus-within:border-primary/40 focus-within:ring-1 focus-within:ring-ring"
9934
+ }
8224
9935
  ),
8225
- children: [
8226
- /* @__PURE__ */ jsxRuntime.jsx(
8227
- Textarea,
9936
+ speech.listening || speech.interim !== "" ? /* @__PURE__ */ jsxRuntime.jsxs(
9937
+ "p",
9938
+ {
9939
+ "data-boff-explore": "dictation",
9940
+ className: "mt-2 flex items-center gap-2 text-xs text-muted-foreground",
9941
+ "aria-live": "polite",
9942
+ children: [
9943
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "relative flex h-2 w-2 shrink-0", children: [
9944
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute inline-flex h-full w-full animate-ping rounded-full bg-primary opacity-75" }),
9945
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "relative inline-flex h-2 w-2 rounded-full bg-primary" })
9946
+ ] }),
9947
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "italic", children: speech.interim !== "" ? speech.interim : "Listening\u2026" })
9948
+ ]
9949
+ }
9950
+ ) : null,
9951
+ speech.error !== null ? /* @__PURE__ */ jsxRuntime.jsx(
9952
+ "p",
9953
+ {
9954
+ "data-boff-explore": "dictation-error",
9955
+ className: "mt-2 text-xs text-destructive",
9956
+ children: speech.error
9957
+ }
9958
+ ) : null,
9959
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground", children: [
9960
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden sm:inline", children: "Enter to send \xB7 Shift + Enter for a new line" }),
9961
+ remainingToday !== null ? /* @__PURE__ */ jsxRuntime.jsxs("span", { "data-boff-explore": "remaining", children: [
9962
+ remainingToday,
9963
+ " message",
9964
+ remainingToday === 1 ? "" : "s",
9965
+ " left today"
9966
+ ] }) : null,
9967
+ /* @__PURE__ */ jsxRuntime.jsxs(
9968
+ "span",
8228
9969
  {
8229
- ref: textareaRef,
8230
- value: draft,
8231
- rows: 1,
8232
- disabled: composerDisabled,
8233
- onChange: (event) => setDraft(event.target.value),
8234
- onKeyDown: handleKeyDown,
8235
- onCompositionStart: () => {
8236
- composingRef.current = true;
8237
- },
8238
- onCompositionEnd: () => {
8239
- composingRef.current = false;
8240
- },
8241
- "aria-label": "Ask a question",
8242
- placeholder: composerDisabled ? phase === "limited" ? "Message limit reached \u2014 please try again later" : "Explore is unavailable right now" : focusedProductName ? fill(T.placeholderProduct, {
8243
- product: focusedProductName
8244
- }) : T.placeholder,
8245
- className: "max-h-[200px] min-h-[44px] w-full min-w-0 flex-1 resize-none border-0 bg-transparent px-2 py-2.5 text-base shadow-none focus-visible:ring-0 sm:text-sm"
9970
+ className: cn(
9971
+ "ml-auto tabular-nums",
9972
+ overLimit && "font-medium text-destructive"
9973
+ ),
9974
+ children: [
9975
+ draft.length.toLocaleString(),
9976
+ " / ",
9977
+ maxLength.toLocaleString()
9978
+ ]
8246
9979
  }
8247
- ),
9980
+ )
9981
+ ] }),
9982
+ shareUrl ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 flex items-center gap-2", children: [
8248
9983
  /* @__PURE__ */ jsxRuntime.jsx(
8249
9984
  "input",
8250
9985
  {
8251
- ref: fileInputRef,
8252
- type: "file",
8253
- multiple: true,
8254
- className: "hidden",
8255
- onChange: (event) => {
8256
- void handleFilesPicked(event.target.files);
8257
- event.target.value = "";
8258
- }
9986
+ "data-boff-explore": "share-url",
9987
+ readOnly: true,
9988
+ value: shareUrl,
9989
+ "aria-label": "Read-only link to this conversation",
9990
+ onFocus: (event) => event.currentTarget.select(),
9991
+ className: "min-w-0 flex-1 rounded-md border border-border bg-muted px-2 py-1 text-xs text-muted-foreground"
8259
9992
  }
8260
9993
  ),
8261
9994
  /* @__PURE__ */ jsxRuntime.jsx(
8262
- Button,
8263
- {
8264
- "data-boff-explore": "attach",
8265
- type: "button",
8266
- size: "icon",
8267
- variant: "ghost",
8268
- disabled: composerDisabled || staged.length >= MAX_ATTACHMENTS,
8269
- "aria-label": T.attach,
8270
- title: `${T.attach} \u2014 ${fill(T.attachHint, {
8271
- max: String(MAX_ATTACHMENTS),
8272
- size: String(MAX_ATTACHMENT_BYTES / (1024 * 1024))
8273
- })}`,
8274
- onClick: () => fileInputRef.current?.click(),
8275
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Paperclip, { className: "h-4 w-4" })
8276
- }
8277
- ),
8278
- speech.supported ? /* @__PURE__ */ jsxRuntime.jsx(
8279
- Button,
9995
+ "button",
8280
9996
  {
8281
- "data-boff-explore": "mic",
8282
- "data-listening": speech.listening ? "true" : "false",
8283
9997
  type: "button",
8284
- size: "icon",
8285
- variant: speech.listening ? "default" : "ghost",
8286
- disabled: composerDisabled,
8287
- "aria-label": speech.listening ? T.stopDictating : T.dictate,
8288
- "aria-pressed": speech.listening,
8289
- title: speech.listening ? T.stopDictating : T.dictate,
8290
- onClick: speech.toggle,
8291
- className: cn(speech.listening && "animate-pulse"),
8292
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Mic, { className: "h-4 w-4" })
9998
+ className: "shrink-0 text-xs underline underline-offset-2",
9999
+ onClick: () => setShareUrl(null),
10000
+ children: "Done"
8293
10001
  }
8294
- ) : null,
8295
- busy ? /* @__PURE__ */ jsxRuntime.jsx(
8296
- Button,
10002
+ )
10003
+ ] }) : null,
10004
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground", children: [
10005
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 truncate", children: T.disclaimerShort }),
10006
+ /* @__PURE__ */ jsxRuntime.jsx(
10007
+ "button",
8297
10008
  {
8298
- "data-boff-explore": "stop",
8299
10009
  type: "button",
8300
- size: "icon",
8301
- variant: "secondary",
8302
- "aria-label": T.stop,
8303
- onClick: stop,
8304
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Square, { className: "h-4 w-4" })
8305
- }
8306
- ) : /* @__PURE__ */ jsxRuntime.jsx(
8307
- Button,
8308
- {
8309
- "data-boff-explore": "send",
8310
- type: "submit",
8311
- size: "icon",
8312
- disabled: !canSubmit,
8313
- "aria-label": T.send,
8314
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "h-4 w-4" })
10010
+ "data-boff-explore": "disclaimer",
10011
+ onClick: () => setDisclaimerOpen(true),
10012
+ className: "shrink-0 underline underline-offset-2 transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
10013
+ children: T.disclaimer
8315
10014
  }
8316
10015
  )
8317
- ]
8318
- }
8319
- ),
8320
- speech.listening || speech.interim !== "" ? /* @__PURE__ */ jsxRuntime.jsxs(
8321
- "p",
8322
- {
8323
- "data-boff-explore": "dictation",
8324
- className: "mt-2 flex items-center gap-2 text-xs text-muted-foreground",
8325
- "aria-live": "polite",
8326
- children: [
8327
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "relative flex h-2 w-2 shrink-0", children: [
8328
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute inline-flex h-full w-full animate-ping rounded-full bg-primary opacity-75" }),
8329
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "relative inline-flex h-2 w-2 rounded-full bg-primary" })
8330
- ] }),
8331
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "italic", children: speech.interim !== "" ? speech.interim : "Listening\u2026" })
8332
- ]
8333
- }
8334
- ) : null,
8335
- speech.error !== null ? /* @__PURE__ */ jsxRuntime.jsx(
8336
- "p",
8337
- {
8338
- "data-boff-explore": "dictation-error",
8339
- className: "mt-2 text-xs text-destructive",
8340
- children: speech.error
8341
- }
8342
- ) : null,
8343
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground", children: [
8344
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden sm:inline", children: "Enter to send \xB7 Shift + Enter for a new line" }),
8345
- remainingToday !== null ? /* @__PURE__ */ jsxRuntime.jsxs("span", { "data-boff-explore": "remaining", children: [
8346
- remainingToday,
8347
- " message",
8348
- remainingToday === 1 ? "" : "s",
8349
- " left today"
8350
- ] }) : null,
8351
- /* @__PURE__ */ jsxRuntime.jsxs(
8352
- "span",
8353
- {
8354
- className: cn(
8355
- "ml-auto tabular-nums",
8356
- overLimit && "font-medium text-destructive"
8357
- ),
8358
- children: [
8359
- draft.length.toLocaleString(),
8360
- " / ",
8361
- maxLength.toLocaleString()
8362
- ]
8363
- }
8364
- )
8365
- ] }),
8366
- shareUrl ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 flex items-center gap-2", children: [
8367
- /* @__PURE__ */ jsxRuntime.jsx(
8368
- "input",
8369
- {
8370
- "data-boff-explore": "share-url",
8371
- readOnly: true,
8372
- value: shareUrl,
8373
- "aria-label": "Read-only link to this conversation",
8374
- onFocus: (event) => event.currentTarget.select(),
8375
- className: "min-w-0 flex-1 rounded-md border border-border bg-muted px-2 py-1 text-xs text-muted-foreground"
8376
- }
8377
- ),
8378
- /* @__PURE__ */ jsxRuntime.jsx(
8379
- "button",
8380
- {
8381
- type: "button",
8382
- className: "shrink-0 text-xs underline underline-offset-2",
8383
- onClick: () => setShareUrl(null),
8384
- children: "Done"
8385
- }
8386
- )
8387
- ] }) : null,
8388
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground", children: [
8389
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 truncate", children: T.disclaimerShort }),
8390
- /* @__PURE__ */ jsxRuntime.jsx(
8391
- "button",
8392
- {
8393
- type: "button",
8394
- "data-boff-explore": "disclaimer",
8395
- onClick: () => setDisclaimerOpen(true),
8396
- className: "shrink-0 underline underline-offset-2 transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
8397
- children: T.disclaimer
8398
- }
8399
- )
8400
- ] })
8401
- ]
10016
+ ] })
10017
+ ]
10018
+ }
10019
+ )
8402
10020
  }
8403
- ) })
10021
+ )
8404
10022
  ]
8405
10023
  }
8406
10024
  );
@@ -13068,11 +14686,16 @@ function StoreProductDetailPage({
13068
14686
  exports.BlogListPage = BlogListPage;
13069
14687
  exports.BlogPostPage = BlogPostPage;
13070
14688
  exports.Button = Button;
14689
+ exports.CONCEPTS_PATH = CONCEPTS_PATH;
14690
+ exports.CONCEPT_ILLUSTRATION_NOTICE = CONCEPT_ILLUSTRATION_NOTICE;
14691
+ exports.CONCEPT_PATH_PREFIX = CONCEPT_PATH_PREFIX;
13071
14692
  exports.CareersApplyForm = CareersApplyForm;
13072
14693
  exports.CareersSubmissionForm = CareersSubmissionForm;
13073
14694
  exports.CaseStudiesListPage = CaseStudiesListPage;
13074
14695
  exports.CaseStudyPage = CaseStudyPage;
13075
14696
  exports.ChangelogPage = ChangelogPage;
14697
+ exports.ConceptDetailPage = ConceptDetailPage;
14698
+ exports.ConceptsPage = ConceptsPage;
13076
14699
  exports.ContactPage = ContactPage;
13077
14700
  exports.ContentRenderer = ContentRenderer;
13078
14701
  exports.ContractsPage = ContractsPage;
@@ -13121,12 +14744,14 @@ exports.WebsiteSwitcher = WebsiteSwitcher;
13121
14744
  exports.buttonVariants = buttonVariants;
13122
14745
  exports.classifyExploreError = classifyExploreError;
13123
14746
  exports.cn = cn;
14747
+ exports.conceptPath = conceptPath;
13124
14748
  exports.detectElectronPlatform = detectElectronPlatform;
13125
14749
  exports.fetchPublicJobPosting = fetchPublicJobPosting;
13126
14750
  exports.fetchPublicJobPostings = fetchPublicJobPostings;
13127
14751
  exports.fetchPublicJobPostingsForSubmission = fetchPublicJobPostingsForSubmission;
13128
14752
  exports.formatDownloads = formatDownloads;
13129
14753
  exports.formatPrice = formatPrice2;
14754
+ exports.getConceptBySlug = getConceptBySlug;
13130
14755
  exports.getRecaptchaV3Token = getRecaptchaV3Token;
13131
14756
  exports.isValidUrl = isValidUrl;
13132
14757
  exports.loadRecaptchaV3 = loadRecaptchaV3;