@burdenoff/website-sdk 2026.922.5 → 2026.923.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.
@@ -6,11 +6,11 @@ var ReactMarkdown = require('react-markdown');
6
6
  var rehypeSanitize = require('rehype-sanitize');
7
7
  var remarkGfm = require('remark-gfm');
8
8
  var jsxRuntime = require('react/jsx-runtime');
9
+ var clsx = require('clsx');
10
+ var tailwindMerge = require('tailwind-merge');
9
11
  var reactHelmetAsync = require('react-helmet-async');
10
12
  var reactSlot = require('@radix-ui/react-slot');
11
13
  var classVarianceAuthority = require('class-variance-authority');
12
- var clsx = require('clsx');
13
- var tailwindMerge = require('tailwind-merge');
14
14
 
15
15
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
16
16
 
@@ -40,244 +40,752 @@ var remarkGfm__default = /*#__PURE__*/_interopDefault(remarkGfm);
40
40
  var __defProp = Object.defineProperty;
41
41
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
42
42
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
43
-
44
- // src/components/explore-i18n.ts
45
- var EXPLORE_LOCALES = ["en", "zh", "hi", "es", "ar", "ta"];
46
- var RTL_LOCALES = /* @__PURE__ */ new Set(["ar"]);
47
- function isRtlLocale(locale) {
48
- return RTL_LOCALES.has(locale);
43
+ function MarkdownLink({
44
+ href,
45
+ children,
46
+ // react-markdown passes the hast node to every override; it is not a DOM attribute.
47
+ node: _node,
48
+ ...props
49
+ }) {
50
+ const isExternal = typeof href === "string" && (href.startsWith("http://") || href.startsWith("https://"));
51
+ return /* @__PURE__ */ jsxRuntime.jsx(
52
+ "a",
53
+ {
54
+ href,
55
+ ...props,
56
+ ...isExternal ? { target: "_blank", rel: "noopener noreferrer" } : { rel: "noopener noreferrer" },
57
+ children
58
+ }
59
+ );
49
60
  }
50
- var EN = {
51
- badge: "AI answers, grounded in our documentation",
52
- welcomeTitle: "What problem are you trying to solve?",
53
- welcomeBody: "Describe the problem you're facing, in your own words. You can also ask about a specific product or compare a few. Every answer comes straight from our documentation.",
54
- tryAsking: "Try asking",
55
- focusProduct: "Focus on a product",
56
- allProducts: "All products",
57
- showAll: "All",
58
- showLess: "Show less",
59
- sources: "Sources",
60
- nextYouCouldAsk: "Next you could ask",
61
- placeholder: "Describe the problem you're facing\u2026",
62
- placeholderProduct: "Describe a problem, or ask about {product}\u2026",
63
- send: "Send",
64
- stop: "Stop",
65
- newChat: "New chat",
66
- history: "History",
67
- clearHistory: "Clear history",
68
- share: "Share",
69
- copied: "Copied",
70
- copying: "Copying\u2026",
71
- shareUnavailable: "Unavailable",
72
- disclaimer: "Disclaimer",
73
- disclaimerShort: "Grounded in our docs \u2014 check anything important.",
74
- dictate: "Dictate your question",
75
- stopDictating: "Stop dictating",
76
- attach: "Attach files",
77
- attachHint: "Up to {{max}} files, {{size}} MB each",
78
- removeAttachment: "Remove {{name}}",
79
- attachmentNameOnly: "name and type only",
80
- attachmentTooLarge: "{{name}} is larger than {{size}} MB and was not attached.",
81
- attachmentTooMany: "Only {{max}} files can be attached, so {{name}} was skipped.",
82
- noAnswer: "I couldn't find this in our documentation, so the answer below is not grounded in a source. Try rephrasing, or",
83
- answerDidNotArrive: "That answer didn't come through. Try asking again.",
84
- askHuman: "ask a human",
85
- // An offer, not a demand: this row sits under every answer on 38 marketing sites, so the
86
- // wording has to be shruggable. "Was this helpful?" invites; "Please rate this answer"
87
- // conscripts.
88
- feedbackPrompt: "Was this helpful?",
89
- feedbackHelpful: "Helpful",
90
- feedbackNotHelpful: "Not helpful",
91
- feedbackReasonTitle: "What was off?",
92
- feedbackReasonInaccurate: "Inaccurate",
93
- feedbackReasonIncomplete: "Incomplete",
94
- feedbackReasonOffTopic: "Off topic",
95
- feedbackReasonHardToFollow: "Hard to follow",
96
- feedbackReasonOther: "Something else",
97
- feedbackCommentPlaceholder: "Add a detail (optional)",
98
- feedbackSend: "Send",
99
- feedbackDismiss: "Dismiss",
100
- feedbackThanks: "Thanks \u2014 that helps.",
101
- feedbackNotSent: "That didn't send.",
102
- prevSuggestion: "Previous suggestion",
103
- nextSuggestion: "Next suggestion",
104
- conceptIllustration: "Concept illustration",
105
- opensInNewTab: "opens in a new tab",
106
- conceptStripTitle: "Or start from a concept",
107
- conceptStripLabel: "Concepts you can ask about",
108
- conceptAsk: "Ask about {title} in {product}",
109
- conceptAskQuestion: "How does {title} work in {product}?",
110
- copyAnswer: "Copy answer",
111
- copyFailed: "Couldn't copy",
112
- readAloud: "Read aloud",
113
- stopReading: "Stop reading",
114
- answerTable: "Table",
115
- groundedIn: "What this answer is based on",
116
- sourceCountOne: "1 source",
117
- sourceCountMany: "{count} sources",
118
- sourceKindConcept: "Concept",
119
- sourceKindDocs: "Documentation",
120
- sourceKindPricing: "Pricing",
121
- sourceKindUseCase: "Use case",
122
- sourceKindPage: "Product page",
123
- seoTitle: "Describe a problem, get an answer",
124
- seoDescription: "Describe the problem you're facing in your own words and get an answer drawn from our product documentation, with links to the exact pages it came from.",
125
- seoDescriptionProduct: "Describe the problem you're facing in your own words and get an answer drawn from {product}'s documentation \u2014 and every other product we build \u2014 with links to the exact pages it came from.",
126
- capabilities: [
127
- "Grounded in our own docs",
128
- "Compare across the ecosystem",
129
- "Straight to the right page"
130
- ],
131
- prompts: [
132
- "Every course we run lives in a different spreadsheet.",
133
- "We only hear a machine is down after the line has stopped.",
134
- "Members quietly stop showing up and we never know why.",
135
- "Which of your products fits a problem like mine?"
136
- ]
137
- };
138
- var ZH = {
139
- badge: "\u57FA\u4E8E\u6211\u4EEC\u6587\u6863\u7684 AI \u56DE\u7B54",
140
- welcomeTitle: "\u4F60\u60F3\u89E3\u51B3\u4EC0\u4E48\u95EE\u9898\uFF1F",
141
- welcomeBody: "\u7528\u4F60\u81EA\u5DF1\u7684\u8BDD\u63CF\u8FF0\u4F60\u9047\u5230\u7684\u95EE\u9898\u3002\u4F60\u4E5F\u53EF\u4EE5\u8BE2\u95EE\u67D0\u4E00\u6B3E\u4EA7\u54C1\uFF0C\u6216\u6BD4\u8F83\u51E0\u6B3E\u4EA7\u54C1\u3002\u6240\u6709\u56DE\u7B54\u90FD\u76F4\u63A5\u6765\u81EA\u6211\u4EEC\u7684\u6587\u6863\u3002",
142
- tryAsking: "\u8BD5\u7740\u95EE",
143
- focusProduct: "\u805A\u7126\u67D0\u4E2A\u4EA7\u54C1",
144
- allProducts: "\u5168\u90E8\u4EA7\u54C1",
145
- showAll: "\u5168\u90E8",
146
- showLess: "\u6536\u8D77",
147
- sources: "\u6765\u6E90",
148
- nextYouCouldAsk: "\u4F60\u8FD8\u53EF\u4EE5\u95EE",
149
- placeholder: "\u63CF\u8FF0\u4F60\u9047\u5230\u7684\u95EE\u9898\u2026",
150
- placeholderProduct: "\u63CF\u8FF0\u95EE\u9898\uFF0C\u6216\u8BE2\u95EE {product}\u2026",
151
- send: "\u53D1\u9001",
152
- stop: "\u505C\u6B62",
153
- newChat: "\u65B0\u5BF9\u8BDD",
154
- history: "\u5386\u53F2",
155
- clearHistory: "\u6E05\u9664\u5386\u53F2",
156
- share: "\u5206\u4EAB",
157
- copied: "\u5DF2\u590D\u5236",
158
- copying: "\u590D\u5236\u4E2D\u2026",
159
- shareUnavailable: "\u4E0D\u53EF\u7528",
160
- disclaimer: "\u514D\u8D23\u58F0\u660E",
161
- disclaimerShort: "\u5185\u5BB9\u57FA\u4E8E\u6211\u4EEC\u7684\u6587\u6863\uFF0C\u91CD\u8981\u4FE1\u606F\u8BF7\u81EA\u884C\u6838\u5BF9\u3002",
162
- dictate: "\u8BED\u97F3\u8F93\u5165",
163
- stopDictating: "\u505C\u6B62\u8BED\u97F3\u8F93\u5165",
164
- attach: "\u6DFB\u52A0\u6587\u4EF6",
165
- attachHint: "\u6700\u591A {{max}} \u4E2A\u6587\u4EF6\uFF0C\u6BCF\u4E2A\u4E0D\u8D85\u8FC7 {{size}} MB",
166
- removeAttachment: "\u79FB\u9664 {{name}}",
167
- attachmentNameOnly: "\u4EC5\u6587\u4EF6\u540D\u548C\u7C7B\u578B",
168
- attachmentTooLarge: "{{name}} \u8D85\u8FC7 {{size}} MB\uFF0C\u672A\u80FD\u6DFB\u52A0\u3002",
169
- attachmentTooMany: "\u6700\u591A\u53EA\u80FD\u6DFB\u52A0 {{max}} \u4E2A\u6587\u4EF6\uFF0C\u5DF2\u8DF3\u8FC7 {{name}}\u3002",
170
- 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",
171
- answerDidNotArrive: "\u8FD9\u6761\u56DE\u7B54\u6CA1\u6709\u8FD4\u56DE\u3002\u8BF7\u518D\u95EE\u4E00\u6B21\u3002",
172
- askHuman: "\u8054\u7CFB\u6211\u4EEC",
173
- feedbackPrompt: "\u8FD9\u4E2A\u56DE\u7B54\u6709\u5E2E\u52A9\u5417\uFF1F",
174
- feedbackHelpful: "\u6709\u5E2E\u52A9",
175
- feedbackNotHelpful: "\u6CA1\u6709\u5E2E\u52A9",
176
- feedbackReasonTitle: "\u54EA\u91CC\u4E0D\u5BF9\uFF1F",
177
- feedbackReasonInaccurate: "\u4E0D\u51C6\u786E",
178
- feedbackReasonIncomplete: "\u4E0D\u5B8C\u6574",
179
- feedbackReasonOffTopic: "\u7B54\u975E\u6240\u95EE",
180
- feedbackReasonHardToFollow: "\u770B\u4E0D\u592A\u61C2",
181
- feedbackReasonOther: "\u5176\u4ED6\u539F\u56E0",
182
- feedbackCommentPlaceholder: "\u8865\u5145\u8BF4\u660E\uFF08\u53EF\u9009\uFF09",
183
- feedbackSend: "\u63D0\u4EA4",
184
- feedbackDismiss: "\u5173\u95ED",
185
- feedbackThanks: "\u8C22\u8C22\uFF0C\u8FD9\u5BF9\u6211\u4EEC\u5F88\u6709\u5E2E\u52A9\u3002",
186
- feedbackNotSent: "\u6CA1\u6709\u63D0\u4EA4\u6210\u529F\u3002",
187
- prevSuggestion: "\u4E0A\u4E00\u4E2A\u5EFA\u8BAE",
188
- nextSuggestion: "\u4E0B\u4E00\u4E2A\u5EFA\u8BAE",
189
- conceptIllustration: "\u6982\u5FF5\u793A\u610F\u56FE",
190
- opensInNewTab: "\u5728\u65B0\u6807\u7B7E\u9875\u4E2D\u6253\u5F00",
191
- conceptStripTitle: "\u6216\u8005\u4ECE\u4E00\u4E2A\u6982\u5FF5\u5F00\u59CB",
192
- conceptStripLabel: "\u4F60\u53EF\u4EE5\u8BE2\u95EE\u7684\u6982\u5FF5",
193
- conceptAsk: "\u8BE2\u95EE {product} \u4E2D\u7684 {title}",
194
- conceptAskQuestion: "{product} \u7684 {title} \u662F\u600E\u4E48\u8FD0\u4F5C\u7684\uFF1F",
195
- copyAnswer: "\u590D\u5236\u56DE\u7B54",
196
- copyFailed: "\u590D\u5236\u5931\u8D25",
197
- readAloud: "\u6717\u8BFB",
198
- stopReading: "\u505C\u6B62\u6717\u8BFB",
199
- answerTable: "\u8868\u683C",
200
- groundedIn: "\u8FD9\u4E2A\u56DE\u7B54\u7684\u4F9D\u636E",
201
- sourceCountOne: "1 \u4E2A\u6765\u6E90",
202
- sourceCountMany: "{count} \u4E2A\u6765\u6E90",
203
- sourceKindConcept: "\u6982\u5FF5",
204
- sourceKindDocs: "\u6587\u6863",
205
- sourceKindPricing: "\u4EF7\u683C",
206
- sourceKindUseCase: "\u5E94\u7528\u573A\u666F",
207
- sourceKindPage: "\u4EA7\u54C1\u9875\u9762",
208
- seoTitle: "\u63CF\u8FF0\u95EE\u9898\uFF0C\u83B7\u5F97\u7B54\u6848",
209
- seoDescription: "\u7528\u4F60\u81EA\u5DF1\u7684\u8BDD\u63CF\u8FF0\u9047\u5230\u7684\u95EE\u9898\uFF0C\u5373\u53EF\u83B7\u5F97\u6765\u81EA\u6211\u4EEC\u4EA7\u54C1\u6587\u6863\u7684\u56DE\u7B54\uFF0C\u5E76\u9644\u4E0A\u7B54\u6848\u51FA\u5904\u7684\u9875\u9762\u94FE\u63A5\u3002",
210
- seoDescriptionProduct: "\u7528\u4F60\u81EA\u5DF1\u7684\u8BDD\u63CF\u8FF0\u9047\u5230\u7684\u95EE\u9898\uFF0C\u5373\u53EF\u83B7\u5F97\u6765\u81EA {product} \u53CA\u6211\u4EEC\u5176\u4ED6\u4EA7\u54C1\u6587\u6863\u7684\u56DE\u7B54\uFF0C\u5E76\u9644\u4E0A\u7B54\u6848\u51FA\u5904\u7684\u9875\u9762\u94FE\u63A5\u3002",
211
- capabilities: ["\u57FA\u4E8E\u6211\u4EEC\u81EA\u5DF1\u7684\u6587\u6863", "\u8DE8\u4EA7\u54C1\u5BF9\u6BD4", "\u76F4\u8FBE\u6B63\u786E\u7684\u9875\u9762"],
212
- prompts: [
213
- "\u6BCF\u95E8\u8BFE\u7A0B\u90FD\u8BB0\u5728\u4E0D\u540C\u7684\u8868\u683C\u91CC\u3002",
214
- "\u673A\u5668\u505C\u4E86\uFF0C\u8981\u7B49\u6574\u6761\u7EBF\u505C\u4E0B\u6765\u624D\u77E5\u9053\u3002",
215
- "\u4F1A\u5458\u6162\u6162\u5C31\u4E0D\u6765\u4E86\uFF0C\u6211\u4EEC\u4E5F\u4E0D\u77E5\u9053\u4E3A\u4EC0\u4E48\u3002",
216
- "\u54EA\u4E2A\u4EA7\u54C1\u9002\u5408\u6211\u8FD9\u6837\u7684\u95EE\u9898\uFF1F"
217
- ]
61
+ var defaultComponents = {
62
+ a: MarkdownLink
218
63
  };
219
- var HI = {
220
- 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",
221
- welcomeTitle: "\u0906\u092A \u0915\u094C\u0928 \u0938\u0940 \u0938\u092E\u0938\u094D\u092F\u093E \u0939\u0932 \u0915\u0930\u0928\u093E \u091A\u093E\u0939\u0924\u0947 \u0939\u0948\u0902?",
222
- welcomeBody: "\u0905\u092A\u0928\u0940 \u0938\u092E\u0938\u094D\u092F\u093E \u0905\u092A\u0928\u0947 \u0936\u092C\u094D\u0926\u094B\u0902 \u092E\u0947\u0902 \u092C\u0924\u093E\u090F\u0902\u0964 \u0906\u092A \u0915\u093F\u0938\u0940 \u090F\u0915 \u0909\u0924\u094D\u092A\u093E\u0926 \u0915\u0947 \u092C\u093E\u0930\u0947 \u092E\u0947\u0902 \u092D\u0940 \u092A\u0942\u091B \u0938\u0915\u0924\u0947 \u0939\u0948\u0902 \u092F\u093E \u0915\u0941\u091B \u0915\u0940 \u0924\u0941\u0932\u0928\u093E \u0915\u0930 \u0938\u0915\u0924\u0947 \u0939\u0948\u0902\u0964 \u0939\u0930 \u0909\u0924\u094D\u0924\u0930 \u0938\u0940\u0927\u0947 \u0939\u092E\u093E\u0930\u0947 \u0926\u0938\u094D\u0924\u093E\u0935\u0947\u091C\u093C \u0938\u0947 \u0906\u0924\u093E \u0939\u0948\u0964",
223
- tryAsking: "\u092F\u0939 \u092A\u0942\u091B\u0915\u0930 \u0926\u0947\u0916\u0947\u0902",
224
- focusProduct: "\u0915\u093F\u0938\u0940 \u0909\u0924\u094D\u092A\u093E\u0926 \u092A\u0930 \u0927\u094D\u092F\u093E\u0928 \u0926\u0947\u0902",
225
- allProducts: "\u0938\u092D\u0940 \u0909\u0924\u094D\u092A\u093E\u0926",
226
- showAll: "\u0938\u092D\u0940",
227
- showLess: "\u0915\u092E \u0926\u093F\u0916\u093E\u090F\u0901",
228
- sources: "\u0938\u094D\u0930\u094B\u0924",
229
- nextYouCouldAsk: "\u0906\u0917\u0947 \u0906\u092A \u092A\u0942\u091B \u0938\u0915\u0924\u0947 \u0939\u0948\u0902",
230
- placeholder: "\u0905\u092A\u0928\u0940 \u0938\u092E\u0938\u094D\u092F\u093E \u092F\u0939\u093E\u0901 \u092C\u0924\u093E\u090F\u0902\u2026",
231
- placeholderProduct: "\u0938\u092E\u0938\u094D\u092F\u093E \u092C\u0924\u093E\u090F\u0902, \u092F\u093E {product} \u0915\u0947 \u092C\u093E\u0930\u0947 \u092E\u0947\u0902 \u092A\u0942\u091B\u0947\u0902\u2026",
232
- send: "\u092D\u0947\u091C\u0947\u0902",
233
- stop: "\u0930\u094B\u0915\u0947\u0902",
234
- newChat: "\u0928\u0908 \u092C\u093E\u0924\u091A\u0940\u0924",
235
- history: "\u0907\u0924\u093F\u0939\u093E\u0938",
236
- clearHistory: "\u0907\u0924\u093F\u0939\u093E\u0938 \u092E\u093F\u091F\u093E\u090F\u0901",
237
- share: "\u0938\u093E\u091D\u093E \u0915\u0930\u0947\u0902",
238
- copied: "\u0915\u0949\u092A\u0940 \u0939\u094B \u0917\u092F\u093E",
239
- copying: "\u0915\u0949\u092A\u0940 \u0939\u094B \u0930\u0939\u093E \u0939\u0948\u2026",
240
- shareUnavailable: "\u0909\u092A\u0932\u092C\u094D\u0927 \u0928\u0939\u0940\u0902",
241
- disclaimer: "\u0905\u0938\u094D\u0935\u0940\u0915\u0930\u0923",
242
- 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",
243
- dictate: "\u092C\u094B\u0932\u0915\u0930 \u092A\u0942\u091B\u0947\u0902",
244
- stopDictating: "\u092C\u094B\u0932\u0928\u093E \u092C\u0902\u0926 \u0915\u0930\u0947\u0902",
245
- attach: "\u092B\u093C\u093E\u0907\u0932\u0947\u0902 \u091C\u094B\u0921\u093C\u0947\u0902",
246
- 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",
247
- removeAttachment: "{{name}} \u0939\u091F\u093E\u090F\u0901",
248
- attachmentNameOnly: "\u0915\u0947\u0935\u0932 \u0928\u093E\u092E \u0914\u0930 \u092A\u094D\u0930\u0915\u093E\u0930",
249
- 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",
250
- 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",
251
- 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",
252
- 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",
253
- askHuman: "\u0939\u092E\u0938\u0947 \u0938\u0902\u092A\u0930\u094D\u0915 \u0915\u0930\u0947\u0902",
254
- feedbackPrompt: "\u0915\u094D\u092F\u093E \u092F\u0939 \u0909\u0924\u094D\u0924\u0930 \u092E\u0926\u0926\u0917\u093E\u0930 \u0925\u093E?",
255
- feedbackHelpful: "\u092E\u0926\u0926\u0917\u093E\u0930",
256
- feedbackNotHelpful: "\u092E\u0926\u0926\u0917\u093E\u0930 \u0928\u0939\u0940\u0902",
257
- feedbackReasonTitle: "\u0915\u094D\u092F\u093E \u0920\u0940\u0915 \u0928\u0939\u0940\u0902 \u0925\u093E?",
258
- feedbackReasonInaccurate: "\u0917\u093C\u0932\u0924 \u091C\u093E\u0928\u0915\u093E\u0930\u0940",
259
- feedbackReasonIncomplete: "\u0905\u0927\u0942\u0930\u093E",
260
- feedbackReasonOffTopic: "\u0935\u093F\u0937\u092F \u0938\u0947 \u0939\u091F\u0915\u0930",
261
- feedbackReasonHardToFollow: "\u0938\u092E\u091D\u0928\u0947 \u092E\u0947\u0902 \u092E\u0941\u0936\u094D\u0915\u093F\u0932",
262
- feedbackReasonOther: "\u0915\u0941\u091B \u0914\u0930",
263
- feedbackCommentPlaceholder: "\u0915\u0941\u091B \u0914\u0930 \u092C\u0924\u093E\u090F\u0902 (\u0935\u0948\u0915\u0932\u094D\u092A\u093F\u0915)",
264
- feedbackSend: "\u092D\u0947\u091C\u0947\u0902",
265
- feedbackDismiss: "\u092C\u0902\u0926 \u0915\u0930\u0947\u0902",
266
- feedbackThanks: "\u0927\u0928\u094D\u092F\u0935\u093E\u0926, \u0907\u0938\u0938\u0947 \u092E\u0926\u0926 \u092E\u093F\u0932\u0924\u0940 \u0939\u0948\u0964",
267
- feedbackNotSent: "\u092F\u0939 \u092D\u0947\u091C\u093E \u0928\u0939\u0940\u0902 \u091C\u093E \u0938\u0915\u093E\u0964",
268
- prevSuggestion: "\u092A\u093F\u091B\u0932\u093E \u0938\u0941\u091D\u093E\u0935",
269
- nextSuggestion: "\u0905\u0917\u0932\u093E \u0938\u0941\u091D\u093E\u0935",
270
- conceptIllustration: "\u0905\u0935\u0927\u093E\u0930\u0923\u093E \u091A\u093F\u0924\u094D\u0930\u0923",
271
- opensInNewTab: "\u0928\u090F \u091F\u0948\u092C \u092E\u0947\u0902 \u0916\u0941\u0932\u0924\u093E \u0939\u0948",
272
- conceptStripTitle: "\u092F\u093E \u0915\u093F\u0938\u0940 \u0905\u0935\u0927\u093E\u0930\u0923\u093E \u0938\u0947 \u0936\u0941\u0930\u0942 \u0915\u0930\u0947\u0902",
273
- conceptStripLabel: "\u091C\u093F\u0928 \u0905\u0935\u0927\u093E\u0930\u0923\u093E\u0913\u0902 \u0915\u0947 \u092C\u093E\u0930\u0947 \u092E\u0947\u0902 \u0906\u092A \u092A\u0942\u091B \u0938\u0915\u0924\u0947 \u0939\u0948\u0902",
274
- conceptAsk: "{product} \u092E\u0947\u0902 {title} \u0915\u0947 \u092C\u093E\u0930\u0947 \u092E\u0947\u0902 \u092A\u0942\u091B\u0947\u0902",
275
- conceptAskQuestion: "{product} \u092E\u0947\u0902 {title} \u0915\u0948\u0938\u0947 \u0915\u093E\u092E \u0915\u0930\u0924\u093E \u0939\u0948?",
276
- copyAnswer: "\u0909\u0924\u094D\u0924\u0930 \u0915\u0949\u092A\u0940 \u0915\u0930\u0947\u0902",
277
- copyFailed: "\u0915\u0949\u092A\u0940 \u0928\u0939\u0940\u0902 \u0939\u094B \u0938\u0915\u093E",
278
- readAloud: "\u091C\u093C\u094B\u0930 \u0938\u0947 \u092A\u0922\u093C\u0947\u0902",
279
- stopReading: "\u092A\u0922\u093C\u0928\u093E \u092C\u0902\u0926 \u0915\u0930\u0947\u0902",
280
- answerTable: "\u0924\u093E\u0932\u093F\u0915\u093E",
64
+ function Markdown({
65
+ children,
66
+ className,
67
+ inline,
68
+ components
69
+ }) {
70
+ const content = /* @__PURE__ */ jsxRuntime.jsx(
71
+ ReactMarkdown__default.default,
72
+ {
73
+ remarkPlugins: [remarkGfm__default.default],
74
+ rehypePlugins: [rehypeSanitize__default.default],
75
+ components: { ...defaultComponents, ...components },
76
+ ...inline ? { disallowedElements: ["p"], unwrapDisallowed: true } : {},
77
+ children
78
+ }
79
+ );
80
+ if (inline) {
81
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className, children: content });
82
+ }
83
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className, children: content });
84
+ }
85
+ function cn(...inputs) {
86
+ return tailwindMerge.twMerge(clsx.clsx(inputs));
87
+ }
88
+ var EXPLORE_ANSWER_CSS = `
89
+ /* Nothing inside an answer may widen the page. Model output carries long URLs, code and
90
+ tables \u2014 each of those overflows a phone by default, and one overflowing child makes the
91
+ WHOLE document horizontally scrollable, not just the bubble. */
92
+ .boff-explore-body,
93
+ .boff-explore-body p,
94
+ .boff-explore-body li,
95
+ .boff-explore-body a,
96
+ .boff-explore-body h1,
97
+ .boff-explore-body h2,
98
+ .boff-explore-body h3,
99
+ /* h4-h6 as well: a published answer page pushes the model's own headings down a level or
100
+ two so they nest under the page's, and a long unbroken word is no narrower for it. */
101
+ .boff-explore-body h4,
102
+ .boff-explore-body h5,
103
+ .boff-explore-body h6 {
104
+ overflow-wrap: anywhere;
105
+ word-break: break-word;
106
+ }
107
+ .boff-explore-body pre {
108
+ overflow-x: auto;
109
+ max-width: 100%;
110
+ }
111
+ .boff-explore-body table {
112
+ display: block;
113
+ overflow-x: auto;
114
+ max-width: 100%;
115
+ }
116
+ .boff-explore-body img {
117
+ max-width: 100%;
118
+ height: auto;
119
+ }
120
+
121
+ /* ---- Comparison and pricing tables ----------------------------------------------------
122
+ The harness emits a comparison, and a set of plans, as a GFM table whose FIRST column is
123
+ the subject \u2014 the product, or the plan (CONTRACT.md \xA77). Two things have to be true of it:
124
+ the page must not scroll sideways because of it, and it has to be readable on a 320px
125
+ phone, which a four-column grid of 1px borders is not.
126
+
127
+ Wide: a contained scroller. \`max-width: 100%\` keeps the box inside the bubble and
128
+ \`overflow-x: auto\` keeps the columns inside the box, so the widest possible table still
129
+ cannot widen the document. \`overscroll-behavior-x: contain\` stops a swipe that runs out
130
+ of table from chaining to the page \u2014 the same rule the transcript needed. */
131
+ .boff-explore-body .boff-explore-table {
132
+ max-width: 100%;
133
+ overflow-x: auto;
134
+ overscroll-behavior-x: contain;
135
+ margin: 0.75rem 0;
136
+ border: 1px solid hsl(var(--border));
137
+ border-radius: 0.75rem;
138
+ background-color: hsl(var(--card));
139
+ }
140
+ .boff-explore-body .boff-explore-table:focus-visible {
141
+ outline: 2px solid hsl(var(--ring));
142
+ outline-offset: 2px;
143
+ }
144
+ .boff-explore-body .boff-explore-table table {
145
+ /* Beats the blanket \`display: block\` above, which is the unwrapped fallback. */
146
+ display: table;
147
+ width: 100%;
148
+ max-width: none;
149
+ margin: 0;
150
+ border-collapse: collapse;
151
+ font-size: 0.8125rem;
152
+ }
153
+ .boff-explore-body .boff-explore-table th,
154
+ .boff-explore-body .boff-explore-table td {
155
+ border: 0;
156
+ border-top: 1px solid hsl(var(--border));
157
+ padding: 0.5rem 0.75rem;
158
+ /* \`start\`, never \`left\`: the page root carries dir="rtl" in Arabic, and a left-aligned
159
+ table there reads against the grain of every other line on the page. */
160
+ text-align: start;
161
+ vertical-align: top;
162
+ overflow-wrap: anywhere;
163
+ /* Prices in a column line up under each other instead of drifting by digit width. */
164
+ font-variant-numeric: tabular-nums;
165
+ min-width: 6rem;
166
+ }
167
+ .boff-explore-body .boff-explore-table thead th {
168
+ border-top: 0;
169
+ background-color: hsl(var(--muted));
170
+ color: hsl(var(--foreground));
171
+ font-weight: 600;
172
+ white-space: nowrap;
173
+ }
174
+ /* The first column is what the row is ABOUT \u2014 the product, or the plan name. Reading it as
175
+ the row's heading is what makes a price list scannable rather than a grid of strings. */
176
+ .boff-explore-body .boff-explore-table tbody td:first-child {
177
+ font-weight: 600;
178
+ color: hsl(var(--card-foreground));
179
+ }
180
+
181
+ /* Narrow: one card per row. Each cell prints its own column heading, so a value is never
182
+ stranded without the question it answers, and the subject cell becomes the card's title.
183
+ A media query rather than a measured width \u2014 these pages are serialised post-hydration,
184
+ so anything measured here would ship to every phone as static HTML. */
185
+ @media (max-width: 30rem) {
186
+ .boff-explore-body .boff-explore-table {
187
+ overflow-x: visible;
188
+ }
189
+ .boff-explore-body .boff-explore-table table,
190
+ .boff-explore-body .boff-explore-table tbody,
191
+ .boff-explore-body .boff-explore-table tr,
192
+ .boff-explore-body .boff-explore-table td {
193
+ display: block;
194
+ width: 100%;
195
+ min-width: 0;
196
+ }
197
+ .boff-explore-body .boff-explore-table thead {
198
+ display: none;
199
+ }
200
+ .boff-explore-body .boff-explore-table tr {
201
+ border-top: 1px solid hsl(var(--border));
202
+ padding: 0.5rem 0;
203
+ }
204
+ .boff-explore-body .boff-explore-table tr:first-child {
205
+ border-top: 0;
206
+ }
207
+ .boff-explore-body .boff-explore-table td {
208
+ border: 0;
209
+ padding: 0.125rem 0.75rem;
210
+ }
211
+ .boff-explore-body .boff-explore-table td[data-label]::before {
212
+ content: attr(data-label);
213
+ display: block;
214
+ font-size: 0.6875rem;
215
+ font-weight: 600;
216
+ text-transform: uppercase;
217
+ letter-spacing: 0.04em;
218
+ color: hsl(var(--muted-foreground));
219
+ }
220
+ .boff-explore-body .boff-explore-table tbody td:first-child {
221
+ font-size: 0.9375rem;
222
+ padding-bottom: 0.25rem;
223
+ }
224
+ .boff-explore-body .boff-explore-table tbody td:first-child::before {
225
+ /* The plan's own name is the heading here; repeating "Plan" over it says nothing. */
226
+ display: none;
227
+ }
228
+ }
229
+ `;
230
+ function ExploreAnswerStyles() {
231
+ return /* @__PURE__ */ jsxRuntime.jsx("style", { dangerouslySetInnerHTML: { __html: EXPLORE_ANSWER_CSS } });
232
+ }
233
+ function inlineImageOrigins(products) {
234
+ const origins = /* @__PURE__ */ new Set();
235
+ for (const product of products) {
236
+ let url;
237
+ try {
238
+ url = new URL(product.website);
239
+ } catch {
240
+ continue;
241
+ }
242
+ if (url.protocol !== "https:") continue;
243
+ const apex = url.hostname.replace(/^www\./, "");
244
+ const port = url.port ? `:${url.port}` : "";
245
+ origins.add(`https://${apex}${port}`);
246
+ origins.add(`https://www.${apex}${port}`);
247
+ }
248
+ return origins;
249
+ }
250
+ function isInlineImageAllowed(src, origins) {
251
+ if (typeof src !== "string" || src === "") return false;
252
+ let url;
253
+ try {
254
+ url = new URL(src);
255
+ } catch {
256
+ return false;
257
+ }
258
+ if (url.protocol !== "https:" || url.username || url.password) return false;
259
+ return origins.has(url.origin);
260
+ }
261
+ var CODE_FENCE = /^\s*(?:```|~~~)/;
262
+ var TABLE_ROW_LINE = /^\s{0,3}\|/;
263
+ var TABLE_DELIMITER_ROW = /^\s{0,3}\|[\s:|-]*-[\s:|-]*$/;
264
+ function readableLine(raw, mode) {
265
+ let line = raw;
266
+ line = line.replace(/!\[[^\]]*\]\([^)\s]*(?:\s+"[^"]*")?\)/g, "");
267
+ line = mode === "speech" ? (
268
+ // A voice reading "h-t-t-p-s colon slash slash…" mid-sentence is noise, and the
269
+ // prompt asks for two to five links in every answer. The label IS the words.
270
+ line.replace(/\[([^\]]+)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g, "$1")
271
+ ) : line.replace(/\[([^\]]+)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g, "$1 ($2)");
272
+ line = mode === "speech" ? line.replace(/<(https?:\/\/[^>\s]+)>/g, "") : line.replace(/<(https?:\/\/[^>\s]+)>/g, "$1");
273
+ line = line.replace(/^\s{0,3}#{1,6}\s+/, "");
274
+ line = line.replace(/^\s{0,3}>\s?/, "");
275
+ if (/^\s{0,3}([-*_])[ \t]*(?:\1[ \t]*){2,}$/.test(line)) return "";
276
+ line = line.replace(/`([^`]+)`/g, "$1");
277
+ line = line.replace(/\*\*([^*]+)\*\*/g, "$1");
278
+ line = line.replace(/__([^_]+)__/g, "$1");
279
+ line = line.replace(/(^|[^*\w])\*([^*\n]+)\*(?![*\w])/g, "$1$2");
280
+ line = line.replace(/(^|[^_\w])_([^_\n]+)_(?![_\w])/g, "$1$2");
281
+ return line.replace(/[ \t]+$/, "");
282
+ }
283
+ function tableCells(row) {
284
+ const trimmed = row.trim();
285
+ const cells = [];
286
+ let cell = "";
287
+ for (let at = 0; at < trimmed.length; at += 1) {
288
+ const char = trimmed[at] ?? "";
289
+ if (char === "\\") {
290
+ cell += char + (trimmed[at + 1] ?? "");
291
+ at += 1;
292
+ continue;
293
+ }
294
+ if (char === "|") {
295
+ cells.push(cell.trim());
296
+ cell = "";
297
+ continue;
298
+ }
299
+ cell += char;
300
+ }
301
+ cells.push(cell.trim());
302
+ if (trimmed.startsWith("|") && cells[0] === "") cells.shift();
303
+ if (trimmed.endsWith("|") && cells[cells.length - 1] === "") cells.pop();
304
+ return cells;
305
+ }
306
+ function asSentence(text) {
307
+ return /[.!?]$/.test(text) ? text : `${text}.`;
308
+ }
309
+ function spokenTable(rows) {
310
+ if (rows.length < 2 || !TABLE_DELIMITER_ROW.test(rows[1] ?? "")) return rows;
311
+ const labels = tableCells(rows[0] ?? "");
312
+ const spoken = [];
313
+ for (const row of rows.slice(2)) {
314
+ const parts = [];
315
+ tableCells(row).forEach((value, column) => {
316
+ if (value === "") return;
317
+ const label = column === 0 ? "" : labels[column] ?? "";
318
+ parts.push(label === "" ? value : `${label}: ${value}`);
319
+ });
320
+ if (parts.length > 0) spoken.push(asSentence(parts.join(". ")));
321
+ }
322
+ return spoken;
323
+ }
324
+ function readableText(markdown, mode) {
325
+ const lines = [];
326
+ let inFence = false;
327
+ let table = [];
328
+ const flushTable = () => {
329
+ if (table.length === 0) return;
330
+ for (const line of spokenTable(table)) lines.push(readableLine(line, mode));
331
+ table = [];
332
+ };
333
+ for (const raw of markdown.split("\n")) {
334
+ if (CODE_FENCE.test(raw)) {
335
+ flushTable();
336
+ inFence = !inFence;
337
+ continue;
338
+ }
339
+ if (inFence) {
340
+ lines.push(raw);
341
+ continue;
342
+ }
343
+ if (mode === "speech" && TABLE_ROW_LINE.test(raw)) {
344
+ table.push(raw);
345
+ continue;
346
+ }
347
+ flushTable();
348
+ lines.push(readableLine(raw, mode));
349
+ }
350
+ flushTable();
351
+ return lines.join("\n").replace(/\n{3,}/g, "\n\n").trim();
352
+ }
353
+ function answerPlainText(markdown) {
354
+ return readableText(markdown, "clipboard");
355
+ }
356
+ function answerSpeechText(markdown) {
357
+ return readableText(markdown, "speech");
358
+ }
359
+ var AnswerScopeContext = React.createContext(null);
360
+ var InsideLinkContext = React.createContext(false);
361
+ function AnswerLink(props) {
362
+ return /* @__PURE__ */ jsxRuntime.jsx(InsideLinkContext.Provider, { value: true, children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownLink, { ...props }) });
363
+ }
364
+ var INLINE_IMAGE_FRAME = "relative inline-block max-w-full overflow-hidden rounded-xl border border-border bg-muted align-top shadow-sm";
365
+ function InlineConceptImage({ src, alt }) {
366
+ const scope = React.useContext(AnswerScopeContext);
367
+ const insideLink = React.useContext(InsideLinkContext);
368
+ if (!scope || !scope.enabled || !isInlineImageAllowed(src, scope.origins) || scope.failed.has(src)) {
369
+ return null;
370
+ }
371
+ const { strings: T, onFailed } = scope;
372
+ const picture = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
373
+ /* @__PURE__ */ jsxRuntime.jsx(
374
+ "img",
375
+ {
376
+ src,
377
+ alt: alt ?? "",
378
+ loading: "lazy",
379
+ decoding: "async",
380
+ onError: () => onFailed(src),
381
+ className: "block h-auto max-h-80 w-auto max-w-full object-contain"
382
+ }
383
+ ),
384
+ /* @__PURE__ */ jsxRuntime.jsx(
385
+ "span",
386
+ {
387
+ "aria-hidden": "true",
388
+ 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",
389
+ children: T.conceptIllustration
390
+ }
391
+ )
392
+ ] });
393
+ 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(
394
+ "a",
395
+ {
396
+ href: src,
397
+ target: "_blank",
398
+ rel: "noopener noreferrer",
399
+ className: cn(
400
+ INLINE_IMAGE_FRAME,
401
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
402
+ ),
403
+ children: [
404
+ picture,
405
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "sr-only", children: [
406
+ "(",
407
+ T.opensInNewTab,
408
+ ")"
409
+ ] })
410
+ ]
411
+ }
412
+ ) });
413
+ }
414
+ function markdownText(node) {
415
+ if (!node) return "";
416
+ if (typeof node.value === "string") return node.value;
417
+ return (node.children ?? []).map(markdownText).join("");
418
+ }
419
+ function hasHeaderCells(node) {
420
+ return (node?.children ?? []).some((cell) => cell.tagName === "th");
421
+ }
422
+ function findHeaderRow(node) {
423
+ for (const child of node?.children ?? []) {
424
+ if (child.tagName === "tr" && hasHeaderCells(child)) return child;
425
+ const nested = findHeaderRow(child);
426
+ if (nested !== null) return nested;
427
+ }
428
+ return null;
429
+ }
430
+ function tableColumnLabels(node) {
431
+ const row = findHeaderRow(node);
432
+ if (row === null) return [];
433
+ return (row.children ?? []).filter((cell) => cell.tagName === "th").map((cell) => markdownText(cell).trim());
434
+ }
435
+ var TableColumnsContext = React.createContext([]);
436
+ function AnswerTable({ node, children }) {
437
+ const scope = React.useContext(AnswerScopeContext);
438
+ const table = /* @__PURE__ */ jsxRuntime.jsx(TableColumnsContext.Provider, { value: tableColumnLabels(node), children: /* @__PURE__ */ jsxRuntime.jsx("table", { children }) });
439
+ if (!scope) return table;
440
+ return /* @__PURE__ */ jsxRuntime.jsx(
441
+ "div",
442
+ {
443
+ "data-boff-explore": "table",
444
+ role: "region",
445
+ "aria-label": scope.strings.answerTable,
446
+ tabIndex: 0,
447
+ className: "boff-explore-table",
448
+ children: table
449
+ }
450
+ );
451
+ }
452
+ function AnswerTableRow({
453
+ node,
454
+ children,
455
+ ...props
456
+ }) {
457
+ const labels = React.useContext(TableColumnsContext);
458
+ if (labels.length === 0 || hasHeaderCells(node)) {
459
+ return /* @__PURE__ */ jsxRuntime.jsx("tr", { ...props, children });
460
+ }
461
+ let column = 0;
462
+ const cells = React.Children.map(children, (cell) => {
463
+ if (!React.isValidElement(cell)) return cell;
464
+ const label = labels[column] ?? "";
465
+ column += 1;
466
+ if (label === "") return cell;
467
+ return React.cloneElement(cell, {
468
+ "data-label": label
469
+ });
470
+ });
471
+ return /* @__PURE__ */ jsxRuntime.jsx("tr", { ...props, children: cells });
472
+ }
473
+ var ANSWER_MARKDOWN_COMPONENTS = {
474
+ a: AnswerLink,
475
+ img: InlineConceptImage,
476
+ table: AnswerTable,
477
+ tr: AnswerTableRow
478
+ };
479
+ function ExploreAnswerBody({
480
+ children,
481
+ strings,
482
+ imageOrigins,
483
+ enabled = true,
484
+ components,
485
+ className
486
+ }) {
487
+ const [failed, setFailed] = React.useState(() => /* @__PURE__ */ new Set());
488
+ const onFailed = React.useCallback((src) => {
489
+ setFailed(
490
+ (current) => current.has(src) ? current : new Set(current).add(src)
491
+ );
492
+ }, []);
493
+ const scope = React.useMemo(
494
+ () => ({
495
+ strings,
496
+ enabled,
497
+ origins: imageOrigins,
498
+ failed,
499
+ onFailed
500
+ }),
501
+ [strings, enabled, imageOrigins, failed, onFailed]
502
+ );
503
+ const overrides = React.useMemo(
504
+ () => components ? { ...ANSWER_MARKDOWN_COMPONENTS, ...components } : ANSWER_MARKDOWN_COMPONENTS,
505
+ [components]
506
+ );
507
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("boff-prose boff-explore-body min-w-0", className), children: /* @__PURE__ */ jsxRuntime.jsx(AnswerScopeContext.Provider, { value: scope, children: /* @__PURE__ */ jsxRuntime.jsx(Markdown, { components: overrides, children }) }) });
508
+ }
509
+
510
+ // src/components/explore-i18n.ts
511
+ var EXPLORE_LOCALES = ["en", "zh", "hi", "es", "ar", "ta"];
512
+ var RTL_LOCALES = /* @__PURE__ */ new Set(["ar"]);
513
+ function isRtlLocale(locale) {
514
+ return RTL_LOCALES.has(locale);
515
+ }
516
+ var EN = {
517
+ badge: "AI answers, grounded in our documentation",
518
+ welcomeTitle: "What problem are you trying to solve?",
519
+ welcomeBody: "Describe the problem you're facing, in your own words. You can also ask about a specific product or compare a few. Every answer comes straight from our documentation.",
520
+ tryAsking: "Try asking",
521
+ focusProduct: "Focus on a product",
522
+ allProducts: "All products",
523
+ showAll: "All",
524
+ showLess: "Show less",
525
+ sources: "Sources",
526
+ nextYouCouldAsk: "Next you could ask",
527
+ placeholder: "Describe the problem\u2026",
528
+ placeholderProduct: "Describe a problem, or ask about {product}\u2026",
529
+ send: "Send",
530
+ stop: "Stop",
531
+ newChat: "New chat",
532
+ history: "History",
533
+ clearHistory: "Clear history",
534
+ share: "Share",
535
+ copied: "Copied",
536
+ copying: "Copying\u2026",
537
+ shareUnavailable: "Unavailable",
538
+ disclaimer: "Disclaimer",
539
+ disclaimerShort: "Grounded in our docs \u2014 check anything important.",
540
+ dictate: "Dictate your question",
541
+ stopDictating: "Stop dictating",
542
+ attach: "Attach files",
543
+ attachHint: "Up to {{max}} files, {{size}} MB each",
544
+ removeAttachment: "Remove {{name}}",
545
+ attachmentNameOnly: "name and type only",
546
+ attachmentTooLarge: "{{name}} is larger than {{size}} MB and was not attached.",
547
+ attachmentTooMany: "Only {{max}} files can be attached, so {{name}} was skipped.",
548
+ noAnswer: "I couldn't find this in our documentation, so the answer below is not grounded in a source. Try rephrasing, or",
549
+ answerDidNotArrive: "That answer didn't come through. Try asking again.",
550
+ askHuman: "ask a human",
551
+ // An offer, not a demand: this row sits under every answer on 38 marketing sites, so the
552
+ // wording has to be shruggable. "Was this helpful?" invites; "Please rate this answer"
553
+ // conscripts.
554
+ feedbackPrompt: "Was this helpful?",
555
+ feedbackHelpful: "Helpful",
556
+ feedbackNotHelpful: "Not helpful",
557
+ feedbackReasonTitle: "What was off?",
558
+ feedbackReasonInaccurate: "Inaccurate",
559
+ feedbackReasonIncomplete: "Incomplete",
560
+ feedbackReasonOffTopic: "Off topic",
561
+ feedbackReasonHardToFollow: "Hard to follow",
562
+ feedbackReasonOther: "Something else",
563
+ feedbackCommentPlaceholder: "Add a detail (optional)",
564
+ feedbackSend: "Send",
565
+ feedbackDismiss: "Dismiss",
566
+ feedbackThanks: "Thanks \u2014 that helps.",
567
+ feedbackNotSent: "That didn't send.",
568
+ escalateOpen: "Talk to a person",
569
+ escalateTitle: "Send this conversation to a person",
570
+ escalateBody: "We'll pass the whole conversation on, so you don't have to type any of it again.",
571
+ escalateEmailLabel: "Your email address",
572
+ escalateEmailPlaceholder: "you@example.com",
573
+ escalateNoteLabel: "Anything else a person should know? (optional)",
574
+ escalateSubmit: "Send to a person",
575
+ escalateSending: "Sending\u2026",
576
+ escalateCancel: "Cancel",
577
+ escalateInvalidEmail: "That doesn't look like an address we can reply to. Please check it.",
578
+ escalateSent: "A person now has this conversation. We'll reply to {email}.",
579
+ escalateRateLimited: "That's a lot of requests in a short time. Please try again in a few minutes.",
580
+ escalateCaptcha: "We couldn't verify that you're human. Please try that again.",
581
+ escalateFailed: "We couldn't pass that on just now. Please try again.",
582
+ prevSuggestion: "Previous suggestion",
583
+ nextSuggestion: "Next suggestion",
584
+ conceptIllustration: "Concept illustration",
585
+ opensInNewTab: "opens in a new tab",
586
+ conceptStripTitle: "Or start from a concept",
587
+ conceptStripLabel: "Concepts you can ask about",
588
+ conceptAsk: "Ask about {title} in {product}",
589
+ conceptAskQuestion: "How does {title} work in {product}?",
590
+ copyAnswer: "Copy answer",
591
+ copyFailed: "Couldn't copy",
592
+ readAloud: "Read aloud",
593
+ stopReading: "Stop reading",
594
+ answerTable: "Table",
595
+ groundedIn: "What this answer is based on",
596
+ sourceCountOne: "1 source",
597
+ sourceCountMany: "{count} sources",
598
+ sourceKindConcept: "Concept",
599
+ sourceKindDocs: "Documentation",
600
+ sourceKindPricing: "Pricing",
601
+ sourceKindUseCase: "Use case",
602
+ sourceKindPage: "Product page",
603
+ seoTitle: "Describe a problem, get an answer",
604
+ seoDescription: "Describe the problem you're facing in your own words and get an answer drawn from our product documentation, with links to the exact pages it came from.",
605
+ seoDescriptionProduct: "Describe the problem you're facing in your own words and get an answer drawn from {product}'s documentation \u2014 and every other product we build \u2014 with links to the exact pages it came from.",
606
+ capabilities: [
607
+ "Grounded in our own docs",
608
+ "Compare across the ecosystem",
609
+ "Straight to the right page"
610
+ ],
611
+ prompts: [
612
+ "Every course we run lives in a different spreadsheet.",
613
+ "We only hear a machine is down after the line has stopped.",
614
+ "Members quietly stop showing up and we never know why.",
615
+ "Which of your products fits a problem like mine?"
616
+ ]
617
+ };
618
+ var ZH = {
619
+ badge: "\u57FA\u4E8E\u6211\u4EEC\u6587\u6863\u7684 AI \u56DE\u7B54",
620
+ welcomeTitle: "\u4F60\u60F3\u89E3\u51B3\u4EC0\u4E48\u95EE\u9898\uFF1F",
621
+ welcomeBody: "\u7528\u4F60\u81EA\u5DF1\u7684\u8BDD\u63CF\u8FF0\u4F60\u9047\u5230\u7684\u95EE\u9898\u3002\u4F60\u4E5F\u53EF\u4EE5\u8BE2\u95EE\u67D0\u4E00\u6B3E\u4EA7\u54C1\uFF0C\u6216\u6BD4\u8F83\u51E0\u6B3E\u4EA7\u54C1\u3002\u6240\u6709\u56DE\u7B54\u90FD\u76F4\u63A5\u6765\u81EA\u6211\u4EEC\u7684\u6587\u6863\u3002",
622
+ tryAsking: "\u8BD5\u7740\u95EE",
623
+ focusProduct: "\u805A\u7126\u67D0\u4E2A\u4EA7\u54C1",
624
+ allProducts: "\u5168\u90E8\u4EA7\u54C1",
625
+ showAll: "\u5168\u90E8",
626
+ showLess: "\u6536\u8D77",
627
+ sources: "\u6765\u6E90",
628
+ nextYouCouldAsk: "\u4F60\u8FD8\u53EF\u4EE5\u95EE",
629
+ placeholder: "\u63CF\u8FF0\u4F60\u7684\u95EE\u9898\u2026",
630
+ placeholderProduct: "\u63CF\u8FF0\u95EE\u9898\uFF0C\u6216\u8BE2\u95EE {product}\u2026",
631
+ send: "\u53D1\u9001",
632
+ stop: "\u505C\u6B62",
633
+ newChat: "\u65B0\u5BF9\u8BDD",
634
+ history: "\u5386\u53F2",
635
+ clearHistory: "\u6E05\u9664\u5386\u53F2",
636
+ share: "\u5206\u4EAB",
637
+ copied: "\u5DF2\u590D\u5236",
638
+ copying: "\u590D\u5236\u4E2D\u2026",
639
+ shareUnavailable: "\u4E0D\u53EF\u7528",
640
+ disclaimer: "\u514D\u8D23\u58F0\u660E",
641
+ disclaimerShort: "\u5185\u5BB9\u57FA\u4E8E\u6211\u4EEC\u7684\u6587\u6863\uFF0C\u91CD\u8981\u4FE1\u606F\u8BF7\u81EA\u884C\u6838\u5BF9\u3002",
642
+ dictate: "\u8BED\u97F3\u8F93\u5165",
643
+ stopDictating: "\u505C\u6B62\u8BED\u97F3\u8F93\u5165",
644
+ attach: "\u6DFB\u52A0\u6587\u4EF6",
645
+ attachHint: "\u6700\u591A {{max}} \u4E2A\u6587\u4EF6\uFF0C\u6BCF\u4E2A\u4E0D\u8D85\u8FC7 {{size}} MB",
646
+ removeAttachment: "\u79FB\u9664 {{name}}",
647
+ attachmentNameOnly: "\u4EC5\u6587\u4EF6\u540D\u548C\u7C7B\u578B",
648
+ attachmentTooLarge: "{{name}} \u8D85\u8FC7 {{size}} MB\uFF0C\u672A\u80FD\u6DFB\u52A0\u3002",
649
+ attachmentTooMany: "\u6700\u591A\u53EA\u80FD\u6DFB\u52A0 {{max}} \u4E2A\u6587\u4EF6\uFF0C\u5DF2\u8DF3\u8FC7 {{name}}\u3002",
650
+ 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",
651
+ answerDidNotArrive: "\u8FD9\u6761\u56DE\u7B54\u6CA1\u6709\u8FD4\u56DE\u3002\u8BF7\u518D\u95EE\u4E00\u6B21\u3002",
652
+ askHuman: "\u8054\u7CFB\u6211\u4EEC",
653
+ feedbackPrompt: "\u8FD9\u4E2A\u56DE\u7B54\u6709\u5E2E\u52A9\u5417\uFF1F",
654
+ feedbackHelpful: "\u6709\u5E2E\u52A9",
655
+ feedbackNotHelpful: "\u6CA1\u6709\u5E2E\u52A9",
656
+ feedbackReasonTitle: "\u54EA\u91CC\u4E0D\u5BF9\uFF1F",
657
+ feedbackReasonInaccurate: "\u4E0D\u51C6\u786E",
658
+ feedbackReasonIncomplete: "\u4E0D\u5B8C\u6574",
659
+ feedbackReasonOffTopic: "\u7B54\u975E\u6240\u95EE",
660
+ feedbackReasonHardToFollow: "\u770B\u4E0D\u592A\u61C2",
661
+ feedbackReasonOther: "\u5176\u4ED6\u539F\u56E0",
662
+ feedbackCommentPlaceholder: "\u8865\u5145\u8BF4\u660E\uFF08\u53EF\u9009\uFF09",
663
+ feedbackSend: "\u63D0\u4EA4",
664
+ feedbackDismiss: "\u5173\u95ED",
665
+ feedbackThanks: "\u8C22\u8C22\uFF0C\u8FD9\u5BF9\u6211\u4EEC\u5F88\u6709\u5E2E\u52A9\u3002",
666
+ feedbackNotSent: "\u6CA1\u6709\u63D0\u4EA4\u6210\u529F\u3002",
667
+ escalateOpen: "\u8054\u7CFB\u771F\u4EBA",
668
+ escalateTitle: "\u628A\u8FD9\u6BB5\u5BF9\u8BDD\u8F6C\u7ED9\u771F\u4EBA",
669
+ escalateBody: "\u6211\u4EEC\u4F1A\u628A\u6574\u6BB5\u5BF9\u8BDD\u4E00\u8D77\u8F6C\u8FC7\u53BB\uFF0C\u60A8\u4E0D\u7528\u518D\u91CD\u65B0\u6253\u4E00\u904D\u3002",
670
+ escalateEmailLabel: "\u60A8\u7684\u90AE\u7BB1\u5730\u5740",
671
+ escalateEmailPlaceholder: "you@example.com",
672
+ escalateNoteLabel: "\u8FD8\u6709\u4EC0\u4E48\u9700\u8981\u544A\u8BC9\u6211\u4EEC\u7684\u5417\uFF1F\uFF08\u9009\u586B\uFF09",
673
+ escalateSubmit: "\u8F6C\u7ED9\u771F\u4EBA",
674
+ escalateSending: "\u6B63\u5728\u53D1\u9001\u2026",
675
+ escalateCancel: "\u53D6\u6D88",
676
+ escalateInvalidEmail: "\u8FD9\u4F3C\u4E4E\u4E0D\u662F\u4E00\u4E2A\u6211\u4EEC\u53EF\u4EE5\u56DE\u590D\u7684\u90AE\u7BB1\u5730\u5740\uFF0C\u8BF7\u68C0\u67E5\u4E00\u4E0B\u3002",
677
+ escalateSent: "\u5DF2\u6709\u540C\u4E8B\u63A5\u624B\u8FD9\u6BB5\u5BF9\u8BDD\uFF0C\u6211\u4EEC\u4F1A\u56DE\u590D\u5230 {email}\u3002",
678
+ escalateRateLimited: "\u77ED\u65F6\u95F4\u5185\u8BF7\u6C42\u8FC7\u591A\uFF0C\u8BF7\u51E0\u5206\u949F\u540E\u518D\u8BD5\u3002",
679
+ escalateCaptcha: "\u6211\u4EEC\u65E0\u6CD5\u9A8C\u8BC1\u60A8\u662F\u771F\u4EBA\uFF0C\u8BF7\u518D\u8BD5\u4E00\u6B21\u3002",
680
+ escalateFailed: "\u6682\u65F6\u65E0\u6CD5\u8F6C\u4EA4\uFF0C\u8BF7\u518D\u8BD5\u4E00\u6B21\u3002",
681
+ prevSuggestion: "\u4E0A\u4E00\u4E2A\u5EFA\u8BAE",
682
+ nextSuggestion: "\u4E0B\u4E00\u4E2A\u5EFA\u8BAE",
683
+ conceptIllustration: "\u6982\u5FF5\u793A\u610F\u56FE",
684
+ opensInNewTab: "\u5728\u65B0\u6807\u7B7E\u9875\u4E2D\u6253\u5F00",
685
+ conceptStripTitle: "\u6216\u8005\u4ECE\u4E00\u4E2A\u6982\u5FF5\u5F00\u59CB",
686
+ conceptStripLabel: "\u4F60\u53EF\u4EE5\u8BE2\u95EE\u7684\u6982\u5FF5",
687
+ conceptAsk: "\u8BE2\u95EE {product} \u4E2D\u7684 {title}",
688
+ conceptAskQuestion: "{product} \u7684 {title} \u662F\u600E\u4E48\u8FD0\u4F5C\u7684\uFF1F",
689
+ copyAnswer: "\u590D\u5236\u56DE\u7B54",
690
+ copyFailed: "\u590D\u5236\u5931\u8D25",
691
+ readAloud: "\u6717\u8BFB",
692
+ stopReading: "\u505C\u6B62\u6717\u8BFB",
693
+ answerTable: "\u8868\u683C",
694
+ groundedIn: "\u8FD9\u4E2A\u56DE\u7B54\u7684\u4F9D\u636E",
695
+ sourceCountOne: "1 \u4E2A\u6765\u6E90",
696
+ sourceCountMany: "{count} \u4E2A\u6765\u6E90",
697
+ sourceKindConcept: "\u6982\u5FF5",
698
+ sourceKindDocs: "\u6587\u6863",
699
+ sourceKindPricing: "\u4EF7\u683C",
700
+ sourceKindUseCase: "\u5E94\u7528\u573A\u666F",
701
+ sourceKindPage: "\u4EA7\u54C1\u9875\u9762",
702
+ seoTitle: "\u63CF\u8FF0\u95EE\u9898\uFF0C\u83B7\u5F97\u7B54\u6848",
703
+ seoDescription: "\u7528\u4F60\u81EA\u5DF1\u7684\u8BDD\u63CF\u8FF0\u9047\u5230\u7684\u95EE\u9898\uFF0C\u5373\u53EF\u83B7\u5F97\u6765\u81EA\u6211\u4EEC\u4EA7\u54C1\u6587\u6863\u7684\u56DE\u7B54\uFF0C\u5E76\u9644\u4E0A\u7B54\u6848\u51FA\u5904\u7684\u9875\u9762\u94FE\u63A5\u3002",
704
+ seoDescriptionProduct: "\u7528\u4F60\u81EA\u5DF1\u7684\u8BDD\u63CF\u8FF0\u9047\u5230\u7684\u95EE\u9898\uFF0C\u5373\u53EF\u83B7\u5F97\u6765\u81EA {product} \u53CA\u6211\u4EEC\u5176\u4ED6\u4EA7\u54C1\u6587\u6863\u7684\u56DE\u7B54\uFF0C\u5E76\u9644\u4E0A\u7B54\u6848\u51FA\u5904\u7684\u9875\u9762\u94FE\u63A5\u3002",
705
+ capabilities: ["\u57FA\u4E8E\u6211\u4EEC\u81EA\u5DF1\u7684\u6587\u6863", "\u8DE8\u4EA7\u54C1\u5BF9\u6BD4", "\u76F4\u8FBE\u6B63\u786E\u7684\u9875\u9762"],
706
+ prompts: [
707
+ "\u6BCF\u95E8\u8BFE\u7A0B\u90FD\u8BB0\u5728\u4E0D\u540C\u7684\u8868\u683C\u91CC\u3002",
708
+ "\u673A\u5668\u505C\u4E86\uFF0C\u8981\u7B49\u6574\u6761\u7EBF\u505C\u4E0B\u6765\u624D\u77E5\u9053\u3002",
709
+ "\u4F1A\u5458\u6162\u6162\u5C31\u4E0D\u6765\u4E86\uFF0C\u6211\u4EEC\u4E5F\u4E0D\u77E5\u9053\u4E3A\u4EC0\u4E48\u3002",
710
+ "\u54EA\u4E2A\u4EA7\u54C1\u9002\u5408\u6211\u8FD9\u6837\u7684\u95EE\u9898\uFF1F"
711
+ ]
712
+ };
713
+ var HI = {
714
+ 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",
715
+ welcomeTitle: "\u0906\u092A \u0915\u094C\u0928 \u0938\u0940 \u0938\u092E\u0938\u094D\u092F\u093E \u0939\u0932 \u0915\u0930\u0928\u093E \u091A\u093E\u0939\u0924\u0947 \u0939\u0948\u0902?",
716
+ welcomeBody: "\u0905\u092A\u0928\u0940 \u0938\u092E\u0938\u094D\u092F\u093E \u0905\u092A\u0928\u0947 \u0936\u092C\u094D\u0926\u094B\u0902 \u092E\u0947\u0902 \u092C\u0924\u093E\u090F\u0902\u0964 \u0906\u092A \u0915\u093F\u0938\u0940 \u090F\u0915 \u0909\u0924\u094D\u092A\u093E\u0926 \u0915\u0947 \u092C\u093E\u0930\u0947 \u092E\u0947\u0902 \u092D\u0940 \u092A\u0942\u091B \u0938\u0915\u0924\u0947 \u0939\u0948\u0902 \u092F\u093E \u0915\u0941\u091B \u0915\u0940 \u0924\u0941\u0932\u0928\u093E \u0915\u0930 \u0938\u0915\u0924\u0947 \u0939\u0948\u0902\u0964 \u0939\u0930 \u0909\u0924\u094D\u0924\u0930 \u0938\u0940\u0927\u0947 \u0939\u092E\u093E\u0930\u0947 \u0926\u0938\u094D\u0924\u093E\u0935\u0947\u091C\u093C \u0938\u0947 \u0906\u0924\u093E \u0939\u0948\u0964",
717
+ tryAsking: "\u092F\u0939 \u092A\u0942\u091B\u0915\u0930 \u0926\u0947\u0916\u0947\u0902",
718
+ focusProduct: "\u0915\u093F\u0938\u0940 \u0909\u0924\u094D\u092A\u093E\u0926 \u092A\u0930 \u0927\u094D\u092F\u093E\u0928 \u0926\u0947\u0902",
719
+ allProducts: "\u0938\u092D\u0940 \u0909\u0924\u094D\u092A\u093E\u0926",
720
+ showAll: "\u0938\u092D\u0940",
721
+ showLess: "\u0915\u092E \u0926\u093F\u0916\u093E\u090F\u0901",
722
+ sources: "\u0938\u094D\u0930\u094B\u0924",
723
+ nextYouCouldAsk: "\u0906\u0917\u0947 \u0906\u092A \u092A\u0942\u091B \u0938\u0915\u0924\u0947 \u0939\u0948\u0902",
724
+ placeholder: "\u0905\u092A\u0928\u0940 \u0938\u092E\u0938\u094D\u092F\u093E \u092C\u0924\u093E\u090F\u0902\u2026",
725
+ placeholderProduct: "\u0938\u092E\u0938\u094D\u092F\u093E \u092C\u0924\u093E\u090F\u0902, \u092F\u093E {product} \u0915\u0947 \u092C\u093E\u0930\u0947 \u092E\u0947\u0902 \u092A\u0942\u091B\u0947\u0902\u2026",
726
+ send: "\u092D\u0947\u091C\u0947\u0902",
727
+ stop: "\u0930\u094B\u0915\u0947\u0902",
728
+ newChat: "\u0928\u0908 \u092C\u093E\u0924\u091A\u0940\u0924",
729
+ history: "\u0907\u0924\u093F\u0939\u093E\u0938",
730
+ clearHistory: "\u0907\u0924\u093F\u0939\u093E\u0938 \u092E\u093F\u091F\u093E\u090F\u0901",
731
+ share: "\u0938\u093E\u091D\u093E \u0915\u0930\u0947\u0902",
732
+ copied: "\u0915\u0949\u092A\u0940 \u0939\u094B \u0917\u092F\u093E",
733
+ copying: "\u0915\u0949\u092A\u0940 \u0939\u094B \u0930\u0939\u093E \u0939\u0948\u2026",
734
+ shareUnavailable: "\u0909\u092A\u0932\u092C\u094D\u0927 \u0928\u0939\u0940\u0902",
735
+ disclaimer: "\u0905\u0938\u094D\u0935\u0940\u0915\u0930\u0923",
736
+ 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",
737
+ dictate: "\u092C\u094B\u0932\u0915\u0930 \u092A\u0942\u091B\u0947\u0902",
738
+ stopDictating: "\u092C\u094B\u0932\u0928\u093E \u092C\u0902\u0926 \u0915\u0930\u0947\u0902",
739
+ attach: "\u092B\u093C\u093E\u0907\u0932\u0947\u0902 \u091C\u094B\u0921\u093C\u0947\u0902",
740
+ 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",
741
+ removeAttachment: "{{name}} \u0939\u091F\u093E\u090F\u0901",
742
+ attachmentNameOnly: "\u0915\u0947\u0935\u0932 \u0928\u093E\u092E \u0914\u0930 \u092A\u094D\u0930\u0915\u093E\u0930",
743
+ 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",
744
+ 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",
745
+ 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",
746
+ 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",
747
+ askHuman: "\u0939\u092E\u0938\u0947 \u0938\u0902\u092A\u0930\u094D\u0915 \u0915\u0930\u0947\u0902",
748
+ feedbackPrompt: "\u0915\u094D\u092F\u093E \u092F\u0939 \u0909\u0924\u094D\u0924\u0930 \u092E\u0926\u0926\u0917\u093E\u0930 \u0925\u093E?",
749
+ feedbackHelpful: "\u092E\u0926\u0926\u0917\u093E\u0930",
750
+ feedbackNotHelpful: "\u092E\u0926\u0926\u0917\u093E\u0930 \u0928\u0939\u0940\u0902",
751
+ feedbackReasonTitle: "\u0915\u094D\u092F\u093E \u0920\u0940\u0915 \u0928\u0939\u0940\u0902 \u0925\u093E?",
752
+ feedbackReasonInaccurate: "\u0917\u093C\u0932\u0924 \u091C\u093E\u0928\u0915\u093E\u0930\u0940",
753
+ feedbackReasonIncomplete: "\u0905\u0927\u0942\u0930\u093E",
754
+ feedbackReasonOffTopic: "\u0935\u093F\u0937\u092F \u0938\u0947 \u0939\u091F\u0915\u0930",
755
+ feedbackReasonHardToFollow: "\u0938\u092E\u091D\u0928\u0947 \u092E\u0947\u0902 \u092E\u0941\u0936\u094D\u0915\u093F\u0932",
756
+ feedbackReasonOther: "\u0915\u0941\u091B \u0914\u0930",
757
+ feedbackCommentPlaceholder: "\u0915\u0941\u091B \u0914\u0930 \u092C\u0924\u093E\u090F\u0902 (\u0935\u0948\u0915\u0932\u094D\u092A\u093F\u0915)",
758
+ feedbackSend: "\u092D\u0947\u091C\u0947\u0902",
759
+ feedbackDismiss: "\u092C\u0902\u0926 \u0915\u0930\u0947\u0902",
760
+ feedbackThanks: "\u0927\u0928\u094D\u092F\u0935\u093E\u0926, \u0907\u0938\u0938\u0947 \u092E\u0926\u0926 \u092E\u093F\u0932\u0924\u0940 \u0939\u0948\u0964",
761
+ feedbackNotSent: "\u092F\u0939 \u092D\u0947\u091C\u093E \u0928\u0939\u0940\u0902 \u091C\u093E \u0938\u0915\u093E\u0964",
762
+ escalateOpen: "\u0915\u093F\u0938\u0940 \u0935\u094D\u092F\u0915\u094D\u0924\u093F \u0938\u0947 \u092C\u093E\u0924 \u0915\u0930\u0947\u0902",
763
+ escalateTitle: "\u092F\u0939 \u092C\u093E\u0924\u091A\u0940\u0924 \u0915\u093F\u0938\u0940 \u0935\u094D\u092F\u0915\u094D\u0924\u093F \u0915\u094B \u092D\u0947\u091C\u0947\u0902",
764
+ escalateBody: "\u0939\u092E \u092A\u0942\u0930\u0940 \u092C\u093E\u0924\u091A\u0940\u0924 \u0906\u0917\u0947 \u092D\u0947\u091C \u0926\u0947\u0902\u0917\u0947, \u0924\u093E\u0915\u093F \u0906\u092A\u0915\u094B \u0926\u094B\u092C\u093E\u0930\u093E \u0915\u0941\u091B \u0932\u093F\u0916\u0928\u093E \u0928 \u092A\u0921\u093C\u0947\u0964",
765
+ escalateEmailLabel: "\u0906\u092A\u0915\u093E \u0908\u092E\u0947\u0932 \u092A\u0924\u093E",
766
+ escalateEmailPlaceholder: "you@example.com",
767
+ escalateNoteLabel: "\u0915\u094D\u092F\u093E \u0915\u0941\u091B \u0914\u0930 \u092C\u0924\u093E\u0928\u093E \u091A\u093E\u0939\u0947\u0902\u0917\u0947? (\u0935\u0948\u0915\u0932\u094D\u092A\u093F\u0915)",
768
+ escalateSubmit: "\u0915\u093F\u0938\u0940 \u0935\u094D\u092F\u0915\u094D\u0924\u093F \u0915\u094B \u092D\u0947\u091C\u0947\u0902",
769
+ escalateSending: "\u092D\u0947\u091C\u093E \u091C\u093E \u0930\u0939\u093E \u0939\u0948\u2026",
770
+ escalateCancel: "\u0930\u0926\u094D\u0926 \u0915\u0930\u0947\u0902",
771
+ escalateInvalidEmail: "\u092F\u0939 \u0910\u0938\u093E \u0908\u092E\u0947\u0932 \u092A\u0924\u093E \u0928\u0939\u0940\u0902 \u0932\u0917\u0924\u093E \u091C\u093F\u0938 \u092A\u0930 \u0939\u092E \u091C\u0935\u093E\u092C \u092D\u0947\u091C \u0938\u0915\u0947\u0902\u0964 \u0915\u0943\u092A\u092F\u093E \u091C\u093E\u0901\u091A \u0932\u0947\u0902\u0964",
772
+ escalateSent: "\u0905\u092C \u092F\u0939 \u092C\u093E\u0924\u091A\u0940\u0924 \u090F\u0915 \u0935\u094D\u092F\u0915\u094D\u0924\u093F \u0915\u0947 \u092A\u093E\u0938 \u0939\u0948\u0964 \u0939\u092E {email} \u092A\u0930 \u091C\u0935\u093E\u092C \u0926\u0947\u0902\u0917\u0947\u0964",
773
+ escalateRateLimited: "\u0925\u094B\u0921\u093C\u0947 \u0938\u092E\u092F \u092E\u0947\u0902 \u092C\u0939\u0941\u0924 \u0938\u093E\u0930\u0947 \u0905\u0928\u0941\u0930\u094B\u0927 \u0939\u094B \u0917\u090F\u0964 \u0915\u0943\u092A\u092F\u093E \u0915\u0941\u091B \u092E\u093F\u0928\u091F \u092C\u093E\u0926 \u0915\u094B\u0936\u093F\u0936 \u0915\u0930\u0947\u0902\u0964",
774
+ escalateCaptcha: "\u0939\u092E \u092F\u0939 \u0938\u0924\u094D\u092F\u093E\u092A\u093F\u0924 \u0928\u0939\u0940\u0902 \u0915\u0930 \u0938\u0915\u0947 \u0915\u093F \u0906\u092A \u090F\u0915 \u0935\u094D\u092F\u0915\u094D\u0924\u093F \u0939\u0948\u0902\u0964 \u0915\u0943\u092A\u092F\u093E \u092B\u093F\u0930 \u0915\u094B\u0936\u093F\u0936 \u0915\u0930\u0947\u0902\u0964",
775
+ escalateFailed: "\u0905\u092D\u0940 \u0907\u0938\u0947 \u0906\u0917\u0947 \u0928\u0939\u0940\u0902 \u092D\u0947\u091C\u093E \u091C\u093E \u0938\u0915\u093E\u0964 \u0915\u0943\u092A\u092F\u093E \u092B\u093F\u0930 \u0915\u094B\u0936\u093F\u0936 \u0915\u0930\u0947\u0902\u0964",
776
+ prevSuggestion: "\u092A\u093F\u091B\u0932\u093E \u0938\u0941\u091D\u093E\u0935",
777
+ nextSuggestion: "\u0905\u0917\u0932\u093E \u0938\u0941\u091D\u093E\u0935",
778
+ conceptIllustration: "\u0905\u0935\u0927\u093E\u0930\u0923\u093E \u091A\u093F\u0924\u094D\u0930\u0923",
779
+ opensInNewTab: "\u0928\u090F \u091F\u0948\u092C \u092E\u0947\u0902 \u0916\u0941\u0932\u0924\u093E \u0939\u0948",
780
+ conceptStripTitle: "\u092F\u093E \u0915\u093F\u0938\u0940 \u0905\u0935\u0927\u093E\u0930\u0923\u093E \u0938\u0947 \u0936\u0941\u0930\u0942 \u0915\u0930\u0947\u0902",
781
+ conceptStripLabel: "\u091C\u093F\u0928 \u0905\u0935\u0927\u093E\u0930\u0923\u093E\u0913\u0902 \u0915\u0947 \u092C\u093E\u0930\u0947 \u092E\u0947\u0902 \u0906\u092A \u092A\u0942\u091B \u0938\u0915\u0924\u0947 \u0939\u0948\u0902",
782
+ conceptAsk: "{product} \u092E\u0947\u0902 {title} \u0915\u0947 \u092C\u093E\u0930\u0947 \u092E\u0947\u0902 \u092A\u0942\u091B\u0947\u0902",
783
+ conceptAskQuestion: "{product} \u092E\u0947\u0902 {title} \u0915\u0948\u0938\u0947 \u0915\u093E\u092E \u0915\u0930\u0924\u093E \u0939\u0948?",
784
+ copyAnswer: "\u0909\u0924\u094D\u0924\u0930 \u0915\u0949\u092A\u0940 \u0915\u0930\u0947\u0902",
785
+ copyFailed: "\u0915\u0949\u092A\u0940 \u0928\u0939\u0940\u0902 \u0939\u094B \u0938\u0915\u093E",
786
+ readAloud: "\u091C\u093C\u094B\u0930 \u0938\u0947 \u092A\u0922\u093C\u0947\u0902",
787
+ stopReading: "\u092A\u0922\u093C\u0928\u093E \u092C\u0902\u0926 \u0915\u0930\u0947\u0902",
788
+ answerTable: "\u0924\u093E\u0932\u093F\u0915\u093E",
281
789
  groundedIn: "\u092F\u0939 \u0909\u0924\u094D\u0924\u0930 \u0915\u093F\u0938 \u092A\u0930 \u0906\u0927\u093E\u0930\u093F\u0924 \u0939\u0948",
282
790
  sourceCountOne: "1 \u0938\u094D\u0930\u094B\u0924",
283
791
  sourceCountMany: "{count} \u0938\u094D\u0930\u094B\u0924",
@@ -312,7 +820,7 @@ var ES = {
312
820
  showLess: "Ver menos",
313
821
  sources: "Fuentes",
314
822
  nextYouCouldAsk: "Tambi\xE9n puedes preguntar",
315
- placeholder: "Describe el problema que tienes\u2026",
823
+ placeholder: "Describe el problema\u2026",
316
824
  placeholderProduct: "Describe un problema o pregunta por {product}\u2026",
317
825
  send: "Enviar",
318
826
  stop: "Detener",
@@ -350,6 +858,20 @@ var ES = {
350
858
  feedbackDismiss: "Cerrar",
351
859
  feedbackThanks: "Gracias, nos sirve.",
352
860
  feedbackNotSent: "No se ha podido enviar.",
861
+ escalateOpen: "Hablar con una persona",
862
+ escalateTitle: "Env\xEDa esta conversaci\xF3n a una persona",
863
+ escalateBody: "Le pasaremos la conversaci\xF3n entera, as\xED no tienes que volver a escribirla.",
864
+ escalateEmailLabel: "Tu direcci\xF3n de correo",
865
+ escalateEmailPlaceholder: "tu@ejemplo.com",
866
+ escalateNoteLabel: "\xBFAlgo m\xE1s que deber\xEDamos saber? (opcional)",
867
+ escalateSubmit: "Enviar a una persona",
868
+ escalateSending: "Enviando\u2026",
869
+ escalateCancel: "Cancelar",
870
+ escalateInvalidEmail: "No parece una direcci\xF3n a la que podamos responder. Rev\xEDsala, por favor.",
871
+ escalateSent: "Una persona ya tiene esta conversaci\xF3n. Te responderemos a {email}.",
872
+ escalateRateLimited: "Han sido muchas peticiones en poco tiempo. Int\xE9ntalo de nuevo en unos minutos.",
873
+ escalateCaptcha: "No hemos podido verificar que eres una persona. Int\xE9ntalo de nuevo.",
874
+ escalateFailed: "No hemos podido pasarlo ahora mismo. Int\xE9ntalo de nuevo.",
353
875
  prevSuggestion: "Sugerencia anterior",
354
876
  nextSuggestion: "Sugerencia siguiente",
355
877
  conceptIllustration: "Ilustraci\xF3n del concepto",
@@ -397,7 +919,7 @@ var AR = {
397
919
  showLess: "\u0639\u0631\u0636 \u0623\u0642\u0644",
398
920
  sources: "\u0627\u0644\u0645\u0635\u0627\u062F\u0631",
399
921
  nextYouCouldAsk: "\u064A\u0645\u0643\u0646\u0643 \u0623\u0646 \u062A\u0633\u0623\u0644 \u0628\u0639\u062F \u0630\u0644\u0643",
400
- placeholder: "\u0635\u0641 \u0627\u0644\u0645\u0634\u0643\u0644\u0629 \u0627\u0644\u062A\u064A \u062A\u0648\u0627\u062C\u0647\u0647\u0627\u2026",
922
+ placeholder: "\u0635\u0641 \u0627\u0644\u0645\u0634\u0643\u0644\u0629\u2026",
401
923
  placeholderProduct: "\u0635\u0641 \u0645\u0634\u0643\u0644\u062A\u0643 \u0623\u0648 \u0627\u0633\u0623\u0644 \u0639\u0646 {product}\u2026",
402
924
  send: "\u0625\u0631\u0633\u0627\u0644",
403
925
  stop: "\u0625\u064A\u0642\u0627\u0641",
@@ -435,6 +957,20 @@ var AR = {
435
957
  feedbackDismiss: "\u0625\u063A\u0644\u0627\u0642",
436
958
  feedbackThanks: "\u0634\u0643\u0631\u064B\u0627\u060C \u0647\u0630\u0627 \u064A\u0633\u0627\u0639\u062F\u0646\u0627.",
437
959
  feedbackNotSent: "\u0644\u0645 \u064A\u062A\u0645 \u0627\u0644\u0625\u0631\u0633\u0627\u0644.",
960
+ escalateOpen: "\u062A\u0648\u0627\u0635\u0644 \u0645\u0639 \u0634\u062E\u0635",
961
+ escalateTitle: "\u0623\u0631\u0633\u0644 \u0647\u0630\u0647 \u0627\u0644\u0645\u062D\u0627\u062F\u062B\u0629 \u0625\u0644\u0649 \u0634\u062E\u0635",
962
+ escalateBody: "\u0633\u0646\u0645\u0631\u0651\u0631 \u0627\u0644\u0645\u062D\u0627\u062F\u062B\u0629 \u0643\u0627\u0645\u0644\u0629\u060C \u062D\u062A\u0649 \u0644\u0627 \u062A\u0636\u0637\u0631 \u0625\u0644\u0649 \u0643\u062A\u0627\u0628\u062A\u0647\u0627 \u0645\u0646 \u062C\u062F\u064A\u062F.",
963
+ escalateEmailLabel: "\u0628\u0631\u064A\u062F\u0643 \u0627\u0644\u0625\u0644\u0643\u062A\u0631\u0648\u0646\u064A",
964
+ escalateEmailPlaceholder: "you@example.com",
965
+ escalateNoteLabel: "\u0647\u0644 \u0645\u0646 \u0634\u064A\u0621 \u0622\u062E\u0631 \u064A\u0646\u0628\u063A\u064A \u0623\u0646 \u0646\u0639\u0631\u0641\u0647\u061F (\u0627\u062E\u062A\u064A\u0627\u0631\u064A)",
966
+ escalateSubmit: "\u0625\u0631\u0633\u0627\u0644 \u0625\u0644\u0649 \u0634\u062E\u0635",
967
+ escalateSending: "\u062C\u0627\u0631\u064D \u0627\u0644\u0625\u0631\u0633\u0627\u0644\u2026",
968
+ escalateCancel: "\u0625\u0644\u063A\u0627\u0621",
969
+ escalateInvalidEmail: "\u0644\u0627 \u064A\u0628\u062F\u0648 \u0647\u0630\u0627 \u0639\u0646\u0648\u0627\u0646\u064B\u0627 \u064A\u0645\u0643\u0646\u0646\u0627 \u0627\u0644\u0631\u062F \u0639\u0644\u064A\u0647. \u064A\u0631\u062C\u0649 \u0627\u0644\u062A\u062D\u0642\u0642 \u0645\u0646\u0647.",
970
+ escalateSent: "\u0623\u0635\u0628\u062D\u062A \u0647\u0630\u0647 \u0627\u0644\u0645\u062D\u0627\u062F\u062B\u0629 \u0644\u062F\u0649 \u0623\u062D\u062F \u0627\u0644\u0632\u0645\u0644\u0627\u0621. \u0633\u0646\u0631\u062F \u0639\u0644\u0649 {email}.",
971
+ escalateRateLimited: "\u0637\u0644\u0628\u0627\u062A \u0643\u062B\u064A\u0631\u0629 \u062E\u0644\u0627\u0644 \u0648\u0642\u062A \u0642\u0635\u064A\u0631. \u064A\u0631\u062C\u0649 \u0627\u0644\u0645\u062D\u0627\u0648\u0644\u0629 \u0628\u0639\u062F \u0628\u0636\u0639 \u062F\u0642\u0627\u0626\u0642.",
972
+ escalateCaptcha: "\u062A\u0639\u0630\u0651\u0631 \u0627\u0644\u062A\u062D\u0642\u0642 \u0645\u0646 \u0623\u0646\u0643 \u0644\u0633\u062A \u0631\u0648\u0628\u0648\u062A\u064B\u0627. \u064A\u0631\u062C\u0649 \u0627\u0644\u0645\u062D\u0627\u0648\u0644\u0629 \u0645\u0631\u0629 \u0623\u062E\u0631\u0649.",
973
+ escalateFailed: "\u062A\u0639\u0630\u0651\u0631 \u062A\u0645\u0631\u064A\u0631 \u0627\u0644\u0645\u062D\u0627\u062F\u062B\u0629 \u0627\u0644\u0622\u0646. \u064A\u0631\u062C\u0649 \u0627\u0644\u0645\u062D\u0627\u0648\u0644\u0629 \u0645\u0631\u0629 \u0623\u062E\u0631\u0649.",
438
974
  prevSuggestion: "\u0627\u0644\u0627\u0642\u062A\u0631\u0627\u062D \u0627\u0644\u0633\u0627\u0628\u0642",
439
975
  nextSuggestion: "\u0627\u0644\u0627\u0642\u062A\u0631\u0627\u062D \u0627\u0644\u062A\u0627\u0644\u064A",
440
976
  conceptIllustration: "\u0631\u0633\u0645 \u062A\u0648\u0636\u064A\u062D\u064A \u0644\u0644\u0645\u0641\u0647\u0648\u0645",
@@ -482,7 +1018,7 @@ var TA = {
482
1018
  showLess: "\u0B95\u0BC1\u0BB1\u0BC8\u0BB5\u0BBE\u0B95\u0B95\u0BCD \u0B95\u0BBE\u0B9F\u0BCD\u0B9F\u0BC1",
483
1019
  sources: "\u0BAE\u0BC2\u0BB2\u0B99\u0BCD\u0B95\u0BB3\u0BCD",
484
1020
  nextYouCouldAsk: "\u0B85\u0B9F\u0BC1\u0BA4\u0BCD\u0BA4\u0BC1 \u0BA8\u0BC0\u0B99\u0BCD\u0B95\u0BB3\u0BCD \u0B95\u0BC7\u0B9F\u0BCD\u0B95\u0BB2\u0BBE\u0BAE\u0BCD",
485
- placeholder: "\u0BA8\u0BC0\u0B99\u0BCD\u0B95\u0BB3\u0BCD \u0B8E\u0BA4\u0BBF\u0BB0\u0BCD\u0B95\u0BCA\u0BB3\u0BCD\u0BB3\u0BC1\u0BAE\u0BCD \u0B9A\u0BBF\u0B95\u0BCD\u0B95\u0BB2\u0BC8 \u0BB5\u0BBF\u0BB5\u0BB0\u0BBF\u0BAF\u0BC1\u0B99\u0BCD\u0B95\u0BB3\u0BCD\u2026",
1021
+ placeholder: "\u0B9A\u0BBF\u0B95\u0BCD\u0B95\u0BB2\u0BC8 \u0BB5\u0BBF\u0BB5\u0BB0\u0BBF\u0BAF\u0BC1\u0B99\u0BCD\u0B95\u0BB3\u0BCD\u2026",
486
1022
  placeholderProduct: "\u0B9A\u0BBF\u0B95\u0BCD\u0B95\u0BB2\u0BC8 \u0BB5\u0BBF\u0BB5\u0BB0\u0BBF\u0BAF\u0BC1\u0B99\u0BCD\u0B95\u0BB3\u0BCD, \u0B85\u0BB2\u0BCD\u0BB2\u0BA4\u0BC1 {product} \u0BAA\u0BB1\u0BCD\u0BB1\u0BBF\u0B95\u0BCD \u0B95\u0BC7\u0BB3\u0BC1\u0B99\u0BCD\u0B95\u0BB3\u0BCD\u2026",
487
1023
  send: "\u0B85\u0BA9\u0BC1\u0BAA\u0BCD\u0BAA\u0BC1",
488
1024
  stop: "\u0BA8\u0BBF\u0BB1\u0BC1\u0BA4\u0BCD\u0BA4\u0BC1",
@@ -520,6 +1056,20 @@ var TA = {
520
1056
  feedbackDismiss: "\u0BAE\u0BC2\u0B9F\u0BC1",
521
1057
  feedbackThanks: "\u0BA8\u0BA9\u0BCD\u0BB1\u0BBF, \u0B87\u0BA4\u0BC1 \u0B8E\u0B99\u0BCD\u0B95\u0BB3\u0BC1\u0B95\u0BCD\u0B95\u0BC1 \u0B89\u0BA4\u0BB5\u0BC1\u0B95\u0BBF\u0BB1\u0BA4\u0BC1.",
522
1058
  feedbackNotSent: "\u0B85\u0BA9\u0BC1\u0BAA\u0BCD\u0BAA \u0BAE\u0BC1\u0B9F\u0BBF\u0BAF\u0BB5\u0BBF\u0BB2\u0BCD\u0BB2\u0BC8.",
1059
+ escalateOpen: "\u0B92\u0BB0\u0BC1\u0BB5\u0BB0\u0BBF\u0B9F\u0BAE\u0BCD \u0BAA\u0BC7\u0B9A\u0BC1\u0B99\u0BCD\u0B95\u0BB3\u0BCD",
1060
+ escalateTitle: "\u0B87\u0BA8\u0BCD\u0BA4 \u0B89\u0BB0\u0BC8\u0BAF\u0BBE\u0B9F\u0BB2\u0BC8 \u0B92\u0BB0\u0BC1\u0BB5\u0BB0\u0BC1\u0B95\u0BCD\u0B95\u0BC1 \u0B85\u0BA9\u0BC1\u0BAA\u0BCD\u0BAA\u0BC1\u0B99\u0BCD\u0B95\u0BB3\u0BCD",
1061
+ escalateBody: "\u0BAE\u0BC1\u0BB4\u0BC1 \u0B89\u0BB0\u0BC8\u0BAF\u0BBE\u0B9F\u0BB2\u0BC8\u0BAF\u0BC1\u0BAE\u0BCD \u0BA8\u0BBE\u0B99\u0BCD\u0B95\u0BB3\u0BCD \u0B85\u0BA9\u0BC1\u0BAA\u0BCD\u0BAA\u0BBF\u0BB5\u0BBF\u0B9F\u0BC1\u0BB5\u0BCB\u0BAE\u0BCD; \u0BA8\u0BC0\u0B99\u0BCD\u0B95\u0BB3\u0BCD \u0BAE\u0BC0\u0BA3\u0BCD\u0B9F\u0BC1\u0BAE\u0BCD \u0BA4\u0B9F\u0BCD\u0B9F\u0B9A\u0BCD\u0B9A\u0BC1 \u0B9A\u0BC6\u0BAF\u0BCD\u0BAF \u0BB5\u0BC7\u0BA3\u0BCD\u0B9F\u0BBF\u0BAF\u0BA4\u0BBF\u0BB2\u0BCD\u0BB2\u0BC8.",
1062
+ escalateEmailLabel: "\u0B89\u0B99\u0BCD\u0B95\u0BB3\u0BCD \u0BAE\u0BBF\u0BA9\u0BCD\u0BA9\u0B9E\u0BCD\u0B9A\u0BB2\u0BCD \u0BAE\u0BC1\u0B95\u0BB5\u0BB0\u0BBF",
1063
+ escalateEmailPlaceholder: "you@example.com",
1064
+ escalateNoteLabel: "\u0BB5\u0BC7\u0BB1\u0BC1 \u0B8F\u0BA4\u0BC7\u0BA9\u0BC1\u0BAE\u0BCD \u0B9A\u0BCA\u0BB2\u0BCD\u0BB2 \u0BB5\u0BBF\u0BB0\u0BC1\u0BAE\u0BCD\u0BAA\u0BC1\u0B95\u0BBF\u0BB1\u0BC0\u0BB0\u0BCD\u0B95\u0BB3\u0BBE? (\u0BB5\u0BBF\u0BB0\u0BC1\u0BAA\u0BCD\u0BAA\u0BAE\u0BCD)",
1065
+ escalateSubmit: "\u0B92\u0BB0\u0BC1\u0BB5\u0BB0\u0BC1\u0B95\u0BCD\u0B95\u0BC1 \u0B85\u0BA9\u0BC1\u0BAA\u0BCD\u0BAA\u0BC1",
1066
+ escalateSending: "\u0B85\u0BA9\u0BC1\u0BAA\u0BCD\u0BAA\u0BAA\u0BCD\u0BAA\u0B9F\u0BC1\u0B95\u0BBF\u0BB1\u0BA4\u0BC1\u2026",
1067
+ escalateCancel: "\u0BB0\u0BA4\u0BCD\u0BA4\u0BC1 \u0B9A\u0BC6\u0BAF\u0BCD",
1068
+ escalateInvalidEmail: "\u0B87\u0BA4\u0BC1 \u0BA8\u0BBE\u0B99\u0BCD\u0B95\u0BB3\u0BCD \u0BAA\u0BA4\u0BBF\u0BB2\u0BB3\u0BBF\u0B95\u0BCD\u0B95\u0B95\u0BCD\u0B95\u0BC2\u0B9F\u0BBF\u0BAF \u0BAE\u0BBF\u0BA9\u0BCD\u0BA9\u0B9E\u0BCD\u0B9A\u0BB2\u0BCD \u0BAE\u0BC1\u0B95\u0BB5\u0BB0\u0BBF \u0BAA\u0BCB\u0BB2\u0BCD \u0BA4\u0BC6\u0BB0\u0BBF\u0BAF\u0BB5\u0BBF\u0BB2\u0BCD\u0BB2\u0BC8. \u0B9A\u0BB0\u0BBF\u0BAA\u0BBE\u0BB0\u0BCD\u0B95\u0BCD\u0B95\u0BB5\u0BC1\u0BAE\u0BCD.",
1069
+ escalateSent: "\u0B87\u0BA8\u0BCD\u0BA4 \u0B89\u0BB0\u0BC8\u0BAF\u0BBE\u0B9F\u0BB2\u0BCD \u0B87\u0BAA\u0BCD\u0BAA\u0BCB\u0BA4\u0BC1 \u0B92\u0BB0\u0BC1\u0BB5\u0BB0\u0BBF\u0B9F\u0BAE\u0BCD \u0B89\u0BB3\u0BCD\u0BB3\u0BA4\u0BC1. {email} \u0BAE\u0BC1\u0B95\u0BB5\u0BB0\u0BBF\u0B95\u0BCD\u0B95\u0BC1\u0BAA\u0BCD \u0BAA\u0BA4\u0BBF\u0BB2\u0BB3\u0BBF\u0BAA\u0BCD\u0BAA\u0BCB\u0BAE\u0BCD.",
1070
+ escalateRateLimited: "\u0B95\u0BC1\u0BB1\u0BC1\u0B95\u0BBF\u0BAF \u0BA8\u0BC7\u0BB0\u0BA4\u0BCD\u0BA4\u0BBF\u0BB2\u0BCD \u0B85\u0BA4\u0BBF\u0B95 \u0B95\u0BCB\u0BB0\u0BBF\u0B95\u0BCD\u0B95\u0BC8\u0B95\u0BB3\u0BCD. \u0B9A\u0BBF\u0BB2 \u0BA8\u0BBF\u0BAE\u0BBF\u0B9F\u0B99\u0BCD\u0B95\u0BB3\u0BCD \u0B95\u0BB4\u0BBF\u0BA4\u0BCD\u0BA4\u0BC1 \u0BAE\u0BC1\u0BAF\u0BB1\u0BCD\u0B9A\u0BBF\u0B95\u0BCD\u0B95\u0BB5\u0BC1\u0BAE\u0BCD.",
1071
+ escalateCaptcha: "\u0BA8\u0BC0\u0B99\u0BCD\u0B95\u0BB3\u0BCD \u0BAE\u0BA9\u0BBF\u0BA4\u0BB0\u0BCD \u0B8E\u0BA9\u0BCD\u0BAA\u0BA4\u0BC8 \u0B89\u0BB1\u0BC1\u0BA4\u0BBF\u0B9A\u0BC6\u0BAF\u0BCD\u0BAF \u0BAE\u0BC1\u0B9F\u0BBF\u0BAF\u0BB5\u0BBF\u0BB2\u0BCD\u0BB2\u0BC8. \u0BAE\u0BC0\u0BA3\u0BCD\u0B9F\u0BC1\u0BAE\u0BCD \u0BAE\u0BC1\u0BAF\u0BB1\u0BCD\u0B9A\u0BBF\u0B95\u0BCD\u0B95\u0BB5\u0BC1\u0BAE\u0BCD.",
1072
+ escalateFailed: "\u0B87\u0BAA\u0BCD\u0BAA\u0BCB\u0BA4\u0BC1 \u0B85\u0BA9\u0BC1\u0BAA\u0BCD\u0BAA \u0BAE\u0BC1\u0B9F\u0BBF\u0BAF\u0BB5\u0BBF\u0BB2\u0BCD\u0BB2\u0BC8. \u0BAE\u0BC0\u0BA3\u0BCD\u0B9F\u0BC1\u0BAE\u0BCD \u0BAE\u0BC1\u0BAF\u0BB1\u0BCD\u0B9A\u0BBF\u0B95\u0BCD\u0B95\u0BB5\u0BC1\u0BAE\u0BCD.",
523
1073
  prevSuggestion: "\u0BAE\u0BC1\u0BA8\u0BCD\u0BA4\u0BC8\u0BAF \u0BAA\u0BB0\u0BBF\u0BA8\u0BCD\u0BA4\u0BC1\u0BB0\u0BC8",
524
1074
  nextSuggestion: "\u0B85\u0B9F\u0BC1\u0BA4\u0BCD\u0BA4 \u0BAA\u0BB0\u0BBF\u0BA8\u0BCD\u0BA4\u0BC1\u0BB0\u0BC8",
525
1075
  conceptIllustration: "\u0B95\u0BB0\u0BC1\u0BA4\u0BCD\u0BA4\u0BC1 \u0BB5\u0BBF\u0BB3\u0B95\u0BCD\u0B95\u0BAA\u0BCD\u0BAA\u0B9F\u0BAE\u0BCD",
@@ -762,6 +1312,29 @@ var SUBMIT_EXPLORE_FEEDBACK_MUTATION = (
762
1312
  `
763
1313
  );
764
1314
  var EXPLORE_FEEDBACK_COMMENT_SOFT_MAX = 500;
1315
+ var ESCALATE_EXPLORE_CONVERSATION_MUTATION = (
1316
+ /* GraphQL */
1317
+ `
1318
+ mutation EscalateExploreConversation(
1319
+ $input: EscalateExploreConversationInput!
1320
+ ) {
1321
+ escalateExploreConversation(input: $input) {
1322
+ created
1323
+ escalation {
1324
+ email
1325
+ createdAt
1326
+ }
1327
+ }
1328
+ }
1329
+ `
1330
+ );
1331
+ var EXPLORE_ESCALATION_NOTE_SOFT_MAX = 2e3;
1332
+ var DELIVERABLE_EMAIL = /^[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?\.)+[A-Za-z]{2,}$/;
1333
+ function looksLikeDeliverableEmail(value) {
1334
+ const trimmed = value.trim();
1335
+ if (trimmed.length === 0 || trimmed.length > 254) return false;
1336
+ return DELIVERABLE_EMAIL.test(trimmed);
1337
+ }
765
1338
  var CREATE_EXPLORE_SHARE_LINK_MUTATION = (
766
1339
  /* GraphQL */
767
1340
  `
@@ -782,48 +1355,6 @@ var EXPLORE_SHARED_CONVERSATION_QUERY = (
782
1355
  }
783
1356
  `
784
1357
  );
785
- function MarkdownLink({
786
- href,
787
- children,
788
- // react-markdown passes the hast node to every override; it is not a DOM attribute.
789
- node: _node,
790
- ...props
791
- }) {
792
- const isExternal = typeof href === "string" && (href.startsWith("http://") || href.startsWith("https://"));
793
- return /* @__PURE__ */ jsxRuntime.jsx(
794
- "a",
795
- {
796
- href,
797
- ...props,
798
- ...isExternal ? { target: "_blank", rel: "noopener noreferrer" } : { rel: "noopener noreferrer" },
799
- children
800
- }
801
- );
802
- }
803
- var defaultComponents = {
804
- a: MarkdownLink
805
- };
806
- function Markdown({
807
- children,
808
- className,
809
- inline,
810
- components
811
- }) {
812
- const content = /* @__PURE__ */ jsxRuntime.jsx(
813
- ReactMarkdown__default.default,
814
- {
815
- remarkPlugins: [remarkGfm__default.default],
816
- rehypePlugins: [rehypeSanitize__default.default],
817
- components: { ...defaultComponents, ...components },
818
- ...inline ? { disallowedElements: ["p"], unwrapDisallowed: true } : {},
819
- children
820
- }
821
- );
822
- if (inline) {
823
- return /* @__PURE__ */ jsxRuntime.jsx("span", { className, children: content });
824
- }
825
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className, children: content });
826
- }
827
1358
  var PROSE_CSS = `
828
1359
  .boff-prose {
829
1360
  max-width: none;
@@ -1220,6 +1751,10 @@ function useWebSDK() {
1220
1751
  }
1221
1752
  return client;
1222
1753
  }
1754
+ function useWebSDKConfig() {
1755
+ const client = useWebSDK();
1756
+ return client.getConfig();
1757
+ }
1223
1758
 
1224
1759
  // src/data/products.ts
1225
1760
  var ECOSYSTEM_PRODUCTS = [
@@ -1566,6 +2101,25 @@ var VALIDATION_CODES = /* @__PURE__ */ new Set([
1566
2101
  "CONVERSATION_LIMIT",
1567
2102
  "INVALID_INPUT"
1568
2103
  ]);
2104
+ function escalateOutcomeForCode(code) {
2105
+ switch (code) {
2106
+ case "INVALID_EMAIL":
2107
+ return "invalid-email";
2108
+ case "RATE_LIMITED":
2109
+ case "HTTP_429":
2110
+ return "rate-limited";
2111
+ case "CAPTCHA_REQUIRED":
2112
+ case "CAPTCHA_FAILED":
2113
+ return "captcha";
2114
+ // The token owns nothing, or owns a different conversation. The server answers NOT_FOUND
2115
+ // rather than FORBIDDEN so the endpoint is not an oracle for whether a thread exists;
2116
+ // either way there is nothing this visitor can do about it, so it reads as a failure.
2117
+ case "NOT_FOUND":
2118
+ return "failed";
2119
+ default:
2120
+ return "failed";
2121
+ }
2122
+ }
1569
2123
  function classifyExploreError(code, serverMessage) {
1570
2124
  const c = code ?? "UNKNOWN";
1571
2125
  if (RATE_LIMIT_CODES.has(c)) {
@@ -1732,6 +2286,7 @@ function useExploreChat(options = {}) {
1732
2286
  onSend
1733
2287
  } = options;
1734
2288
  const client = useWebSDK();
2289
+ const { productId } = useWebSDKConfig();
1735
2290
  const promptKey = JSON.stringify(examplePrompts ?? []);
1736
2291
  const welcomeKey = JSON.stringify(fallbackWelcome ?? null);
1737
2292
  const fallbackCatalog = React.useMemo(
@@ -1755,6 +2310,7 @@ function useExploreChat(options = {}) {
1755
2310
  const pollGenerationRef = React.useRef(0);
1756
2311
  const pollTimerRef = React.useRef(null);
1757
2312
  const conversationTokenRef = React.useRef(null);
2313
+ const [hasConversation, setHasConversation] = React.useState(false);
1758
2314
  const shareTokenRef = React.useRef(void 0);
1759
2315
  if (shareTokenRef.current === void 0) {
1760
2316
  shareTokenRef.current = readShareToken();
@@ -1769,6 +2325,10 @@ function useExploreChat(options = {}) {
1769
2325
  );
1770
2326
  const inFlightRef = React.useRef(false);
1771
2327
  catalogRef.current = catalog;
2328
+ const rememberConversationToken = React.useCallback((token) => {
2329
+ conversationTokenRef.current = token;
2330
+ setHasConversation(token !== null);
2331
+ }, []);
1772
2332
  const clearPollTimer = React.useCallback(() => {
1773
2333
  if (pollTimerRef.current !== null) {
1774
2334
  clearTimeout(pollTimerRef.current);
@@ -1835,7 +2395,7 @@ function useExploreChat(options = {}) {
1835
2395
  setArchive(readArchive(storageKey));
1836
2396
  const stored = readPersisted(storageKey);
1837
2397
  if (stored) {
1838
- conversationTokenRef.current = stored.conversationToken;
2398
+ rememberConversationToken(stored.conversationToken);
1839
2399
  focusProductRef.current = stored.focusProduct;
1840
2400
  const settled = stored.messages.flatMap((m) => {
1841
2401
  if (m.role !== "ASSISTANT") return [m];
@@ -1851,7 +2411,7 @@ function useExploreChat(options = {}) {
1851
2411
  setTurnCount(turns);
1852
2412
  }
1853
2413
  setHydrated(true);
1854
- }, [persist, storageKey]);
2414
+ }, [persist, rememberConversationToken, storageKey]);
1855
2415
  React.useEffect(() => {
1856
2416
  if (!persist || !hydrated || typeof window === "undefined") return;
1857
2417
  try {
@@ -2044,7 +2604,7 @@ function useExploreChat(options = {}) {
2044
2604
  setPhase(classified.kind === "rate-limit" ? "limited" : "error");
2045
2605
  return;
2046
2606
  }
2047
- conversationTokenRef.current = result.conversation.token;
2607
+ rememberConversationToken(result.conversation.token);
2048
2608
  turnCountRef.current = result.conversation.turnCount;
2049
2609
  setTurnCount(result.conversation.turnCount);
2050
2610
  setRemainingToday(result.remainingToday ?? null);
@@ -2070,7 +2630,14 @@ function useExploreChat(options = {}) {
2070
2630
  setPhase("streaming");
2071
2631
  startPolling(result.conversation.token, assistantMessage.id);
2072
2632
  },
2073
- [client, locale, pageUrl, productSlug, startPolling]
2633
+ [
2634
+ client,
2635
+ locale,
2636
+ pageUrl,
2637
+ productSlug,
2638
+ rememberConversationToken,
2639
+ startPolling
2640
+ ]
2074
2641
  );
2075
2642
  const send = React.useCallback(
2076
2643
  async (message, attachments = []) => {
@@ -2187,7 +2754,7 @@ function useExploreChat(options = {}) {
2187
2754
  );
2188
2755
  cancelPolling();
2189
2756
  inFlightRef.current = false;
2190
- conversationTokenRef.current = null;
2757
+ rememberConversationToken(null);
2191
2758
  lastSentTextRef.current = null;
2192
2759
  lastSentAttachmentsRef.current = [];
2193
2760
  turnCountRef.current = 0;
@@ -2202,7 +2769,7 @@ function useExploreChat(options = {}) {
2202
2769
  } catch {
2203
2770
  }
2204
2771
  }
2205
- }, [cancelPolling, storageKey]);
2772
+ }, [cancelPolling, rememberConversationToken, storageKey]);
2206
2773
  const openConversation = React.useCallback(
2207
2774
  (token) => {
2208
2775
  const entry = readArchive(storageKey).find((e) => e.token === token);
@@ -2214,7 +2781,7 @@ function useExploreChat(options = {}) {
2214
2781
  });
2215
2782
  cancelPolling();
2216
2783
  inFlightRef.current = false;
2217
- conversationTokenRef.current = entry.token;
2784
+ rememberConversationToken(entry.token);
2218
2785
  turnCountRef.current = entry.messages.filter(
2219
2786
  (m) => m.role === "USER"
2220
2787
  ).length;
@@ -2226,7 +2793,7 @@ function useExploreChat(options = {}) {
2226
2793
  setPhase(catalogRef.current.enabled ? "ready" : "disabled");
2227
2794
  setArchive(readArchive(storageKey));
2228
2795
  },
2229
- [cancelPolling, storageKey]
2796
+ [cancelPolling, rememberConversationToken, storageKey]
2230
2797
  );
2231
2798
  const deleteConversation = React.useCallback(
2232
2799
  (token) => {
@@ -2315,6 +2882,69 @@ function useExploreChat(options = {}) {
2315
2882
  },
2316
2883
  [client]
2317
2884
  );
2885
+ const escalate = React.useCallback(
2886
+ async (email, note) => {
2887
+ const token = conversationTokenRef.current;
2888
+ if (!token) {
2889
+ return { outcome: "no-conversation", email: null, createdAt: null };
2890
+ }
2891
+ const attempt = async (captchaAttempt) => {
2892
+ const activeCatalog = catalogRef.current;
2893
+ let recaptchaToken;
2894
+ if (activeCatalog.captchaRequired && activeCatalog.recaptchaSiteKey) {
2895
+ try {
2896
+ recaptchaToken = await getRecaptchaV3Token(
2897
+ activeCatalog.recaptchaSiteKey
2898
+ );
2899
+ } catch (err) {
2900
+ console.warn(
2901
+ "[WebSDK] reCAPTCHA v3 token could not be minted",
2902
+ err
2903
+ );
2904
+ }
2905
+ }
2906
+ const input = {
2907
+ conversationToken: token,
2908
+ email: email.trim(),
2909
+ note: note?.trim() ? note.trim() : null,
2910
+ ...recaptchaToken ? { recaptchaToken } : {},
2911
+ ...productId ? { productId } : {}
2912
+ };
2913
+ try {
2914
+ const res = await client.mutate(
2915
+ ESCALATE_EXPLORE_CONVERSATION_MUTATION,
2916
+ { input }
2917
+ );
2918
+ const first = res?.errors?.[0];
2919
+ const result = res?.data?.escalateExploreConversation;
2920
+ if (first || !result) {
2921
+ const code = typeof first?.extensions?.code === "string" ? first.extensions.code : "ESCALATION_FAILED";
2922
+ if (code === "CAPTCHA_FAILED" && captchaAttempt === 0) {
2923
+ return attempt(1);
2924
+ }
2925
+ return {
2926
+ outcome: escalateOutcomeForCode(code),
2927
+ email: null,
2928
+ createdAt: null
2929
+ };
2930
+ }
2931
+ return {
2932
+ // `created: false` is the double-click, the retry, the reload. The handover
2933
+ // exists, so this is a success — and the address comes from the SERVER, because
2934
+ // on a repeat submit the reply goes to whatever the FIRST handover used.
2935
+ outcome: result.created ? "sent" : "already-handed-over",
2936
+ email: result.escalation?.email ?? null,
2937
+ createdAt: result.escalation?.createdAt ?? null
2938
+ };
2939
+ } catch {
2940
+ return { outcome: "failed", email: null, createdAt: null };
2941
+ }
2942
+ };
2943
+ return attempt(0);
2944
+ },
2945
+ [client, productId]
2946
+ );
2947
+ const canEscalate = hasConversation && !isSharedView;
2318
2948
  return {
2319
2949
  phase,
2320
2950
  catalog,
@@ -2335,6 +2965,8 @@ function useExploreChat(options = {}) {
2335
2965
  clearHistory,
2336
2966
  recordClick,
2337
2967
  submitFeedback,
2968
+ escalate,
2969
+ canEscalate,
2338
2970
  createShareLink,
2339
2971
  isSharedView
2340
2972
  };
@@ -3020,9 +3652,6 @@ function PageHead({
3020
3652
  ))
3021
3653
  ] });
3022
3654
  }
3023
- function cn(...inputs) {
3024
- return tailwindMerge.twMerge(clsx.clsx(inputs));
3025
- }
3026
3655
  var buttonVariants = classVarianceAuthority.cva(
3027
3656
  "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
3028
3657
  {
@@ -3260,163 +3889,27 @@ var EXPLORE_CSS = `
3260
3889
  .boff-explore-scroll::-webkit-scrollbar { width: 10px; }
3261
3890
  .boff-explore-scroll::-webkit-scrollbar-track { background: transparent; }
3262
3891
  .boff-explore-scroll::-webkit-scrollbar-thumb {
3263
- background-color: hsl(var(--border));
3264
- border: 3px solid transparent;
3265
- border-radius: 9999px;
3266
- background-clip: content-box;
3267
- }
3268
-
3269
- /* The product strip scrolls inside itself; a visible scrollbar would read as a broken
3270
- layout rather than an affordance, and reserving its gutter shifts the chips. */
3271
- .boff-explore-strip {
3272
- scrollbar-width: none;
3273
- -ms-overflow-style: none;
3274
- }
3275
- .boff-explore-strip::-webkit-scrollbar { display: none; }
3276
-
3277
- @keyframes boff-explore-fade-in {
3278
- from { opacity: 0; transform: translateY(2px); }
3279
- to { opacity: 1; transform: none; }
3280
- }
3281
- .boff-explore-fade { animation: boff-explore-fade-in 220ms ease-out; }
3282
- @media (prefers-reduced-motion: reduce) {
3283
- .boff-explore-fade { animation: none; }
3284
- }
3285
-
3286
- /* Nothing inside an answer may widen the page. Model output carries long URLs, code and
3287
- tables \u2014 each of those overflows a phone by default, and one overflowing child makes the
3288
- WHOLE document horizontally scrollable, not just the bubble. */
3289
- .boff-explore-body,
3290
- .boff-explore-body p,
3291
- .boff-explore-body li,
3292
- .boff-explore-body a,
3293
- .boff-explore-body h1,
3294
- .boff-explore-body h2,
3295
- .boff-explore-body h3 {
3296
- overflow-wrap: anywhere;
3297
- word-break: break-word;
3298
- }
3299
- .boff-explore-body pre {
3300
- overflow-x: auto;
3301
- max-width: 100%;
3302
- }
3303
- .boff-explore-body table {
3304
- display: block;
3305
- overflow-x: auto;
3306
- max-width: 100%;
3307
- }
3308
- .boff-explore-body img {
3309
- max-width: 100%;
3310
- height: auto;
3311
- }
3312
-
3313
- /* ---- Comparison and pricing tables ----------------------------------------------------
3314
- The harness emits a comparison, and a set of plans, as a GFM table whose FIRST column is
3315
- the subject \u2014 the product, or the plan (CONTRACT.md \xA77). Two things have to be true of it:
3316
- the page must not scroll sideways because of it, and it has to be readable on a 320px
3317
- phone, which a four-column grid of 1px borders is not.
3318
-
3319
- Wide: a contained scroller. \`max-width: 100%\` keeps the box inside the bubble and
3320
- \`overflow-x: auto\` keeps the columns inside the box, so the widest possible table still
3321
- cannot widen the document. \`overscroll-behavior-x: contain\` stops a swipe that runs out
3322
- of table from chaining to the page \u2014 the same rule the transcript needed. */
3323
- .boff-explore-body .boff-explore-table {
3324
- max-width: 100%;
3325
- overflow-x: auto;
3326
- overscroll-behavior-x: contain;
3327
- margin: 0.75rem 0;
3328
- border: 1px solid hsl(var(--border));
3329
- border-radius: 0.75rem;
3330
- background-color: hsl(var(--card));
3331
- }
3332
- .boff-explore-body .boff-explore-table:focus-visible {
3333
- outline: 2px solid hsl(var(--ring));
3334
- outline-offset: 2px;
3335
- }
3336
- .boff-explore-body .boff-explore-table table {
3337
- /* Beats the blanket \`display: block\` above, which is the unwrapped fallback. */
3338
- display: table;
3339
- width: 100%;
3340
- max-width: none;
3341
- margin: 0;
3342
- border-collapse: collapse;
3343
- font-size: 0.8125rem;
3344
- }
3345
- .boff-explore-body .boff-explore-table th,
3346
- .boff-explore-body .boff-explore-table td {
3347
- border: 0;
3348
- border-top: 1px solid hsl(var(--border));
3349
- padding: 0.5rem 0.75rem;
3350
- /* \`start\`, never \`left\`: the page root carries dir="rtl" in Arabic, and a left-aligned
3351
- table there reads against the grain of every other line on the page. */
3352
- text-align: start;
3353
- vertical-align: top;
3354
- overflow-wrap: anywhere;
3355
- /* Prices in a column line up under each other instead of drifting by digit width. */
3356
- font-variant-numeric: tabular-nums;
3357
- min-width: 6rem;
3358
- }
3359
- .boff-explore-body .boff-explore-table thead th {
3360
- border-top: 0;
3361
- background-color: hsl(var(--muted));
3362
- color: hsl(var(--foreground));
3363
- font-weight: 600;
3364
- white-space: nowrap;
3365
- }
3366
- /* The first column is what the row is ABOUT \u2014 the product, or the plan name. Reading it as
3367
- the row's heading is what makes a price list scannable rather than a grid of strings. */
3368
- .boff-explore-body .boff-explore-table tbody td:first-child {
3369
- font-weight: 600;
3370
- color: hsl(var(--card-foreground));
3371
- }
3372
-
3373
- /* Narrow: one card per row. Each cell prints its own column heading, so a value is never
3374
- stranded without the question it answers, and the subject cell becomes the card's title.
3375
- A media query rather than a measured width \u2014 these pages are serialised post-hydration,
3376
- so anything measured here would ship to every phone as static HTML. */
3377
- @media (max-width: 30rem) {
3378
- .boff-explore-body .boff-explore-table {
3379
- overflow-x: visible;
3380
- }
3381
- .boff-explore-body .boff-explore-table table,
3382
- .boff-explore-body .boff-explore-table tbody,
3383
- .boff-explore-body .boff-explore-table tr,
3384
- .boff-explore-body .boff-explore-table td {
3385
- display: block;
3386
- width: 100%;
3387
- min-width: 0;
3388
- }
3389
- .boff-explore-body .boff-explore-table thead {
3390
- display: none;
3391
- }
3392
- .boff-explore-body .boff-explore-table tr {
3393
- border-top: 1px solid hsl(var(--border));
3394
- padding: 0.5rem 0;
3395
- }
3396
- .boff-explore-body .boff-explore-table tr:first-child {
3397
- border-top: 0;
3398
- }
3399
- .boff-explore-body .boff-explore-table td {
3400
- border: 0;
3401
- padding: 0.125rem 0.75rem;
3402
- }
3403
- .boff-explore-body .boff-explore-table td[data-label]::before {
3404
- content: attr(data-label);
3405
- display: block;
3406
- font-size: 0.6875rem;
3407
- font-weight: 600;
3408
- text-transform: uppercase;
3409
- letter-spacing: 0.04em;
3410
- color: hsl(var(--muted-foreground));
3411
- }
3412
- .boff-explore-body .boff-explore-table tbody td:first-child {
3413
- font-size: 0.9375rem;
3414
- padding-bottom: 0.25rem;
3415
- }
3416
- .boff-explore-body .boff-explore-table tbody td:first-child::before {
3417
- /* The plan's own name is the heading here; repeating "Plan" over it says nothing. */
3418
- display: none;
3419
- }
3892
+ background-color: hsl(var(--border));
3893
+ border: 3px solid transparent;
3894
+ border-radius: 9999px;
3895
+ background-clip: content-box;
3896
+ }
3897
+
3898
+ /* The product strip scrolls inside itself; a visible scrollbar would read as a broken
3899
+ layout rather than an affordance, and reserving its gutter shifts the chips. */
3900
+ .boff-explore-strip {
3901
+ scrollbar-width: none;
3902
+ -ms-overflow-style: none;
3903
+ }
3904
+ .boff-explore-strip::-webkit-scrollbar { display: none; }
3905
+
3906
+ @keyframes boff-explore-fade-in {
3907
+ from { opacity: 0; transform: translateY(2px); }
3908
+ to { opacity: 1; transform: none; }
3909
+ }
3910
+ .boff-explore-fade { animation: boff-explore-fade-in 220ms ease-out; }
3911
+ @media (prefers-reduced-motion: reduce) {
3912
+ .boff-explore-fade { animation: none; }
3420
3913
  }
3421
3914
 
3422
3915
  /* ---- Ambient backdrop -----------------------------------------------------------------
@@ -3475,11 +3968,40 @@ var EXPLORE_CSS = `
3475
3968
  animation: boff-explore-sheen 1.5s ease-in-out infinite;
3476
3969
  }
3477
3970
 
3971
+ /* ---- The composer, before anyone has typed -----------------------------------------
3972
+ On the welcome screen the box IS the page: everything above it is a way of explaining
3973
+ what to put in it. Unhighlighted it reads as a search bar at the bottom of a landing
3974
+ page, which people scroll past \u2014 so while the conversation is empty it gets a soft halo
3975
+ in the site's own --primary and one slow breath of it, and the moment there is a
3976
+ transcript, or the visitor focuses or types, it drops back to the ordinary card it was.
3977
+ An input that keeps pulsing while you are using it is nagging, not inviting.
3978
+
3979
+ Colour comes from the product's --primary token, so it is whatever that site's palette
3980
+ already is, never a colour chosen here. */
3981
+ .boff-explore-invite {
3982
+ border-color: hsl(var(--primary) / 0.45);
3983
+ box-shadow:
3984
+ 0 0 0 4px hsl(var(--primary) / 0.08),
3985
+ 0 8px 24px -12px hsl(var(--primary) / 0.45);
3986
+ animation: boff-explore-breathe 3.2s ease-in-out infinite;
3987
+ }
3988
+ /* Focus and typing both mean "I have found it", and the hint has done its job. */
3989
+ .boff-explore-invite:focus-within,
3990
+ .boff-explore-invite.boff-explore-invite-quiet {
3991
+ animation: none;
3992
+ box-shadow: 0 0 0 4px hsl(var(--primary) / 0.10);
3993
+ }
3994
+ @keyframes boff-explore-breathe {
3995
+ 0%, 100% { box-shadow: 0 0 0 4px hsl(var(--primary) / 0.07), 0 8px 24px -12px hsl(var(--primary) / 0.40); }
3996
+ 50% { box-shadow: 0 0 0 7px hsl(var(--primary) / 0.13), 0 8px 28px -12px hsl(var(--primary) / 0.55); }
3997
+ }
3998
+
3478
3999
  /* Every one of the above is decoration. Reduced motion removes the movement and keeps the
3479
4000
  final state, rather than leaving content mid-animation and invisible. */
3480
4001
  @media (prefers-reduced-motion: reduce) {
3481
4002
  .boff-explore-aura::before,
3482
- .boff-explore-busy::after { animation: none; }
4003
+ .boff-explore-busy::after,
4004
+ .boff-explore-invite { animation: none; }
3483
4005
  .boff-explore-rise { animation: none; opacity: 1; transform: none; }
3484
4006
  }
3485
4007
 
@@ -3506,457 +4028,211 @@ var EXPLORE_CSS = `
3506
4028
  something is actually covering the bottom, so a snapshot taken
3507
4029
  with no keyboard open never carries it. */
3508
4030
  .boff-explore-viewport {
3509
- height: calc(100vh - var(--boff-explore-offset, 0px) - var(--boff-explore-inset, 0px));
3510
- }
3511
- @supports (height: 100dvh) {
3512
- .boff-explore-viewport {
3513
- height: calc(100dvh - var(--boff-explore-offset, 0px) - var(--boff-explore-inset, 0px));
3514
- }
3515
- }
3516
-
3517
- /* Landscape phones and other short viewports: a one-screen pane leaves a ~124px transcript
3518
- sliver under ~610px of chrome and content. At or below the threshold the pane stops
3519
- pretending to be a window and lets the page's own scrolling carry the transcript. The
3520
- min-height is the available height rather than a fixed rem value, so the fallback can
3521
- never be taller than the screen it is rescuing. */
3522
- @media (max-height: ${SHORT_VIEWPORT_PX}px) {
3523
- .boff-explore-viewport {
3524
- height: auto;
3525
- min-height: calc(100vh - var(--boff-explore-offset, 0px));
3526
- }
3527
- }
3528
- @supports (height: 100dvh) {
3529
- @media (max-height: ${SHORT_VIEWPORT_PX}px) {
3530
- .boff-explore-viewport {
3531
- min-height: calc(100dvh - var(--boff-explore-offset, 0px));
3532
- }
3533
- }
3534
- }
3535
-
3536
- /* ---- Composer dock --------------------------------------------------------------------
3537
- Three declarations, each the fallback for the one after it: a plain 1rem where \`max()\`
3538
- is unknown, then the measured overlap of any FOREIGN fixed chrome sitting over the
3539
- composer (the host sites' cookie banner is 229px tall and lands exactly there), then the
3540
- same plus the home-indicator inset. \`--boff-explore-chrome\` is set on the root only once
3541
- an overlap has actually been measured, so everywhere else this collapses to 1rem. */
3542
- .boff-explore-composer {
3543
- padding-bottom: 1rem;
3544
- padding-bottom: max(1rem, var(--boff-explore-chrome, 0px));
3545
- padding-bottom: max(1rem, env(safe-area-inset-bottom), var(--boff-explore-chrome, 0px));
3546
- }
3547
- `;
3548
- function ExploreStyles() {
3549
- return /* @__PURE__ */ jsxRuntime.jsx("style", { dangerouslySetInnerHTML: { __html: EXPLORE_CSS } });
3550
- }
3551
- var CAPABILITIES = [
3552
- {
3553
- icon: lucideReact.BookOpenText,
3554
- title: "Grounded in our own docs",
3555
- body: "Every answer comes from published product pages and documentation \u2014 never invented."
3556
- },
3557
- {
3558
- icon: lucideReact.Layers,
3559
- title: "Compare across the ecosystem",
3560
- body: "Ask which product fits your team, or how two of them work together."
3561
- },
3562
- {
3563
- icon: lucideReact.Link2,
3564
- title: "Straight to the right page",
3565
- body: "Get the sources and next steps \u2014 pricing, docs, a demo \u2014 without hunting."
3566
- }
3567
- ];
3568
- var MIN_VISIBLE_FRACTION = 0.25;
3569
- var SDK_OVERLAY_SELECTOR = "[data-boff-explore]";
3570
- var CTA_VARIANTS = {
3571
- CONTACT: "default",
3572
- WAITLIST: "default",
3573
- APP: "default",
3574
- PRICING: "secondary",
3575
- STORE: "secondary",
3576
- PARTNERS: "secondary",
3577
- DOCS: "outline",
3578
- SERVICES: "outline",
3579
- OTHER: "outline"
3580
- };
3581
- function normalizeOffset(offset) {
3582
- if (offset === void 0) return "0px";
3583
- return typeof offset === "number" ? `${offset}px` : offset;
3584
- }
3585
- function isOwnElement(el, root) {
3586
- if (el === document.body || el === document.documentElement) return true;
3587
- if (root.contains(el)) return true;
3588
- return el.closest(SDK_OVERLAY_SELECTOR) !== null;
3589
- }
3590
- function visualViewportHeight() {
3591
- return window.visualViewport?.height ?? window.innerHeight;
3592
- }
3593
- function clamp2(value, min, max) {
3594
- return Math.min(Math.max(value, min), max);
3595
- }
3596
- function isRestoredNavigation() {
3597
- const entries = window.performance?.getEntriesByType?.("navigation");
3598
- const entry = entries?.[0];
3599
- if (!entry) return true;
3600
- const navigation = entry;
3601
- return navigation.type === "reload" || navigation.type === "back_forward";
3602
- }
3603
- function pageOwnsScrolling(el) {
3604
- if (window.innerHeight > SHORT_VIEWPORT_PX) return false;
3605
- return el.scrollHeight - el.clientHeight <= 1;
3606
- }
3607
- function isInternalPath(url) {
3608
- return url.startsWith("/") && !url.startsWith("//");
3609
- }
3610
- function hostnameOf(url) {
3611
- try {
3612
- return new URL(url).hostname.replace(/^www\./, "");
3613
- } catch {
3614
- return "";
3615
- }
3616
- }
3617
- function initialOf(value, fallback) {
3618
- const source = (value || fallback).trim();
3619
- return source ? source.slice(0, 1).toUpperCase() : "?";
3620
- }
3621
- function logoUrlOf(url) {
3622
- try {
3623
- const { origin, protocol } = new URL(url);
3624
- if (protocol !== "https:" && protocol !== "http:") return null;
3625
- return `${origin}/favicon.svg`;
3626
- } catch {
3627
- return null;
3628
- }
3629
- }
3630
- function inlineImageOrigins(products) {
3631
- const origins = /* @__PURE__ */ new Set();
3632
- for (const product of products) {
3633
- let url;
3634
- try {
3635
- url = new URL(product.website);
3636
- } catch {
3637
- continue;
3638
- }
3639
- if (url.protocol !== "https:") continue;
3640
- const apex = url.hostname.replace(/^www\./, "");
3641
- const port = url.port ? `:${url.port}` : "";
3642
- origins.add(`https://${apex}${port}`);
3643
- origins.add(`https://www.${apex}${port}`);
3644
- }
3645
- return origins;
3646
- }
3647
- function isInlineImageAllowed(src, origins) {
3648
- if (typeof src !== "string" || src === "") return false;
3649
- let url;
3650
- try {
3651
- url = new URL(src);
3652
- } catch {
3653
- return false;
3654
- }
3655
- if (url.protocol !== "https:" || url.username || url.password) return false;
3656
- return origins.has(url.origin);
3657
- }
3658
- var REFERENCE_KIND_ORDER = [
3659
- "CONCEPT",
3660
- "DOCS",
3661
- "PRICING",
3662
- "USE_CASE",
3663
- "PAGE"
3664
- ];
3665
- var PRODUCT_SUBDOMAINS = [
3666
- "alphadocs.",
3667
- "alphaapp.",
3668
- "docs.",
3669
- "alpha.",
3670
- "app.",
3671
- "www."
3672
- ];
3673
- function readCatalogUrl(raw) {
3674
- let parsed;
3675
- try {
3676
- parsed = new URL(raw);
3677
- } catch {
3678
- return null;
3679
- }
3680
- if (parsed.protocol !== "https:") return null;
3681
- const host = parsed.hostname.toLowerCase();
3682
- const prefix = PRODUCT_SUBDOMAINS.find(
3683
- (candidate) => host.startsWith(candidate)
3684
- );
3685
- const apex = prefix === void 0 ? host : host.slice(prefix.length);
3686
- const port = parsed.port ? `:${parsed.port}` : "";
3687
- return {
3688
- origin: `https://${apex}${port}`,
3689
- prefix,
3690
- pathname: parsed.pathname
3691
- };
3692
- }
3693
- function referenceKindOf(url, origins) {
3694
- const parsed = readCatalogUrl(url);
3695
- if (parsed === null || !origins.has(parsed.origin)) return null;
3696
- if (parsed.prefix === "docs." || parsed.prefix === "alphadocs.")
3697
- return "DOCS";
3698
- const segment = parsed.pathname.toLowerCase().split("/").filter(Boolean)[0] ?? "";
3699
- if (segment === "concept" || segment === "concepts") return "CONCEPT";
3700
- if (segment === "docs") return "DOCS";
3701
- if (segment === "pricing" || segment === "plans") return "PRICING";
3702
- if (segment === "use-case" || segment === "use-cases" || segment === "solutions") {
3703
- return "USE_CASE";
3704
- }
3705
- return "PAGE";
3706
- }
3707
- function productNamesByOrigin(products) {
3708
- const names = /* @__PURE__ */ new Map();
3709
- for (const product of products) {
3710
- let url;
3711
- try {
3712
- url = new URL(product.website);
3713
- } catch {
3714
- continue;
3715
- }
3716
- if (url.protocol !== "https:") continue;
3717
- const apex = url.hostname.replace(/^www\./, "");
3718
- const port = url.port ? `:${url.port}` : "";
3719
- const key = `https://${apex}${port}`;
3720
- if (!names.has(key)) names.set(key, product.name);
3721
- }
3722
- return names;
3723
- }
3724
- function summariseReferences(references, origins, productNames) {
3725
- const counts = /* @__PURE__ */ new Map();
3726
- const products = [];
3727
- for (const reference of references) {
3728
- const kind = referenceKindOf(reference.url, origins);
3729
- if (kind !== null) counts.set(kind, (counts.get(kind) ?? 0) + 1);
3730
- const parsed = readCatalogUrl(reference.url);
3731
- const name = (parsed === null ? void 0 : productNames.get(parsed.origin)) ?? (reference.product || null);
3732
- if (name != null && !products.includes(name)) products.push(name);
3733
- }
3734
- return {
3735
- total: references.length,
3736
- kinds: REFERENCE_KIND_ORDER.filter((kind) => counts.has(kind)).map(
3737
- (kind) => ({ kind, count: counts.get(kind) ?? 0 })
3738
- ),
3739
- products
3740
- };
4031
+ height: calc(100vh - var(--boff-explore-offset, 0px) - var(--boff-explore-inset, 0px));
3741
4032
  }
3742
- var CODE_FENCE = /^\s*(?:```|~~~)/;
3743
- var TABLE_ROW_LINE = /^\s{0,3}\|/;
3744
- var TABLE_DELIMITER_ROW = /^\s{0,3}\|[\s:|-]*-[\s:|-]*$/;
3745
- function readableLine(raw, mode) {
3746
- let line = raw;
3747
- line = line.replace(/!\[[^\]]*\]\([^)\s]*(?:\s+"[^"]*")?\)/g, "");
3748
- line = mode === "speech" ? (
3749
- // A voice reading "h-t-t-p-s colon slash slash…" mid-sentence is noise, and the
3750
- // prompt asks for two to five links in every answer. The label IS the words.
3751
- line.replace(/\[([^\]]+)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g, "$1")
3752
- ) : line.replace(/\[([^\]]+)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g, "$1 ($2)");
3753
- line = mode === "speech" ? line.replace(/<(https?:\/\/[^>\s]+)>/g, "") : line.replace(/<(https?:\/\/[^>\s]+)>/g, "$1");
3754
- line = line.replace(/^\s{0,3}#{1,6}\s+/, "");
3755
- line = line.replace(/^\s{0,3}>\s?/, "");
3756
- if (/^\s{0,3}([-*_])[ \t]*(?:\1[ \t]*){2,}$/.test(line)) return "";
3757
- line = line.replace(/`([^`]+)`/g, "$1");
3758
- line = line.replace(/\*\*([^*]+)\*\*/g, "$1");
3759
- line = line.replace(/__([^_]+)__/g, "$1");
3760
- line = line.replace(/(^|[^*\w])\*([^*\n]+)\*(?![*\w])/g, "$1$2");
3761
- line = line.replace(/(^|[^_\w])_([^_\n]+)_(?![_\w])/g, "$1$2");
3762
- return line.replace(/[ \t]+$/, "");
4033
+ @supports (height: 100dvh) {
4034
+ .boff-explore-viewport {
4035
+ height: calc(100dvh - var(--boff-explore-offset, 0px) - var(--boff-explore-inset, 0px));
4036
+ }
3763
4037
  }
3764
- function tableCells(row) {
3765
- const trimmed = row.trim();
3766
- const cells = [];
3767
- let cell = "";
3768
- for (let at = 0; at < trimmed.length; at += 1) {
3769
- const char = trimmed[at] ?? "";
3770
- if (char === "\\") {
3771
- cell += char + (trimmed[at + 1] ?? "");
3772
- at += 1;
3773
- continue;
3774
- }
3775
- if (char === "|") {
3776
- cells.push(cell.trim());
3777
- cell = "";
3778
- continue;
4038
+
4039
+ /* Landscape phones and other short viewports: a one-screen pane leaves a ~124px transcript
4040
+ sliver under ~610px of chrome and content. At or below the threshold the pane stops
4041
+ pretending to be a window and lets the page's own scrolling carry the transcript. The
4042
+ min-height is the available height rather than a fixed rem value, so the fallback can
4043
+ never be taller than the screen it is rescuing. */
4044
+ @media (max-height: ${SHORT_VIEWPORT_PX}px) {
4045
+ .boff-explore-viewport {
4046
+ height: auto;
4047
+ min-height: calc(100vh - var(--boff-explore-offset, 0px));
4048
+ }
4049
+ }
4050
+ @supports (height: 100dvh) {
4051
+ @media (max-height: ${SHORT_VIEWPORT_PX}px) {
4052
+ .boff-explore-viewport {
4053
+ min-height: calc(100dvh - var(--boff-explore-offset, 0px));
3779
4054
  }
3780
- cell += char;
3781
4055
  }
3782
- cells.push(cell.trim());
3783
- if (trimmed.startsWith("|") && cells[0] === "") cells.shift();
3784
- if (trimmed.endsWith("|") && cells[cells.length - 1] === "") cells.pop();
3785
- return cells;
3786
4056
  }
3787
- function asSentence(text) {
3788
- return /[.!?]$/.test(text) ? text : `${text}.`;
4057
+
4058
+ /* ---- Composer dock --------------------------------------------------------------------
4059
+ Three declarations, each the fallback for the one after it: a plain 1rem where \`max()\`
4060
+ is unknown, then the measured overlap of any FOREIGN fixed chrome sitting over the
4061
+ composer (the host sites' cookie banner is 229px tall and lands exactly there), then the
4062
+ same plus the home-indicator inset. \`--boff-explore-chrome\` is set on the root only once
4063
+ an overlap has actually been measured, so everywhere else this collapses to 1rem. */
4064
+ .boff-explore-composer {
4065
+ padding-bottom: 1rem;
4066
+ padding-bottom: max(1rem, var(--boff-explore-chrome, 0px));
4067
+ padding-bottom: max(1rem, env(safe-area-inset-bottom), var(--boff-explore-chrome, 0px));
3789
4068
  }
3790
- function spokenTable(rows) {
3791
- if (rows.length < 2 || !TABLE_DELIMITER_ROW.test(rows[1] ?? "")) return rows;
3792
- const labels = tableCells(rows[0] ?? "");
3793
- const spoken = [];
3794
- for (const row of rows.slice(2)) {
3795
- const parts = [];
3796
- tableCells(row).forEach((value, column) => {
3797
- if (value === "") return;
3798
- const label = column === 0 ? "" : labels[column] ?? "";
3799
- parts.push(label === "" ? value : `${label}: ${value}`);
3800
- });
3801
- if (parts.length > 0) spoken.push(asSentence(parts.join(". ")));
3802
- }
3803
- return spoken;
4069
+ `;
4070
+ function ExploreStyles() {
4071
+ return /* @__PURE__ */ jsxRuntime.jsx("style", { dangerouslySetInnerHTML: { __html: EXPLORE_CSS } });
3804
4072
  }
3805
- function readableText(markdown, mode) {
3806
- const lines = [];
3807
- let inFence = false;
3808
- let table = [];
3809
- const flushTable = () => {
3810
- if (table.length === 0) return;
3811
- for (const line of spokenTable(table)) lines.push(readableLine(line, mode));
3812
- table = [];
3813
- };
3814
- for (const raw of markdown.split("\n")) {
3815
- if (CODE_FENCE.test(raw)) {
3816
- flushTable();
3817
- inFence = !inFence;
3818
- continue;
3819
- }
3820
- if (inFence) {
3821
- lines.push(raw);
3822
- continue;
3823
- }
3824
- if (mode === "speech" && TABLE_ROW_LINE.test(raw)) {
3825
- table.push(raw);
3826
- continue;
3827
- }
3828
- flushTable();
3829
- lines.push(readableLine(raw, mode));
4073
+ var CAPABILITIES = [
4074
+ {
4075
+ icon: lucideReact.BookOpenText,
4076
+ title: "Grounded in our own docs",
4077
+ body: "Every answer comes from published product pages and documentation \u2014 never invented."
4078
+ },
4079
+ {
4080
+ icon: lucideReact.Layers,
4081
+ title: "Compare across the ecosystem",
4082
+ body: "Ask which product fits your team, or how two of them work together."
4083
+ },
4084
+ {
4085
+ icon: lucideReact.Link2,
4086
+ title: "Straight to the right page",
4087
+ body: "Get the sources and next steps \u2014 pricing, docs, a demo \u2014 without hunting."
3830
4088
  }
3831
- flushTable();
3832
- return lines.join("\n").replace(/\n{3,}/g, "\n\n").trim();
4089
+ ];
4090
+ var MIN_VISIBLE_FRACTION = 0.25;
4091
+ var SDK_OVERLAY_SELECTOR = "[data-boff-explore]";
4092
+ var CTA_VARIANTS = {
4093
+ CONTACT: "default",
4094
+ WAITLIST: "default",
4095
+ APP: "default",
4096
+ PRICING: "secondary",
4097
+ STORE: "secondary",
4098
+ PARTNERS: "secondary",
4099
+ DOCS: "outline",
4100
+ SERVICES: "outline",
4101
+ OTHER: "outline"
4102
+ };
4103
+ function normalizeOffset(offset) {
4104
+ if (offset === void 0) return "0px";
4105
+ return typeof offset === "number" ? `${offset}px` : offset;
3833
4106
  }
3834
- function answerPlainText(markdown) {
3835
- return readableText(markdown, "clipboard");
4107
+ function isOwnElement(el, root) {
4108
+ if (el === document.body || el === document.documentElement) return true;
4109
+ if (root.contains(el)) return true;
4110
+ return el.closest(SDK_OVERLAY_SELECTOR) !== null;
3836
4111
  }
3837
- function answerSpeechText(markdown) {
3838
- return readableText(markdown, "speech");
4112
+ function visualViewportHeight() {
4113
+ return window.visualViewport?.height ?? window.innerHeight;
3839
4114
  }
3840
- var AnswerScopeContext = React.createContext(null);
3841
- var InsideLinkContext = React.createContext(false);
3842
- function AnswerLink(props) {
3843
- return /* @__PURE__ */ jsxRuntime.jsx(InsideLinkContext.Provider, { value: true, children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownLink, { ...props }) });
4115
+ function clamp2(value, min, max) {
4116
+ return Math.min(Math.max(value, min), max);
3844
4117
  }
3845
- var INLINE_IMAGE_FRAME = "relative inline-block max-w-full overflow-hidden rounded-xl border border-border bg-muted align-top shadow-sm";
3846
- function InlineConceptImage({ src, alt }) {
3847
- const scope = React.useContext(AnswerScopeContext);
3848
- const insideLink = React.useContext(InsideLinkContext);
3849
- if (!scope || !scope.enabled || !isInlineImageAllowed(src, scope.origins) || scope.failed.has(src)) {
3850
- return null;
3851
- }
3852
- const { strings: T, onFailed } = scope;
3853
- const picture = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3854
- /* @__PURE__ */ jsxRuntime.jsx(
3855
- "img",
3856
- {
3857
- src,
3858
- alt: alt ?? "",
3859
- loading: "lazy",
3860
- decoding: "async",
3861
- onError: () => onFailed(src),
3862
- className: "block h-auto max-h-80 w-auto max-w-full object-contain"
3863
- }
3864
- ),
3865
- /* @__PURE__ */ jsxRuntime.jsx(
3866
- "span",
3867
- {
3868
- "aria-hidden": "true",
3869
- 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",
3870
- children: T.conceptIllustration
3871
- }
3872
- )
3873
- ] });
3874
- 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(
3875
- "a",
3876
- {
3877
- href: src,
3878
- target: "_blank",
3879
- rel: "noopener noreferrer",
3880
- className: cn(
3881
- INLINE_IMAGE_FRAME,
3882
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
3883
- ),
3884
- children: [
3885
- picture,
3886
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "sr-only", children: [
3887
- "(",
3888
- T.opensInNewTab,
3889
- ")"
3890
- ] })
3891
- ]
3892
- }
3893
- ) });
4118
+ function isRestoredNavigation() {
4119
+ const entries = window.performance?.getEntriesByType?.("navigation");
4120
+ const entry = entries?.[0];
4121
+ if (!entry) return true;
4122
+ const navigation = entry;
4123
+ return navigation.type === "reload" || navigation.type === "back_forward";
3894
4124
  }
3895
- function markdownText(node) {
3896
- if (!node) return "";
3897
- if (typeof node.value === "string") return node.value;
3898
- return (node.children ?? []).map(markdownText).join("");
4125
+ function pageOwnsScrolling(el) {
4126
+ if (window.innerHeight > SHORT_VIEWPORT_PX) return false;
4127
+ return el.scrollHeight - el.clientHeight <= 1;
3899
4128
  }
3900
- function hasHeaderCells(node) {
3901
- return (node?.children ?? []).some((cell) => cell.tagName === "th");
4129
+ function isInternalPath(url) {
4130
+ return url.startsWith("/") && !url.startsWith("//");
3902
4131
  }
3903
- function findHeaderRow(node) {
3904
- for (const child of node?.children ?? []) {
3905
- if (child.tagName === "tr" && hasHeaderCells(child)) return child;
3906
- const nested = findHeaderRow(child);
3907
- if (nested !== null) return nested;
4132
+ function hostnameOf(url) {
4133
+ try {
4134
+ return new URL(url).hostname.replace(/^www\./, "");
4135
+ } catch {
4136
+ return "";
4137
+ }
4138
+ }
4139
+ function initialOf(value, fallback) {
4140
+ const source = (value || fallback).trim();
4141
+ return source ? source.slice(0, 1).toUpperCase() : "?";
4142
+ }
4143
+ function logoUrlOf(url) {
4144
+ try {
4145
+ const { origin, protocol } = new URL(url);
4146
+ if (protocol !== "https:" && protocol !== "http:") return null;
4147
+ return `${origin}/favicon.svg`;
4148
+ } catch {
4149
+ return null;
3908
4150
  }
3909
- return null;
3910
4151
  }
3911
- function tableColumnLabels(node) {
3912
- const row = findHeaderRow(node);
3913
- if (row === null) return [];
3914
- return (row.children ?? []).filter((cell) => cell.tagName === "th").map((cell) => markdownText(cell).trim());
4152
+ var REFERENCE_KIND_ORDER = [
4153
+ "CONCEPT",
4154
+ "DOCS",
4155
+ "PRICING",
4156
+ "USE_CASE",
4157
+ "PAGE"
4158
+ ];
4159
+ var PRODUCT_SUBDOMAINS = [
4160
+ "alphadocs.",
4161
+ "alphaapp.",
4162
+ "docs.",
4163
+ "alpha.",
4164
+ "app.",
4165
+ "www."
4166
+ ];
4167
+ function readCatalogUrl(raw) {
4168
+ let parsed;
4169
+ try {
4170
+ parsed = new URL(raw);
4171
+ } catch {
4172
+ return null;
4173
+ }
4174
+ if (parsed.protocol !== "https:") return null;
4175
+ const host = parsed.hostname.toLowerCase();
4176
+ const prefix = PRODUCT_SUBDOMAINS.find(
4177
+ (candidate) => host.startsWith(candidate)
4178
+ );
4179
+ const apex = prefix === void 0 ? host : host.slice(prefix.length);
4180
+ const port = parsed.port ? `:${parsed.port}` : "";
4181
+ return {
4182
+ origin: `https://${apex}${port}`,
4183
+ prefix,
4184
+ pathname: parsed.pathname
4185
+ };
3915
4186
  }
3916
- var TableColumnsContext = React.createContext([]);
3917
- function AnswerTable({ node, children }) {
3918
- const scope = React.useContext(AnswerScopeContext);
3919
- const table = /* @__PURE__ */ jsxRuntime.jsx(TableColumnsContext.Provider, { value: tableColumnLabels(node), children: /* @__PURE__ */ jsxRuntime.jsx("table", { children }) });
3920
- if (!scope) return table;
3921
- return /* @__PURE__ */ jsxRuntime.jsx(
3922
- "div",
3923
- {
3924
- "data-boff-explore": "table",
3925
- role: "region",
3926
- "aria-label": scope.strings.answerTable,
3927
- tabIndex: 0,
3928
- className: "boff-explore-table",
3929
- children: table
4187
+ function referenceKindOf(url, origins) {
4188
+ const parsed = readCatalogUrl(url);
4189
+ if (parsed === null || !origins.has(parsed.origin)) return null;
4190
+ if (parsed.prefix === "docs." || parsed.prefix === "alphadocs.")
4191
+ return "DOCS";
4192
+ const segment = parsed.pathname.toLowerCase().split("/").filter(Boolean)[0] ?? "";
4193
+ if (segment === "concept" || segment === "concepts") return "CONCEPT";
4194
+ if (segment === "docs") return "DOCS";
4195
+ if (segment === "pricing" || segment === "plans") return "PRICING";
4196
+ if (segment === "use-case" || segment === "use-cases" || segment === "solutions") {
4197
+ return "USE_CASE";
4198
+ }
4199
+ return "PAGE";
4200
+ }
4201
+ function productNamesByOrigin(products) {
4202
+ const names = /* @__PURE__ */ new Map();
4203
+ for (const product of products) {
4204
+ let url;
4205
+ try {
4206
+ url = new URL(product.website);
4207
+ } catch {
4208
+ continue;
3930
4209
  }
3931
- );
4210
+ if (url.protocol !== "https:") continue;
4211
+ const apex = url.hostname.replace(/^www\./, "");
4212
+ const port = url.port ? `:${url.port}` : "";
4213
+ const key = `https://${apex}${port}`;
4214
+ if (!names.has(key)) names.set(key, product.name);
4215
+ }
4216
+ return names;
3932
4217
  }
3933
- function AnswerTableRow({
3934
- node,
3935
- children,
3936
- ...props
3937
- }) {
3938
- const labels = React.useContext(TableColumnsContext);
3939
- if (labels.length === 0 || hasHeaderCells(node)) {
3940
- return /* @__PURE__ */ jsxRuntime.jsx("tr", { ...props, children });
4218
+ function summariseReferences(references, origins, productNames) {
4219
+ const counts = /* @__PURE__ */ new Map();
4220
+ const products = [];
4221
+ for (const reference of references) {
4222
+ const kind = referenceKindOf(reference.url, origins);
4223
+ if (kind !== null) counts.set(kind, (counts.get(kind) ?? 0) + 1);
4224
+ const parsed = readCatalogUrl(reference.url);
4225
+ const name = (parsed === null ? void 0 : productNames.get(parsed.origin)) ?? (reference.product || null);
4226
+ if (name != null && !products.includes(name)) products.push(name);
3941
4227
  }
3942
- let column = 0;
3943
- const cells = React.Children.map(children, (cell) => {
3944
- if (!React.isValidElement(cell)) return cell;
3945
- const label = labels[column] ?? "";
3946
- column += 1;
3947
- if (label === "") return cell;
3948
- return React.cloneElement(cell, {
3949
- "data-label": label
3950
- });
3951
- });
3952
- return /* @__PURE__ */ jsxRuntime.jsx("tr", { ...props, children: cells });
4228
+ return {
4229
+ total: references.length,
4230
+ kinds: REFERENCE_KIND_ORDER.filter((kind) => counts.has(kind)).map(
4231
+ (kind) => ({ kind, count: counts.get(kind) ?? 0 })
4232
+ ),
4233
+ products
4234
+ };
3953
4235
  }
3954
- var ANSWER_MARKDOWN_COMPONENTS = {
3955
- a: AnswerLink,
3956
- img: InlineConceptImage,
3957
- table: AnswerTable,
3958
- tr: AnswerTableRow
3959
- };
3960
4236
  function ReferenceCard({
3961
4237
  reference,
3962
4238
  onFollow
@@ -4295,6 +4571,195 @@ function AnswerFeedback({
4295
4571
  }
4296
4572
  );
4297
4573
  }
4574
+ function EscalateForm({
4575
+ strings: T,
4576
+ formId,
4577
+ onEscalate,
4578
+ onClose,
4579
+ onBusyChange
4580
+ }) {
4581
+ const [email, setEmail] = React.useState("");
4582
+ const [note, setNote] = React.useState("");
4583
+ const [error, setError] = React.useState(null);
4584
+ const [sending, setSending] = React.useState(false);
4585
+ const [sentTo, setSentTo] = React.useState(null);
4586
+ const emailRef = React.useRef(null);
4587
+ const inFlightRef = React.useRef(false);
4588
+ const titleId = `${formId}-title`;
4589
+ const emailId = `${formId}-email`;
4590
+ const noteId = `${formId}-note`;
4591
+ const errorId = `${formId}-error`;
4592
+ React.useEffect(() => {
4593
+ emailRef.current?.focus();
4594
+ }, []);
4595
+ React.useEffect(() => {
4596
+ onBusyChange?.(sending);
4597
+ return () => onBusyChange?.(false);
4598
+ }, [sending, onBusyChange]);
4599
+ const handleSubmit = React.useCallback(
4600
+ async (event) => {
4601
+ event.preventDefault();
4602
+ if (inFlightRef.current) return;
4603
+ const address = email.trim();
4604
+ if (!looksLikeDeliverableEmail(address)) {
4605
+ setError(T.escalateInvalidEmail);
4606
+ emailRef.current?.focus();
4607
+ return;
4608
+ }
4609
+ inFlightRef.current = true;
4610
+ setSending(true);
4611
+ setError(null);
4612
+ const result = await onEscalate(address, note);
4613
+ inFlightRef.current = false;
4614
+ setSending(false);
4615
+ switch (result.outcome) {
4616
+ case "sent":
4617
+ case "already-handed-over":
4618
+ setSentTo(result.email ?? address);
4619
+ return;
4620
+ case "invalid-email":
4621
+ setError(T.escalateInvalidEmail);
4622
+ emailRef.current?.focus();
4623
+ return;
4624
+ case "rate-limited":
4625
+ setError(T.escalateRateLimited);
4626
+ return;
4627
+ case "captcha":
4628
+ setError(T.escalateCaptcha);
4629
+ return;
4630
+ // `no-conversation` included: the form should not have been offered at all, and the
4631
+ // honest thing to show is that nothing was passed on.
4632
+ default:
4633
+ setError(T.escalateFailed);
4634
+ return;
4635
+ }
4636
+ },
4637
+ [T, email, note, onEscalate]
4638
+ );
4639
+ if (sentTo !== null) {
4640
+ return /* @__PURE__ */ jsxRuntime.jsx(
4641
+ "p",
4642
+ {
4643
+ id: formId,
4644
+ "data-boff-explore": "escalate-sent",
4645
+ role: "status",
4646
+ className: "boff-explore-fade mt-2 rounded-xl border border-border bg-card px-3 py-2 text-xs leading-relaxed text-card-foreground",
4647
+ children: fill(T.escalateSent, { email: sentTo })
4648
+ }
4649
+ );
4650
+ }
4651
+ return /* @__PURE__ */ jsxRuntime.jsxs(
4652
+ "form",
4653
+ {
4654
+ id: formId,
4655
+ "data-boff-explore": "escalate-form",
4656
+ "aria-labelledby": titleId,
4657
+ noValidate: true,
4658
+ onSubmit: handleSubmit,
4659
+ onKeyDown: (event) => {
4660
+ if (event.key !== "Escape") return;
4661
+ event.stopPropagation();
4662
+ if (sending) return;
4663
+ onClose();
4664
+ },
4665
+ className: "boff-explore-fade mt-2 space-y-2 rounded-xl border border-border bg-card p-3",
4666
+ children: [
4667
+ /* @__PURE__ */ jsxRuntime.jsx("p", { id: titleId, className: "text-xs font-medium text-card-foreground", children: T.escalateTitle }),
4668
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs leading-relaxed text-muted-foreground", children: T.escalateBody }),
4669
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
4670
+ /* @__PURE__ */ jsxRuntime.jsx(
4671
+ "label",
4672
+ {
4673
+ htmlFor: emailId,
4674
+ className: "block text-xs font-medium text-card-foreground",
4675
+ children: T.escalateEmailLabel
4676
+ }
4677
+ ),
4678
+ /* @__PURE__ */ jsxRuntime.jsx(
4679
+ "input",
4680
+ {
4681
+ ref: emailRef,
4682
+ id: emailId,
4683
+ name: "email",
4684
+ "data-boff-explore": "escalate-email",
4685
+ type: "email",
4686
+ inputMode: "email",
4687
+ autoComplete: "email",
4688
+ required: true,
4689
+ value: email,
4690
+ onChange: (event) => {
4691
+ setEmail(event.target.value);
4692
+ if (error) setError(null);
4693
+ },
4694
+ "aria-invalid": error ? "true" : void 0,
4695
+ "aria-describedby": error ? errorId : void 0,
4696
+ placeholder: T.escalateEmailPlaceholder,
4697
+ className: "w-full rounded-md border border-border bg-background px-2 py-1 text-base text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring sm:text-sm"
4698
+ }
4699
+ )
4700
+ ] }),
4701
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
4702
+ /* @__PURE__ */ jsxRuntime.jsx(
4703
+ "label",
4704
+ {
4705
+ htmlFor: noteId,
4706
+ className: "block text-xs font-medium text-card-foreground",
4707
+ children: T.escalateNoteLabel
4708
+ }
4709
+ ),
4710
+ /* @__PURE__ */ jsxRuntime.jsx(
4711
+ Textarea,
4712
+ {
4713
+ id: noteId,
4714
+ name: "note",
4715
+ "data-boff-explore": "escalate-note",
4716
+ rows: 2,
4717
+ value: note,
4718
+ onChange: (event) => setNote(event.target.value),
4719
+ maxLength: EXPLORE_ESCALATION_NOTE_SOFT_MAX,
4720
+ className: "min-h-0 border-border bg-background"
4721
+ }
4722
+ )
4723
+ ] }),
4724
+ error ? /* @__PURE__ */ jsxRuntime.jsx(
4725
+ "p",
4726
+ {
4727
+ id: errorId,
4728
+ "data-boff-explore": "escalate-error",
4729
+ role: "alert",
4730
+ className: "text-xs leading-relaxed text-destructive",
4731
+ children: error
4732
+ }
4733
+ ) : null,
4734
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
4735
+ /* @__PURE__ */ jsxRuntime.jsx(
4736
+ Button,
4737
+ {
4738
+ "data-boff-explore": "escalate-send",
4739
+ type: "submit",
4740
+ size: "sm",
4741
+ disabled: sending,
4742
+ children: sending ? T.escalateSending : T.escalateSubmit
4743
+ }
4744
+ ),
4745
+ /* @__PURE__ */ jsxRuntime.jsx(
4746
+ Button,
4747
+ {
4748
+ "data-boff-explore": "escalate-cancel",
4749
+ type: "button",
4750
+ size: "sm",
4751
+ variant: "ghost",
4752
+ className: "text-muted-foreground",
4753
+ disabled: sending,
4754
+ onClick: onClose,
4755
+ children: T.escalateCancel
4756
+ }
4757
+ )
4758
+ ] })
4759
+ ]
4760
+ }
4761
+ );
4762
+ }
4298
4763
  var PROVENANCE_CHIP = "inline-flex max-w-full items-center gap-1 rounded-full border border-border bg-muted/60 px-2 py-0.5 text-[11px] leading-5 text-muted-foreground";
4299
4764
  function ProvenanceChips({
4300
4765
  strings: T,
@@ -4488,31 +4953,15 @@ function AssistantBubble({
4488
4953
  anchorRef,
4489
4954
  onFollow,
4490
4955
  onFollowUp,
4491
- onFeedback
4956
+ onFeedback,
4957
+ onEscalate,
4958
+ isLatestAnswer = false
4492
4959
  }) {
4493
4960
  const streaming = message.status === "PENDING" || message.status === "RUNNING";
4494
4961
  const body = message.content || message.partialContent || "";
4495
4962
  const failed = message.status === "FAILED";
4496
4963
  const completed = message.status === "COMPLETED";
4497
4964
  const visible = useProgressiveReveal(body, !streaming);
4498
- const [failedImages, setFailedImages] = React.useState(
4499
- () => /* @__PURE__ */ new Set()
4500
- );
4501
- const markImageFailed = React.useCallback((src) => {
4502
- setFailedImages(
4503
- (current) => current.has(src) ? current : new Set(current).add(src)
4504
- );
4505
- }, []);
4506
- const answerScope = React.useMemo(
4507
- () => ({
4508
- strings: T,
4509
- enabled: completed,
4510
- origins: imageOrigins,
4511
- failed: failedImages,
4512
- onFailed: markImageFailed
4513
- }),
4514
- [T, completed, imageOrigins, failedImages, markImageFailed]
4515
- );
4516
4965
  const provenance = React.useMemo(
4517
4966
  () => summariseReferences(message.references, imageOrigins, productNames),
4518
4967
  [message.references, imageOrigins, productNames]
@@ -4526,6 +4975,20 @@ function AssistantBubble({
4526
4975
  [message.content]
4527
4976
  );
4528
4977
  const grounded = completed && message.answered !== false;
4978
+ const [escalateOpen, setEscalateOpen] = React.useState(false);
4979
+ const [escalateBusy, setEscalateBusy] = React.useState(false);
4980
+ const escalateTriggerRef = React.useRef(null);
4981
+ const escalateFormId = `boff-explore-escalate-${message.id}`;
4982
+ const unanswered = completed && message.answered === false;
4983
+ const offerEscalate = Boolean(onEscalate) && completed;
4984
+ const showQuietEscalate = offerEscalate && !unanswered && isLatestAnswer;
4985
+ const closeEscalate = React.useCallback(() => {
4986
+ setEscalateOpen(false);
4987
+ escalateTriggerRef.current?.focus();
4988
+ }, []);
4989
+ const handleEscalateBusy = React.useCallback((busy) => {
4990
+ setEscalateBusy(busy);
4991
+ }, []);
4529
4992
  return /* @__PURE__ */ jsxRuntime.jsxs(
4530
4993
  "div",
4531
4994
  {
@@ -4543,7 +5006,7 @@ function AssistantBubble({
4543
5006
  }
4544
5007
  ),
4545
5008
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1 space-y-3", children: [
4546
- message.status === "COMPLETED" && message.answered === false ? /* @__PURE__ */ jsxRuntime.jsxs(
5009
+ unanswered ? /* @__PURE__ */ jsxRuntime.jsxs(
4547
5010
  "div",
4548
5011
  {
4549
5012
  "data-boff-explore": "no-answer",
@@ -4553,18 +5016,39 @@ function AssistantBubble({
4553
5016
  /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
4554
5017
  T.noAnswer,
4555
5018
  " ",
4556
- /* @__PURE__ */ jsxRuntime.jsx(
4557
- "a",
4558
- {
4559
- href: "/contact",
4560
- className: "underline underline-offset-2 hover:text-primary",
4561
- onClick: (event) => {
4562
- if (!onNavigate) return;
4563
- event.preventDefault();
4564
- onNavigate("/contact");
4565
- },
4566
- children: T.askHuman
4567
- }
5019
+ offerEscalate ? (
5020
+ /* The point of the wave: the same words, but they now open a form that
5021
+ sends the conversation we are both looking at, instead of a link to a
5022
+ blank contact page that asks the visitor to type it all again. */
5023
+ /* @__PURE__ */ jsxRuntime.jsx(
5024
+ "button",
5025
+ {
5026
+ ref: escalateTriggerRef,
5027
+ "data-boff-explore": "escalate-open",
5028
+ type: "button",
5029
+ "aria-expanded": escalateOpen,
5030
+ "aria-controls": escalateOpen ? escalateFormId : void 0,
5031
+ onClick: () => setEscalateOpen(true),
5032
+ className: "rounded-sm underline underline-offset-2 hover:text-primary focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
5033
+ children: T.askHuman
5034
+ }
5035
+ )
5036
+ ) : (
5037
+ /* No conversation to hand over (a shared, read-only view). The old dead end
5038
+ is still better than a control whose submit could only fail. */
5039
+ /* @__PURE__ */ jsxRuntime.jsx(
5040
+ "a",
5041
+ {
5042
+ href: "/contact",
5043
+ className: "underline underline-offset-2 hover:text-primary",
5044
+ onClick: (event) => {
5045
+ if (!onNavigate) return;
5046
+ event.preventDefault();
5047
+ onNavigate("/contact");
5048
+ },
5049
+ children: T.askHuman
5050
+ }
5051
+ )
4568
5052
  ),
4569
5053
  "."
4570
5054
  ] })
@@ -4572,7 +5056,16 @@ function AssistantBubble({
4572
5056
  }
4573
5057
  ) : null,
4574
5058
  visible ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-2xl rounded-tl-md border border-border bg-card px-4 py-3 shadow-sm", children: [
4575
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "boff-prose boff-prose-sm boff-explore-body min-w-0 text-card-foreground", children: /* @__PURE__ */ jsxRuntime.jsx(AnswerScopeContext.Provider, { value: answerScope, children: /* @__PURE__ */ jsxRuntime.jsx(Markdown, { components: ANSWER_MARKDOWN_COMPONENTS, children: visible }) }) }),
5059
+ /* @__PURE__ */ jsxRuntime.jsx(
5060
+ ExploreAnswerBody,
5061
+ {
5062
+ strings: T,
5063
+ imageOrigins,
5064
+ enabled: completed,
5065
+ className: "boff-prose-sm text-card-foreground",
5066
+ children: visible
5067
+ }
5068
+ ),
4576
5069
  streaming ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "boff-explore-caret", "aria-hidden": "true" }) : null
4577
5070
  ] }) : null,
4578
5071
  grounded && provenance.total > 0 ? /* @__PURE__ */ jsxRuntime.jsx(ProvenanceChips, { strings: T, provenance }) : null,
@@ -4632,6 +5125,30 @@ function AssistantBubble({
4632
5125
  feedback: message.feedback,
4633
5126
  onSubmit: onFeedback
4634
5127
  }
5128
+ ) : null,
5129
+ showQuietEscalate ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pt-0.5", children: /* @__PURE__ */ jsxRuntime.jsx(
5130
+ "button",
5131
+ {
5132
+ ref: escalateTriggerRef,
5133
+ "data-boff-explore": "escalate-open",
5134
+ type: "button",
5135
+ "aria-expanded": escalateOpen,
5136
+ "aria-controls": escalateOpen ? escalateFormId : void 0,
5137
+ disabled: escalateBusy,
5138
+ onClick: () => setEscalateOpen((open) => !open),
5139
+ className: "rounded-sm text-xs text-muted-foreground underline underline-offset-2 hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
5140
+ children: T.escalateOpen
5141
+ }
5142
+ ) }) : null,
5143
+ escalateOpen && onEscalate ? /* @__PURE__ */ jsxRuntime.jsx(
5144
+ EscalateForm,
5145
+ {
5146
+ strings: T,
5147
+ formId: escalateFormId,
5148
+ onEscalate,
5149
+ onClose: closeEscalate,
5150
+ onBusyChange: handleEscalateBusy
5151
+ }
4635
5152
  ) : null
4636
5153
  ] })
4637
5154
  ]
@@ -4857,7 +5374,7 @@ function ConceptStrip({
4857
5374
  "div",
4858
5375
  {
4859
5376
  "data-boff-explore": "concept-strip-block",
4860
- className: "boff-explore-rise boff-explore-rise-1 mt-6",
5377
+ className: "boff-explore-rise boff-explore-rise-3 mt-6",
4861
5378
  children: [
4862
5379
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-2 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: T.conceptStripTitle }),
4863
5380
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -5047,15 +5564,6 @@ function WelcomeState({
5047
5564
  /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "mt-4 text-balance break-words text-2xl font-bold tracking-tight sm:text-4xl", children: title }),
5048
5565
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mx-auto mt-2.5 max-w-2xl text-pretty text-sm leading-relaxed text-muted-foreground sm:text-lg", children: body })
5049
5566
  ] }),
5050
- /* @__PURE__ */ jsxRuntime.jsx(
5051
- ConceptStrip,
5052
- {
5053
- strings: T,
5054
- highlights: conceptHighlights,
5055
- onAsk: onPrompt,
5056
- disabled
5057
- }
5058
- ),
5059
5567
  /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "boff-explore-rise boff-explore-rise-1 mt-6 grid grid-cols-3 gap-2 sm:mt-7 sm:gap-2.5", children: CAPABILITIES.map(({ icon: Icon, body: capBody }, i) => /* @__PURE__ */ jsxRuntime.jsxs(
5060
5568
  "li",
5061
5569
  {
@@ -5091,7 +5599,16 @@ function WelcomeState({
5091
5599
  onFocus
5092
5600
  }
5093
5601
  )
5094
- ] }) : null
5602
+ ] }) : null,
5603
+ /* @__PURE__ */ jsxRuntime.jsx(
5604
+ ConceptStrip,
5605
+ {
5606
+ strings: T,
5607
+ highlights: conceptHighlights,
5608
+ onAsk: onPrompt,
5609
+ disabled
5610
+ }
5611
+ )
5095
5612
  ] });
5096
5613
  }
5097
5614
  function ExplorePage({
@@ -5138,6 +5655,8 @@ function ExplorePage({
5138
5655
  clearHistory,
5139
5656
  recordClick,
5140
5657
  submitFeedback,
5658
+ escalate,
5659
+ canEscalate,
5141
5660
  createShareLink,
5142
5661
  isSharedView
5143
5662
  } = useExploreChat({
@@ -5581,6 +6100,7 @@ function ExplorePage({
5581
6100
  ),
5582
6101
  /* @__PURE__ */ jsxRuntime.jsx(ProseStyles, {}),
5583
6102
  /* @__PURE__ */ jsxRuntime.jsx(ExploreStyles, {}),
6103
+ /* @__PURE__ */ jsxRuntime.jsx(ExploreAnswerStyles, {}),
5584
6104
  !showWelcome ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2.5 border-b border-border px-4 py-2.5", children: [
5585
6105
  /* @__PURE__ */ jsxRuntime.jsx(
5586
6106
  "span",
@@ -5759,7 +6279,9 @@ function ExplorePage({
5759
6279
  anchorRef: message.id === latestAssistantId ? latestAssistantRef : void 0,
5760
6280
  onFollow: (kind, url) => recordClick(message.id, kind, url),
5761
6281
  onFollowUp: sendPrompt,
5762
- onFeedback: isSharedView ? void 0 : (rating, detail) => submitFeedback(message.id, rating, detail)
6282
+ onFeedback: isSharedView ? void 0 : (rating, detail) => submitFeedback(message.id, rating, detail),
6283
+ onEscalate: canEscalate ? escalate : void 0,
6284
+ isLatestAnswer: message.id === latestAssistantId
5763
6285
  },
5764
6286
  message.id
5765
6287
  )
@@ -5866,10 +6388,19 @@ function ExplorePage({
5866
6388
  /* @__PURE__ */ jsxRuntime.jsxs(
5867
6389
  "div",
5868
6390
  {
6391
+ "data-boff-explore": "composer-field",
5869
6392
  className: cn(
5870
6393
  "flex min-w-0 items-end gap-2 rounded-2xl border bg-card p-2 shadow-sm transition-all",
5871
6394
  busy && "boff-explore-busy",
5872
- overLimit ? "border-destructive/60" : "border-border focus-within:border-primary/40 focus-within:ring-1 focus-within:ring-ring"
6395
+ overLimit ? "border-destructive/60" : "border-border focus-within:border-primary/40 focus-within:ring-1 focus-within:ring-ring",
6396
+ // The halo is an invitation, and an invitation is only useful before it has
6397
+ // been accepted: while the conversation is empty, the composer is usable, and
6398
+ // nothing has been typed. Once any of those stops being true the class goes
6399
+ // and the box is an ordinary card again. `-quiet` keeps the ring but stops the
6400
+ // movement the moment there are characters in the field, so it does not pulse
6401
+ // at someone who is mid-sentence.
6402
+ showWelcome && !composerDisabled && !overLimit && "boff-explore-invite",
6403
+ showWelcome && draft.length > 0 && "boff-explore-invite-quiet"
5873
6404
  ),
5874
6405
  children: [
5875
6406
  /* @__PURE__ */ jsxRuntime.jsx(