@veluai/velu 0.2.25 → 0.2.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/README.md +80 -80
  2. package/dist/cli.js +44 -44
  3. package/package.json +64 -64
  4. package/runtime/velu-ui/base.css +320 -320
  5. package/runtime/velu-ui/components/Accordion.jsx +64 -64
  6. package/runtime/velu-ui/components/ApiClient.jsx +207 -207
  7. package/runtime/velu-ui/components/ApiField.jsx +87 -87
  8. package/runtime/velu-ui/components/ApiPath.jsx +63 -63
  9. package/runtime/velu-ui/components/ApiReferencePage.jsx +384 -384
  10. package/runtime/velu-ui/components/ApiSamples.jsx +36 -36
  11. package/runtime/velu-ui/components/ApiSidebar.jsx +125 -125
  12. package/runtime/velu-ui/components/AskBar.jsx +71 -71
  13. package/runtime/velu-ui/components/Callout.jsx +114 -114
  14. package/runtime/velu-ui/components/Card.jsx +131 -131
  15. package/runtime/velu-ui/components/ChangelogFilters.jsx +75 -75
  16. package/runtime/velu-ui/components/Chatbot.jsx +972 -972
  17. package/runtime/velu-ui/components/CodeBlock.jsx +375 -375
  18. package/runtime/velu-ui/components/Columns.jsx +56 -56
  19. package/runtime/velu-ui/components/ContextMenu.jsx +327 -327
  20. package/runtime/velu-ui/components/ErrorCard.jsx +138 -138
  21. package/runtime/velu-ui/components/Field.jsx +81 -81
  22. package/runtime/velu-ui/components/Image.jsx +163 -163
  23. package/runtime/velu-ui/components/Logo.jsx +31 -31
  24. package/runtime/velu-ui/components/MethodBadge.jsx +31 -31
  25. package/runtime/velu-ui/components/NavSelect.jsx +108 -108
  26. package/runtime/velu-ui/components/NotFound.jsx +63 -63
  27. package/runtime/velu-ui/components/PageFeedback.jsx +219 -219
  28. package/runtime/velu-ui/components/PageFooter.jsx +145 -145
  29. package/runtime/velu-ui/components/PageHeader.jsx +422 -422
  30. package/runtime/velu-ui/components/PageNav.jsx +77 -77
  31. package/runtime/velu-ui/components/PoweredBy.jsx +51 -51
  32. package/runtime/velu-ui/components/Prompt.jsx +115 -115
  33. package/runtime/velu-ui/components/Search.jsx +460 -460
  34. package/runtime/velu-ui/components/Sidebar.jsx +246 -246
  35. package/runtime/velu-ui/components/SocialLinks.jsx +90 -90
  36. package/runtime/velu-ui/components/Steps.jsx +65 -65
  37. package/runtime/velu-ui/components/ThemeToggle.jsx +48 -48
  38. package/runtime/velu-ui/components/Toc.jsx +537 -537
  39. package/runtime/velu-ui/components/TocBar.jsx +195 -195
  40. package/runtime/velu-ui/components/Tree.jsx +87 -87
  41. package/runtime/velu-ui/components/TryItBar.jsx +102 -102
  42. package/runtime/velu-ui/components/Update.jsx +92 -92
  43. package/runtime/velu-ui/components/accordion.css +92 -92
  44. package/runtime/velu-ui/components/api-page.css +208 -208
  45. package/runtime/velu-ui/components/api.css +635 -635
  46. package/runtime/velu-ui/components/ask-bar.css +103 -103
  47. package/runtime/velu-ui/components/card.css +105 -105
  48. package/runtime/velu-ui/components/changelog-filters.css +43 -43
  49. package/runtime/velu-ui/components/chatbot.css +687 -687
  50. package/runtime/velu-ui/components/code-block.css +263 -263
  51. package/runtime/velu-ui/components/context-menu.css +199 -199
  52. package/runtime/velu-ui/components/docs-layout.css +822 -822
  53. package/runtime/velu-ui/components/field.css +82 -82
  54. package/runtime/velu-ui/components/image.css +237 -237
  55. package/runtime/velu-ui/components/nav-select.css +157 -157
  56. package/runtime/velu-ui/components/not-found.css +94 -94
  57. package/runtime/velu-ui/components/page-feedback.css +261 -261
  58. package/runtime/velu-ui/components/page-footer.css +130 -130
  59. package/runtime/velu-ui/components/page-header.css +558 -558
  60. package/runtime/velu-ui/components/page-nav.css +50 -50
  61. package/runtime/velu-ui/components/powered-by.css +92 -92
  62. package/runtime/velu-ui/components/prompt.css +99 -99
  63. package/runtime/velu-ui/components/search.css +307 -307
  64. package/runtime/velu-ui/components/sidebar.css +205 -205
  65. package/runtime/velu-ui/components/steps.css +77 -77
  66. package/runtime/velu-ui/components/theme-toggle.css +102 -102
  67. package/runtime/velu-ui/components/toc-bar.css +234 -234
  68. package/runtime/velu-ui/components/tree.css +49 -49
  69. package/runtime/velu-ui/components/update.css +111 -111
  70. package/runtime/velu-ui/index.js +58 -58
  71. package/runtime/velu-ui/lib/api-send.js +118 -118
  72. package/runtime/velu-ui/lib/brand-icons.jsx +103 -103
  73. package/runtime/velu-ui/lib/component-schemas.js +100 -100
  74. package/runtime/velu-ui/lib/copyText.js +64 -64
  75. package/runtime/velu-ui/lib/docs-assistant.js +273 -273
  76. package/runtime/velu-ui/lib/lang-icons.jsx +147 -147
  77. package/runtime/velu-ui/lib/page-feedback.js +72 -72
  78. package/runtime/velu-ui/lib/pagefind.js +116 -116
  79. package/runtime/velu-ui/lib/prism-langs.js +957 -957
  80. package/runtime/velu-ui/lib/prism-loader.js +74 -74
  81. package/runtime/velu-ui/lib/resolveIcon.jsx +29 -29
  82. package/runtime/velu-ui/lib/scrollIntoNearestView.js +66 -66
  83. package/runtime/velu-ui/mdx-components.jsx +107 -107
  84. package/runtime/velu-ui/primitives/Cluster.jsx +49 -49
  85. package/runtime/velu-ui/primitives/Stack.jsx +63 -63
  86. package/runtime/velu-ui/primitives/Switcher.jsx +57 -57
  87. package/runtime/velu-ui/primitives/stack.css +3 -3
  88. package/runtime/velu-ui/primitives/switcher.css +25 -25
  89. package/runtime/velu-ui/styles.css +48 -48
  90. package/runtime/velu-ui/tokens.css +4 -4
  91. package/schema/velu.schema.json +419 -419
  92. package/src/lib/extract-mdx-error.js +170 -170
  93. package/src/lib/issues.js +159 -159
  94. package/src/lib/known-components.js +35 -35
  95. package/src/navigation.js +459 -459
  96. package/src/runtime/App.jsx +1754 -1754
  97. package/src/runtime/ErrorBoundary.jsx +54 -54
  98. package/src/runtime/client-entry.jsx +27 -27
  99. package/src/runtime/server-entry.jsx +24 -24
  100. package/src/template.html +48 -48
  101. package/templates/starter/ai-tools/claude-code.mdx +26 -26
  102. package/templates/starter/ai-tools/cursor.mdx +17 -17
  103. package/templates/starter/api-reference/introduction.mdx +43 -43
  104. package/templates/starter/development.mdx +19 -19
  105. package/templates/starter/essentials/code.mdx +29 -29
  106. package/templates/starter/essentials/images.mdx +29 -29
  107. package/templates/starter/essentials/markdown.mdx +25 -25
  108. package/templates/starter/essentials/navigation.mdx +39 -39
  109. package/templates/starter/essentials/settings.mdx +30 -30
  110. package/templates/starter/favicon.svg +6 -6
  111. package/templates/starter/index.mdx +31 -31
  112. package/templates/starter/openapi.json +160 -160
  113. package/templates/starter/quickstart.mdx +31 -31
  114. package/templates/starter/velu.json +40 -40
@@ -1,687 +1,687 @@
1
- /* Chatbot — the Velu "Ask AI" slide-out panel. Ported from the
2
- Claude-Design handoff and retokenized: every design literal is mapped
3
- onto velu-ui's scale, so the panel themes for free via [data-theme].
4
- crimson → --accent-color
5
- gray-100/hover → --surface-color
6
- gray-200/300 → --border-color
7
- gray-500/600 → --muted-color
8
- fg-1 (black) → --text-color
9
- white → --page-bg
10
- 4px spacing → --s-6 … and up the modular scale
11
- */
12
-
13
- /* ── Panel + slide ──────────────────────────────────────────────────── */
14
- /* Fixed panel width via `--vchat-width` (overridable, like --card-min);
15
- capped at 100vw so it can never exceed the viewport. */
16
- .velu-chatbot {
17
- /* Comfortable fixed panel width. The panel slides OVER the right
18
- edge (it's a floating overlay, z-50) so the article doesn't
19
- reflow when it opens. (Mobile/tablet use a full-width bottom
20
- sheet — see the @container override below.) */
21
- --vchat-width: 24rem;
22
- position: fixed;
23
- inset-block: 0;
24
- inset-inline-end: 0;
25
- z-index: 50;
26
- inline-size: var(--vchat-width);
27
- max-inline-size: 100vw;
28
- display: flex;
29
- flex-direction: column;
30
- background: var(--page-bg);
31
- color: var(--text-color);
32
- /* Panel edge — match the page header / TocBar / footer hairline
33
- recipe (--surface-color), not the stronger card/button border
34
- color. Keeps the chatbot consistent with other surface
35
- containers in the product. */
36
- border-inline-start: var(--border-width) solid var(--surface-color);
37
- font-weight: var(--weight-light);
38
- /* Closed: fully off the inline-end edge. Open: slid to 0. */
39
- transform: translateX(100%);
40
- transition: transform 0.28s cubic-bezier(0.4, 0, 0.2, 1);
41
- }
42
- .velu-chatbot--open {
43
- transform: translateX(0);
44
- box-shadow:
45
- 0 0 var(--s2) color-mix(in srgb, #000 18%, transparent),
46
- 0 0 var(--s0) color-mix(in srgb, #000 8%, transparent);
47
- }
48
- /* base.css applies a global `* { max-width: 66ch }`; reset it so the
49
- panel's structural elements size to flex/the panel, not 66ch. */
50
- .velu-chatbot * {
51
- max-inline-size: none;
52
- }
53
-
54
- /* Drag handle is mobile-only — the @container block below flips it
55
- on; this default hide keeps the desktop slide-out clean. */
56
- .velu-chatbot__drag-handle {
57
- display: none;
58
- }
59
-
60
- /* ── Bottom-sheet variant (tablet + mobile, < 1024px) ─────────────── */
61
- /* At narrow / mobile widths the side-panel layout doesn't fit — the
62
- chatbot instead opens as a bottom sheet covering most of the
63
- viewport. Override the inline anchoring with block-end anchoring,
64
- full-width sizing, and a translateY transform that slides up from
65
- the bottom edge. The leading-edge border becomes a top-edge border
66
- and the sheet picks up top corner rounding so it reads as a
67
- separate surface lifting out of the viewport bottom. Threshold
68
- matches the rest of the responsive layout (sidebar collapse,
69
- right-rail TOC drop, etc.). */
70
- @container docs (max-width: 1024px) {
71
- .velu-chatbot {
72
- inset-inline: 0;
73
- inset-block-end: 0;
74
- inset-block-start: auto;
75
- inline-size: 100vw;
76
- max-inline-size: 100vw;
77
- block-size: min(85vh, 40rem);
78
- transform: translateY(100%);
79
- border-inline-start: 0;
80
- border-block-start: var(--border-width) solid var(--surface-color);
81
- border-start-start-radius: var(--radius-md);
82
- border-start-end-radius: var(--radius-md);
83
- /* Make room at the top for the drag-handle pill (the ::before
84
- below) so it doesn't overlap the existing header content. */
85
- padding-block-start: var(--s1);
86
- }
87
- /* Drag handle — full-width touch-capture strip across the top of
88
- the sheet. The pill VISUAL is a centered ::before pseudo; the
89
- strip itself stays invisible but is tall enough to grab. Pointer
90
- events here drive the drag-to-dismiss gesture (see Chatbot.jsx).
91
- `touch-action: none` prevents the browser from interpreting the
92
- vertical drag as a scroll / pull-to-refresh. */
93
- .velu-chatbot__drag-handle {
94
- display: block;
95
- position: absolute;
96
- inset-block-start: 0;
97
- inset-inline: 0;
98
- block-size: var(--s1);
99
- touch-action: none;
100
- cursor: grab;
101
- z-index: 10;
102
- }
103
- .velu-chatbot__drag-handle::before {
104
- content: '';
105
- position: absolute;
106
- inset-block-start: var(--s-3);
107
- inset-inline-start: 50%;
108
- inline-size: var(--s2);
109
- block-size: var(--s-5);
110
- background: var(--border-color);
111
- border-radius: 999px;
112
- transform: translateX(-50%);
113
- pointer-events: none;
114
- }
115
- .velu-chatbot__drag-handle:active {
116
- cursor: grabbing;
117
- }
118
- .velu-chatbot--open {
119
- transform: translateY(0);
120
- }
121
- }
122
-
123
- /* ── Header ─────────────────────────────────────────────────────────── */
124
- /* Layout (flex/gap/justify) comes from <Cluster>; this owns chrome. */
125
- .velu-chatbot__header {
126
- flex: none;
127
- padding: var(--s-2) var(--s-1);
128
- /* Section divider — --surface-color hairline like the page
129
- header's own bottom border, not the stronger --border-color. */
130
- border-block-end: var(--border-width) solid var(--surface-color);
131
- background: var(--page-bg);
132
- }
133
- .velu-chatbot__mark {
134
- flex: none;
135
- color: var(--accent-color);
136
- }
137
- .velu-chatbot__brand {
138
- display: inline-flex;
139
- align-items: baseline;
140
- gap: var(--s-5);
141
- }
142
- .velu-chatbot__brand-name {
143
- font-size: var(--f-h5);
144
- font-weight: var(--weight-light);
145
- color: var(--text-color);
146
- }
147
- .velu-chatbot__brand-sep {
148
- color: var(--muted-color);
149
- }
150
- .velu-chatbot__brand-title {
151
- font-size: var(--f-h6);
152
- font-weight: var(--weight-medium);
153
- color: var(--muted-color);
154
- }
155
- .velu-chatbot__iconbtn {
156
- flex: none;
157
- display: inline-flex;
158
- align-items: center;
159
- justify-content: center;
160
- inline-size: 1.75em;
161
- block-size: 1.75em;
162
- padding: 0;
163
- background: transparent;
164
- border: 0;
165
- border-radius: var(--radius-sm);
166
- color: var(--muted-color);
167
- cursor: pointer;
168
- transition: background 0.12s ease, color 0.12s ease;
169
- }
170
- .velu-chatbot__iconbtn:hover {
171
- background: var(--surface-color);
172
- color: var(--text-color);
173
- }
174
- .velu-chatbot__iconbtn.is-active {
175
- background: color-mix(in srgb, var(--accent-color) 12%, transparent);
176
- color: var(--accent-color);
177
- }
178
-
179
- /* Shared eyebrow label (Suggested / Sources / Follow up / Recent chats) */
180
- .velu-chatbot__eyebrow {
181
- font-size: var(--f-h7);
182
- font-weight: var(--weight-medium);
183
- letter-spacing: 0.04em;
184
- text-transform: uppercase;
185
- color: var(--muted-color);
186
- }
187
-
188
- /* ── History overlay ────────────────────────────────────────────────── */
189
- .velu-chatbot__history {
190
- position: absolute;
191
- inset: 0;
192
- z-index: 5;
193
- display: flex;
194
- flex-direction: column;
195
- background: var(--page-bg);
196
- /* Inherit the chatbot's corner radii — at desktop this is 0 (no
197
- change); at mobile the parent has top-start / top-end rounded,
198
- so the history overlay picks them up too rather than painting
199
- square corners over the parent's curves. */
200
- border-radius: inherit;
201
- }
202
- .velu-chatbot__history-head {
203
- flex: none;
204
- padding: var(--s-2) var(--s-1);
205
- /* Same hairline recipe as the main chatbot header. */
206
- border-block-end: var(--border-width) solid var(--surface-color);
207
- }
208
- .velu-chatbot__history-list {
209
- flex: 1 1 auto;
210
- overflow-y: auto;
211
- padding: var(--s-3);
212
- }
213
- .velu-chatbot__history-item {
214
- display: flex;
215
- flex-direction: column;
216
- align-items: flex-start;
217
- gap: var(--s-6);
218
- padding: var(--s-3) var(--s-2);
219
- background: transparent;
220
- border: 0;
221
- border-radius: var(--radius-sm);
222
- text-align: start;
223
- font: inherit;
224
- cursor: pointer;
225
- transition: background 0.12s ease;
226
- }
227
- .velu-chatbot__history-item:hover {
228
- background: var(--surface-color);
229
- }
230
- .velu-chatbot__history-item-title {
231
- font-size: var(--f-h6);
232
- font-weight: var(--weight-normal);
233
- color: var(--text-color);
234
- }
235
- .velu-chatbot__history-item-when {
236
- font-size: var(--f-h6);
237
- font-weight: var(--weight-light);
238
- color: var(--muted-color);
239
- }
240
- .velu-chatbot__history-empty {
241
- font-size: var(--f-h6);
242
- color: var(--muted-color);
243
- padding: var(--s-3);
244
- }
245
-
246
- /* ── Body ───────────────────────────────────────────────────────────── */
247
- .velu-chatbot__body {
248
- flex: 1 1 auto;
249
- overflow-y: auto;
250
- padding: var(--s0) var(--s-1) var(--s-3);
251
- scroll-behavior: smooth;
252
- }
253
-
254
- /* ── Welcome / suggestions ──────────────────────────────────────────── */
255
- .velu-chatbot__welcome-hi {
256
- margin: 0;
257
- font-size: var(--f-h4);
258
- font-weight: var(--weight-medium);
259
- line-height: var(--lh-h4);
260
- color: var(--text-color);
261
- }
262
- .velu-chatbot__suggest {
263
- display: flex;
264
- align-items: center;
265
- justify-content: space-between;
266
- gap: var(--s-2);
267
- padding: var(--s-2) var(--s-1);
268
- background: var(--page-bg);
269
- border: var(--border-width) solid var(--border-color);
270
- border-radius: var(--radius-sm);
271
- font: inherit;
272
- font-size: var(--f-h6);
273
- font-weight: var(--weight-light);
274
- line-height: var(--lh-h6);
275
- color: var(--text-color);
276
- text-align: start;
277
- cursor: pointer;
278
- transition: border-color 0.12s ease, background 0.12s ease;
279
- }
280
- .velu-chatbot__suggest:hover {
281
- border-color: var(--accent-color);
282
- background: color-mix(in srgb, var(--accent-color) 6%, transparent);
283
- }
284
- .velu-chatbot__suggest svg {
285
- flex: none;
286
- color: var(--muted-color);
287
- }
288
- .velu-chatbot__suggest:hover svg {
289
- color: var(--accent-color);
290
- }
291
-
292
- /* ── Messages ───────────────────────────────────────────────────────── */
293
- .velu-chatbot__msg {
294
- display: flex;
295
- flex-direction: column;
296
- gap: var(--s-3);
297
- }
298
- .velu-chatbot__msg--user {
299
- align-items: flex-end;
300
- }
301
- .velu-chatbot__bubble {
302
- max-inline-size: 84%;
303
- padding: var(--s-3) var(--s-1);
304
- background: var(--surface-color);
305
- color: var(--text-color);
306
- border-radius: var(--radius-md) var(--radius-md) var(--radius-sm)
307
- var(--radius-md);
308
- font-size: var(--f-h6);
309
- line-height: var(--lh-body);
310
- }
311
- .velu-chatbot__role {
312
- font-size: var(--f-h6);
313
- font-weight: var(--weight-medium);
314
- letter-spacing: 0.04em;
315
- text-transform: uppercase;
316
- color: var(--muted-color);
317
- }
318
- .velu-chatbot__role svg {
319
- flex: none;
320
- color: var(--accent-color);
321
- }
322
- .velu-chatbot__role.is-thinking {
323
- color: var(--accent-color);
324
- }
325
- /* Shimmer sweep on the "Velu is thinking" label. */
326
- .velu-chatbot__role.is-thinking span {
327
- background: linear-gradient(
328
- 90deg,
329
- var(--muted-color) 0%,
330
- var(--muted-color) 35%,
331
- var(--accent-color) 50%,
332
- var(--muted-color) 65%,
333
- var(--muted-color) 100%
334
- );
335
- background-size: 200% 100%;
336
- -webkit-background-clip: text;
337
- background-clip: text;
338
- -webkit-text-fill-color: transparent;
339
- animation: velu-chatbot-shimmer 1.8s linear infinite;
340
- }
341
- @keyframes velu-chatbot-shimmer {
342
- 0% { background-position: 100% 0; }
343
- 100% { background-position: -100% 0; }
344
- }
345
-
346
- /* "Thinking" loader — 3x3 squares, opacity wave along the anti-diagonal. */
347
- .velu-chatbot__wave {
348
- display: block;
349
- flex: none;
350
- inline-size: 1em;
351
- block-size: 1em;
352
- }
353
- .velu-chatbot__wave rect {
354
- fill: var(--accent-color);
355
- animation: velu-chatbot-wave 1.3s ease-in-out infinite;
356
- }
357
- .velu-chatbot__wave rect[data-cell="0"] { animation-delay: 0s; }
358
- .velu-chatbot__wave rect[data-cell="1"] { animation-delay: 0.1s; }
359
- .velu-chatbot__wave rect[data-cell="2"] { animation-delay: 0.2s; }
360
- .velu-chatbot__wave rect[data-cell="3"] { animation-delay: 0.3s; }
361
- .velu-chatbot__wave rect[data-cell="4"] { animation-delay: 0.4s; }
362
- @keyframes velu-chatbot-wave {
363
- 0%, 60%, 100% { opacity: 0.2; }
364
- 30% { opacity: 1; }
365
- }
366
-
367
- /* ── AI answer body ─────────────────────────────────────────────────── */
368
- .velu-chatbot__answer {
369
- font-size: var(--f-h6);
370
- line-height: var(--lh-body);
371
- color: var(--text-color);
372
- }
373
- .velu-chatbot__answer p {
374
- margin: 0 0 var(--s-3);
375
- }
376
- .velu-chatbot__answer p:last-child {
377
- margin-block-end: 0;
378
- }
379
- .velu-chatbot__answer code {
380
- font-family: var(--font-mono);
381
- font-size: var(--f-h7);
382
- padding: 0 var(--s-5);
383
- background: var(--surface-color);
384
- border: var(--border-width) solid var(--border-color);
385
- border-radius: var(--radius-sm);
386
- }
387
-
388
- /* Rendered markdown in answers */
389
- .velu-chatbot__answer strong {
390
- font-weight: 600;
391
- }
392
- .velu-chatbot__answer em {
393
- font-style: italic;
394
- }
395
- .velu-chatbot__answer a:not(.velu-chatbot__cite) {
396
- color: var(--accent-color);
397
- text-decoration: underline;
398
- text-underline-offset: 2px;
399
- }
400
- .velu-chatbot__answer h3,
401
- .velu-chatbot__answer h4,
402
- .velu-chatbot__answer h5,
403
- .velu-chatbot__answer h6 {
404
- margin: var(--s-2) 0 var(--s-4);
405
- font-weight: 600;
406
- line-height: var(--lh-heading, 1.3);
407
- }
408
- .velu-chatbot__answer h3 { font-size: var(--f-h5); }
409
- .velu-chatbot__answer h4 { font-size: var(--f-h6); }
410
- .velu-chatbot__answer h5,
411
- .velu-chatbot__answer h6 { font-size: var(--f-h7); }
412
- .velu-chatbot__answer ul,
413
- .velu-chatbot__answer ol {
414
- margin: 0 0 var(--s-3);
415
- padding-inline-start: var(--s-1);
416
- }
417
- .velu-chatbot__answer li {
418
- margin: 0 0 var(--s-5);
419
- }
420
- .velu-chatbot__answer li::marker {
421
- color: var(--muted-color);
422
- }
423
- .velu-chatbot__pre {
424
- margin: 0 0 var(--s-3);
425
- padding: var(--s-3);
426
- overflow-x: auto;
427
- background: var(--surface-color);
428
- border: var(--border-width) solid var(--border-color);
429
- border-radius: var(--radius-sm);
430
- }
431
- .velu-chatbot__pre code {
432
- padding: 0;
433
- background: none;
434
- border: none;
435
- font-size: var(--f-h7);
436
- white-space: pre;
437
- }
438
- .velu-chatbot__answer blockquote {
439
- margin: 0 0 var(--s-3);
440
- padding-inline-start: var(--s-3);
441
- border-inline-start: 2px solid var(--border-color);
442
- color: var(--muted-color);
443
- }
444
-
445
- /* Inline citation chip */
446
- .velu-chatbot__cite {
447
- display: inline-flex;
448
- align-items: center;
449
- justify-content: center;
450
- min-inline-size: 1.5em;
451
- block-size: 1.5em;
452
- padding: 0 var(--s-5);
453
- font-family: var(--font-mono);
454
- font-size: var(--f-h7);
455
- font-weight: var(--weight-medium);
456
- color: var(--accent-color);
457
- background: color-mix(in srgb, var(--accent-color) 14%, transparent);
458
- border: var(--border-width) solid transparent;
459
- border-radius: var(--radius-sm);
460
- text-decoration: none;
461
- cursor: pointer;
462
- transition: background 0.12s ease, border-color 0.12s ease;
463
- }
464
- .velu-chatbot__cite:hover {
465
- background: var(--page-bg);
466
- border-color: var(--accent-color);
467
- }
468
-
469
- /* Streaming caret */
470
- .velu-chatbot__caret {
471
- display: inline-block;
472
- inline-size: var(--s-4);
473
- block-size: 1em;
474
- vertical-align: text-bottom;
475
- background: var(--accent-color);
476
- animation: velu-chatbot-caret 0.9s steps(2) infinite;
477
- }
478
- @keyframes velu-chatbot-caret {
479
- 50% { opacity: 0; }
480
- }
481
-
482
- /* Sources card list */
483
- .velu-chatbot__sources {
484
- margin-block-start: var(--s-2);
485
- }
486
- .velu-chatbot__source {
487
- display: flex;
488
- align-items: flex-start;
489
- gap: var(--s-2);
490
- padding: var(--s-3) var(--s-2);
491
- background: var(--page-bg);
492
- border: var(--border-width) solid var(--border-color);
493
- border-radius: var(--radius-sm);
494
- text-decoration: none;
495
- color: inherit;
496
- transition: border-color 0.12s ease;
497
- }
498
- .velu-chatbot__source:hover {
499
- border-color: var(--accent-color);
500
- }
501
- .velu-chatbot__source-num {
502
- flex: none;
503
- display: inline-flex;
504
- align-items: center;
505
- justify-content: center;
506
- inline-size: 1.5em;
507
- block-size: 1.5em;
508
- font-family: var(--font-mono);
509
- font-size: var(--f-h7);
510
- font-weight: var(--weight-medium);
511
- color: var(--accent-color);
512
- background: color-mix(in srgb, var(--accent-color) 14%, transparent);
513
- border-radius: var(--radius-sm);
514
- }
515
- .velu-chatbot__source-meta {
516
- display: flex;
517
- flex-direction: column;
518
- gap: var(--s-6);
519
- min-inline-size: 0;
520
- }
521
- .velu-chatbot__source-title {
522
- font-size: var(--f-h6);
523
- font-weight: var(--weight-medium);
524
- color: var(--text-color);
525
- }
526
- .velu-chatbot__source-path {
527
- font-family: var(--font-mono);
528
- font-size: var(--f-h7);
529
- color: var(--muted-color);
530
- white-space: nowrap;
531
- overflow: hidden;
532
- text-overflow: ellipsis;
533
- }
534
-
535
- /* Follow-ups */
536
- .velu-chatbot__followups {
537
- margin-block-start: var(--s-1);
538
- }
539
- .velu-chatbot__followup {
540
- display: flex;
541
- align-items: center;
542
- justify-content: space-between;
543
- gap: var(--s-2);
544
- padding: var(--s-3) var(--s-2);
545
- background: transparent;
546
- border: var(--border-width) solid var(--border-color);
547
- border-radius: var(--radius-sm);
548
- font: inherit;
549
- font-size: var(--f-h6);
550
- font-weight: var(--weight-light);
551
- color: var(--text-color);
552
- text-align: start;
553
- cursor: pointer;
554
- transition: border-color 0.12s ease, color 0.12s ease;
555
- }
556
- .velu-chatbot__followup:hover {
557
- border-color: var(--accent-color);
558
- color: var(--accent-color);
559
- }
560
- .velu-chatbot__followup svg {
561
- flex: none;
562
- }
563
-
564
- /* Message hover actions */
565
- .velu-chatbot__msg-actions {
566
- margin-inline-start: calc(var(--s-3) * -1);
567
- opacity: 0;
568
- transition: opacity 0.12s ease;
569
- }
570
- .velu-chatbot__msg--ai:hover .velu-chatbot__msg-actions,
571
- /* Keep the row visible once a vote is cast, so the selection stays
572
- confirmed after the pointer leaves the message. */
573
- .velu-chatbot__msg-actions:has(.is-active) {
574
- opacity: 1;
575
- }
576
- .velu-chatbot__msg-action {
577
- display: inline-flex;
578
- align-items: center;
579
- justify-content: center;
580
- inline-size: 1.625em;
581
- block-size: 1.625em;
582
- padding: 0;
583
- background: transparent;
584
- border: 0;
585
- border-radius: var(--radius-sm);
586
- color: var(--muted-color);
587
- cursor: pointer;
588
- }
589
- .velu-chatbot__msg-action:hover {
590
- background: var(--surface-color);
591
- color: var(--text-color);
592
- }
593
- /* Chosen thumb (up/down) — accent tint so the click is clearly registered. */
594
- .velu-chatbot__msg-action.is-active {
595
- background: color-mix(in srgb, var(--accent-color) 12%, transparent);
596
- color: var(--accent-color);
597
- }
598
-
599
- /* ── Composer ───────────────────────────────────────────────────────── */
600
- .velu-chatbot__composer {
601
- flex: none;
602
- padding: var(--s-1);
603
- background: var(--page-bg);
604
- /* Section divider — --surface-color hairline matching the
605
- chatbot's other structural section borders. The input field
606
- INSIDE the composer keeps its --border-color (it's an
607
- interactive element, not a section divider). */
608
- border-block-start: var(--border-width) solid var(--surface-color);
609
- }
610
- .velu-chatbot__input-wrap {
611
- display: flex;
612
- align-items: flex-end;
613
- gap: var(--s-3);
614
- padding: var(--s-3);
615
- background: var(--page-bg);
616
- border: var(--border-width) solid var(--border-color);
617
- border-radius: var(--radius-sm);
618
- transition: border-color 0.12s ease, box-shadow 0.12s ease;
619
- }
620
- .velu-chatbot__input-wrap:focus-within {
621
- border-color: var(--accent-color);
622
- box-shadow: 0 0 0 var(--border-width) var(--accent-color);
623
- }
624
- .velu-chatbot__input {
625
- flex: 1 1 auto;
626
- min-inline-size: 0;
627
- /* ~4 lines of body leading before the textarea starts scrolling. */
628
- max-block-size: calc(1em * var(--lh-body) * 4);
629
- padding: 0;
630
- border: 0;
631
- outline: none;
632
- resize: none;
633
- background: transparent;
634
- font: inherit;
635
- font-size: var(--f-h6);
636
- font-weight: var(--weight-light);
637
- line-height: var(--lh-body);
638
- color: var(--text-color);
639
- }
640
- .velu-chatbot__input::placeholder {
641
- color: var(--muted-color);
642
- }
643
- .velu-chatbot__send {
644
- flex: none;
645
- display: inline-flex;
646
- align-items: center;
647
- justify-content: center;
648
- inline-size: 1.75em;
649
- block-size: 1.75em;
650
- padding: 0;
651
- border: 0;
652
- border-radius: var(--radius-sm);
653
- background: var(--surface-color);
654
- color: var(--muted-color);
655
- cursor: pointer;
656
- transition: background 0.12s ease, color 0.12s ease;
657
- }
658
- .velu-chatbot__send.is-active {
659
- background: var(--accent-color);
660
- color: #fff;
661
- }
662
- .velu-chatbot__send:disabled {
663
- cursor: not-allowed;
664
- }
665
- .velu-chatbot__foot {
666
- margin-block-start: var(--s-3);
667
- padding-inline: var(--s-5);
668
- font-size: var(--f-h6);
669
- font-weight: var(--weight-light);
670
- color: var(--muted-color);
671
- }
672
- .velu-chatbot__foot kbd {
673
- font-family: var(--font-mono);
674
- font-size: var(--f-h7);
675
- padding: 0 var(--s-5);
676
- background: var(--page-bg);
677
- border: var(--border-width) solid var(--border-color);
678
- border-radius: var(--radius-sm);
679
- color: var(--muted-color);
680
- }
681
-
682
- @media (prefers-reduced-motion: reduce) {
683
- .velu-chatbot { transition: none; }
684
- .velu-chatbot__wave rect,
685
- .velu-chatbot__role.is-thinking span,
686
- .velu-chatbot__caret { animation: none; }
687
- }
1
+ /* Chatbot — the Velu "Ask AI" slide-out panel. Ported from the
2
+ Claude-Design handoff and retokenized: every design literal is mapped
3
+ onto velu-ui's scale, so the panel themes for free via [data-theme].
4
+ crimson → --accent-color
5
+ gray-100/hover → --surface-color
6
+ gray-200/300 → --border-color
7
+ gray-500/600 → --muted-color
8
+ fg-1 (black) → --text-color
9
+ white → --page-bg
10
+ 4px spacing → --s-6 … and up the modular scale
11
+ */
12
+
13
+ /* ── Panel + slide ──────────────────────────────────────────────────── */
14
+ /* Fixed panel width via `--vchat-width` (overridable, like --card-min);
15
+ capped at 100vw so it can never exceed the viewport. */
16
+ .velu-chatbot {
17
+ /* Comfortable fixed panel width. The panel slides OVER the right
18
+ edge (it's a floating overlay, z-50) so the article doesn't
19
+ reflow when it opens. (Mobile/tablet use a full-width bottom
20
+ sheet — see the @container override below.) */
21
+ --vchat-width: 24rem;
22
+ position: fixed;
23
+ inset-block: 0;
24
+ inset-inline-end: 0;
25
+ z-index: 50;
26
+ inline-size: var(--vchat-width);
27
+ max-inline-size: 100vw;
28
+ display: flex;
29
+ flex-direction: column;
30
+ background: var(--page-bg);
31
+ color: var(--text-color);
32
+ /* Panel edge — match the page header / TocBar / footer hairline
33
+ recipe (--surface-color), not the stronger card/button border
34
+ color. Keeps the chatbot consistent with other surface
35
+ containers in the product. */
36
+ border-inline-start: var(--border-width) solid var(--surface-color);
37
+ font-weight: var(--weight-light);
38
+ /* Closed: fully off the inline-end edge. Open: slid to 0. */
39
+ transform: translateX(100%);
40
+ transition: transform 0.28s cubic-bezier(0.4, 0, 0.2, 1);
41
+ }
42
+ .velu-chatbot--open {
43
+ transform: translateX(0);
44
+ box-shadow:
45
+ 0 0 var(--s2) color-mix(in srgb, #000 18%, transparent),
46
+ 0 0 var(--s0) color-mix(in srgb, #000 8%, transparent);
47
+ }
48
+ /* base.css applies a global `* { max-width: 66ch }`; reset it so the
49
+ panel's structural elements size to flex/the panel, not 66ch. */
50
+ .velu-chatbot * {
51
+ max-inline-size: none;
52
+ }
53
+
54
+ /* Drag handle is mobile-only — the @container block below flips it
55
+ on; this default hide keeps the desktop slide-out clean. */
56
+ .velu-chatbot__drag-handle {
57
+ display: none;
58
+ }
59
+
60
+ /* ── Bottom-sheet variant (tablet + mobile, < 1024px) ─────────────── */
61
+ /* At narrow / mobile widths the side-panel layout doesn't fit — the
62
+ chatbot instead opens as a bottom sheet covering most of the
63
+ viewport. Override the inline anchoring with block-end anchoring,
64
+ full-width sizing, and a translateY transform that slides up from
65
+ the bottom edge. The leading-edge border becomes a top-edge border
66
+ and the sheet picks up top corner rounding so it reads as a
67
+ separate surface lifting out of the viewport bottom. Threshold
68
+ matches the rest of the responsive layout (sidebar collapse,
69
+ right-rail TOC drop, etc.). */
70
+ @container docs (max-width: 1024px) {
71
+ .velu-chatbot {
72
+ inset-inline: 0;
73
+ inset-block-end: 0;
74
+ inset-block-start: auto;
75
+ inline-size: 100vw;
76
+ max-inline-size: 100vw;
77
+ block-size: min(85vh, 40rem);
78
+ transform: translateY(100%);
79
+ border-inline-start: 0;
80
+ border-block-start: var(--border-width) solid var(--surface-color);
81
+ border-start-start-radius: var(--radius-md);
82
+ border-start-end-radius: var(--radius-md);
83
+ /* Make room at the top for the drag-handle pill (the ::before
84
+ below) so it doesn't overlap the existing header content. */
85
+ padding-block-start: var(--s1);
86
+ }
87
+ /* Drag handle — full-width touch-capture strip across the top of
88
+ the sheet. The pill VISUAL is a centered ::before pseudo; the
89
+ strip itself stays invisible but is tall enough to grab. Pointer
90
+ events here drive the drag-to-dismiss gesture (see Chatbot.jsx).
91
+ `touch-action: none` prevents the browser from interpreting the
92
+ vertical drag as a scroll / pull-to-refresh. */
93
+ .velu-chatbot__drag-handle {
94
+ display: block;
95
+ position: absolute;
96
+ inset-block-start: 0;
97
+ inset-inline: 0;
98
+ block-size: var(--s1);
99
+ touch-action: none;
100
+ cursor: grab;
101
+ z-index: 10;
102
+ }
103
+ .velu-chatbot__drag-handle::before {
104
+ content: '';
105
+ position: absolute;
106
+ inset-block-start: var(--s-3);
107
+ inset-inline-start: 50%;
108
+ inline-size: var(--s2);
109
+ block-size: var(--s-5);
110
+ background: var(--border-color);
111
+ border-radius: 999px;
112
+ transform: translateX(-50%);
113
+ pointer-events: none;
114
+ }
115
+ .velu-chatbot__drag-handle:active {
116
+ cursor: grabbing;
117
+ }
118
+ .velu-chatbot--open {
119
+ transform: translateY(0);
120
+ }
121
+ }
122
+
123
+ /* ── Header ─────────────────────────────────────────────────────────── */
124
+ /* Layout (flex/gap/justify) comes from <Cluster>; this owns chrome. */
125
+ .velu-chatbot__header {
126
+ flex: none;
127
+ padding: var(--s-2) var(--s-1);
128
+ /* Section divider — --surface-color hairline like the page
129
+ header's own bottom border, not the stronger --border-color. */
130
+ border-block-end: var(--border-width) solid var(--surface-color);
131
+ background: var(--page-bg);
132
+ }
133
+ .velu-chatbot__mark {
134
+ flex: none;
135
+ color: var(--accent-color);
136
+ }
137
+ .velu-chatbot__brand {
138
+ display: inline-flex;
139
+ align-items: baseline;
140
+ gap: var(--s-5);
141
+ }
142
+ .velu-chatbot__brand-name {
143
+ font-size: var(--f-h5);
144
+ font-weight: var(--weight-light);
145
+ color: var(--text-color);
146
+ }
147
+ .velu-chatbot__brand-sep {
148
+ color: var(--muted-color);
149
+ }
150
+ .velu-chatbot__brand-title {
151
+ font-size: var(--f-h6);
152
+ font-weight: var(--weight-medium);
153
+ color: var(--muted-color);
154
+ }
155
+ .velu-chatbot__iconbtn {
156
+ flex: none;
157
+ display: inline-flex;
158
+ align-items: center;
159
+ justify-content: center;
160
+ inline-size: 1.75em;
161
+ block-size: 1.75em;
162
+ padding: 0;
163
+ background: transparent;
164
+ border: 0;
165
+ border-radius: var(--radius-sm);
166
+ color: var(--muted-color);
167
+ cursor: pointer;
168
+ transition: background 0.12s ease, color 0.12s ease;
169
+ }
170
+ .velu-chatbot__iconbtn:hover {
171
+ background: var(--surface-color);
172
+ color: var(--text-color);
173
+ }
174
+ .velu-chatbot__iconbtn.is-active {
175
+ background: color-mix(in srgb, var(--accent-color) 12%, transparent);
176
+ color: var(--accent-color);
177
+ }
178
+
179
+ /* Shared eyebrow label (Suggested / Sources / Follow up / Recent chats) */
180
+ .velu-chatbot__eyebrow {
181
+ font-size: var(--f-h7);
182
+ font-weight: var(--weight-medium);
183
+ letter-spacing: 0.04em;
184
+ text-transform: uppercase;
185
+ color: var(--muted-color);
186
+ }
187
+
188
+ /* ── History overlay ────────────────────────────────────────────────── */
189
+ .velu-chatbot__history {
190
+ position: absolute;
191
+ inset: 0;
192
+ z-index: 5;
193
+ display: flex;
194
+ flex-direction: column;
195
+ background: var(--page-bg);
196
+ /* Inherit the chatbot's corner radii — at desktop this is 0 (no
197
+ change); at mobile the parent has top-start / top-end rounded,
198
+ so the history overlay picks them up too rather than painting
199
+ square corners over the parent's curves. */
200
+ border-radius: inherit;
201
+ }
202
+ .velu-chatbot__history-head {
203
+ flex: none;
204
+ padding: var(--s-2) var(--s-1);
205
+ /* Same hairline recipe as the main chatbot header. */
206
+ border-block-end: var(--border-width) solid var(--surface-color);
207
+ }
208
+ .velu-chatbot__history-list {
209
+ flex: 1 1 auto;
210
+ overflow-y: auto;
211
+ padding: var(--s-3);
212
+ }
213
+ .velu-chatbot__history-item {
214
+ display: flex;
215
+ flex-direction: column;
216
+ align-items: flex-start;
217
+ gap: var(--s-6);
218
+ padding: var(--s-3) var(--s-2);
219
+ background: transparent;
220
+ border: 0;
221
+ border-radius: var(--radius-sm);
222
+ text-align: start;
223
+ font: inherit;
224
+ cursor: pointer;
225
+ transition: background 0.12s ease;
226
+ }
227
+ .velu-chatbot__history-item:hover {
228
+ background: var(--surface-color);
229
+ }
230
+ .velu-chatbot__history-item-title {
231
+ font-size: var(--f-h6);
232
+ font-weight: var(--weight-normal);
233
+ color: var(--text-color);
234
+ }
235
+ .velu-chatbot__history-item-when {
236
+ font-size: var(--f-h6);
237
+ font-weight: var(--weight-light);
238
+ color: var(--muted-color);
239
+ }
240
+ .velu-chatbot__history-empty {
241
+ font-size: var(--f-h6);
242
+ color: var(--muted-color);
243
+ padding: var(--s-3);
244
+ }
245
+
246
+ /* ── Body ───────────────────────────────────────────────────────────── */
247
+ .velu-chatbot__body {
248
+ flex: 1 1 auto;
249
+ overflow-y: auto;
250
+ padding: var(--s0) var(--s-1) var(--s-3);
251
+ scroll-behavior: smooth;
252
+ }
253
+
254
+ /* ── Welcome / suggestions ──────────────────────────────────────────── */
255
+ .velu-chatbot__welcome-hi {
256
+ margin: 0;
257
+ font-size: var(--f-h4);
258
+ font-weight: var(--weight-medium);
259
+ line-height: var(--lh-h4);
260
+ color: var(--text-color);
261
+ }
262
+ .velu-chatbot__suggest {
263
+ display: flex;
264
+ align-items: center;
265
+ justify-content: space-between;
266
+ gap: var(--s-2);
267
+ padding: var(--s-2) var(--s-1);
268
+ background: var(--page-bg);
269
+ border: var(--border-width) solid var(--border-color);
270
+ border-radius: var(--radius-sm);
271
+ font: inherit;
272
+ font-size: var(--f-h6);
273
+ font-weight: var(--weight-light);
274
+ line-height: var(--lh-h6);
275
+ color: var(--text-color);
276
+ text-align: start;
277
+ cursor: pointer;
278
+ transition: border-color 0.12s ease, background 0.12s ease;
279
+ }
280
+ .velu-chatbot__suggest:hover {
281
+ border-color: var(--accent-color);
282
+ background: color-mix(in srgb, var(--accent-color) 6%, transparent);
283
+ }
284
+ .velu-chatbot__suggest svg {
285
+ flex: none;
286
+ color: var(--muted-color);
287
+ }
288
+ .velu-chatbot__suggest:hover svg {
289
+ color: var(--accent-color);
290
+ }
291
+
292
+ /* ── Messages ───────────────────────────────────────────────────────── */
293
+ .velu-chatbot__msg {
294
+ display: flex;
295
+ flex-direction: column;
296
+ gap: var(--s-3);
297
+ }
298
+ .velu-chatbot__msg--user {
299
+ align-items: flex-end;
300
+ }
301
+ .velu-chatbot__bubble {
302
+ max-inline-size: 84%;
303
+ padding: var(--s-3) var(--s-1);
304
+ background: var(--surface-color);
305
+ color: var(--text-color);
306
+ border-radius: var(--radius-md) var(--radius-md) var(--radius-sm)
307
+ var(--radius-md);
308
+ font-size: var(--f-h6);
309
+ line-height: var(--lh-body);
310
+ }
311
+ .velu-chatbot__role {
312
+ font-size: var(--f-h6);
313
+ font-weight: var(--weight-medium);
314
+ letter-spacing: 0.04em;
315
+ text-transform: uppercase;
316
+ color: var(--muted-color);
317
+ }
318
+ .velu-chatbot__role svg {
319
+ flex: none;
320
+ color: var(--accent-color);
321
+ }
322
+ .velu-chatbot__role.is-thinking {
323
+ color: var(--accent-color);
324
+ }
325
+ /* Shimmer sweep on the "Velu is thinking" label. */
326
+ .velu-chatbot__role.is-thinking span {
327
+ background: linear-gradient(
328
+ 90deg,
329
+ var(--muted-color) 0%,
330
+ var(--muted-color) 35%,
331
+ var(--accent-color) 50%,
332
+ var(--muted-color) 65%,
333
+ var(--muted-color) 100%
334
+ );
335
+ background-size: 200% 100%;
336
+ -webkit-background-clip: text;
337
+ background-clip: text;
338
+ -webkit-text-fill-color: transparent;
339
+ animation: velu-chatbot-shimmer 1.8s linear infinite;
340
+ }
341
+ @keyframes velu-chatbot-shimmer {
342
+ 0% { background-position: 100% 0; }
343
+ 100% { background-position: -100% 0; }
344
+ }
345
+
346
+ /* "Thinking" loader — 3x3 squares, opacity wave along the anti-diagonal. */
347
+ .velu-chatbot__wave {
348
+ display: block;
349
+ flex: none;
350
+ inline-size: 1em;
351
+ block-size: 1em;
352
+ }
353
+ .velu-chatbot__wave rect {
354
+ fill: var(--accent-color);
355
+ animation: velu-chatbot-wave 1.3s ease-in-out infinite;
356
+ }
357
+ .velu-chatbot__wave rect[data-cell="0"] { animation-delay: 0s; }
358
+ .velu-chatbot__wave rect[data-cell="1"] { animation-delay: 0.1s; }
359
+ .velu-chatbot__wave rect[data-cell="2"] { animation-delay: 0.2s; }
360
+ .velu-chatbot__wave rect[data-cell="3"] { animation-delay: 0.3s; }
361
+ .velu-chatbot__wave rect[data-cell="4"] { animation-delay: 0.4s; }
362
+ @keyframes velu-chatbot-wave {
363
+ 0%, 60%, 100% { opacity: 0.2; }
364
+ 30% { opacity: 1; }
365
+ }
366
+
367
+ /* ── AI answer body ─────────────────────────────────────────────────── */
368
+ .velu-chatbot__answer {
369
+ font-size: var(--f-h6);
370
+ line-height: var(--lh-body);
371
+ color: var(--text-color);
372
+ }
373
+ .velu-chatbot__answer p {
374
+ margin: 0 0 var(--s-3);
375
+ }
376
+ .velu-chatbot__answer p:last-child {
377
+ margin-block-end: 0;
378
+ }
379
+ .velu-chatbot__answer code {
380
+ font-family: var(--font-mono);
381
+ font-size: var(--f-h7);
382
+ padding: 0 var(--s-5);
383
+ background: var(--surface-color);
384
+ border: var(--border-width) solid var(--border-color);
385
+ border-radius: var(--radius-sm);
386
+ }
387
+
388
+ /* Rendered markdown in answers */
389
+ .velu-chatbot__answer strong {
390
+ font-weight: 600;
391
+ }
392
+ .velu-chatbot__answer em {
393
+ font-style: italic;
394
+ }
395
+ .velu-chatbot__answer a:not(.velu-chatbot__cite) {
396
+ color: var(--accent-color);
397
+ text-decoration: underline;
398
+ text-underline-offset: 2px;
399
+ }
400
+ .velu-chatbot__answer h3,
401
+ .velu-chatbot__answer h4,
402
+ .velu-chatbot__answer h5,
403
+ .velu-chatbot__answer h6 {
404
+ margin: var(--s-2) 0 var(--s-4);
405
+ font-weight: 600;
406
+ line-height: var(--lh-heading, 1.3);
407
+ }
408
+ .velu-chatbot__answer h3 { font-size: var(--f-h5); }
409
+ .velu-chatbot__answer h4 { font-size: var(--f-h6); }
410
+ .velu-chatbot__answer h5,
411
+ .velu-chatbot__answer h6 { font-size: var(--f-h7); }
412
+ .velu-chatbot__answer ul,
413
+ .velu-chatbot__answer ol {
414
+ margin: 0 0 var(--s-3);
415
+ padding-inline-start: var(--s-1);
416
+ }
417
+ .velu-chatbot__answer li {
418
+ margin: 0 0 var(--s-5);
419
+ }
420
+ .velu-chatbot__answer li::marker {
421
+ color: var(--muted-color);
422
+ }
423
+ .velu-chatbot__pre {
424
+ margin: 0 0 var(--s-3);
425
+ padding: var(--s-3);
426
+ overflow-x: auto;
427
+ background: var(--surface-color);
428
+ border: var(--border-width) solid var(--border-color);
429
+ border-radius: var(--radius-sm);
430
+ }
431
+ .velu-chatbot__pre code {
432
+ padding: 0;
433
+ background: none;
434
+ border: none;
435
+ font-size: var(--f-h7);
436
+ white-space: pre;
437
+ }
438
+ .velu-chatbot__answer blockquote {
439
+ margin: 0 0 var(--s-3);
440
+ padding-inline-start: var(--s-3);
441
+ border-inline-start: 2px solid var(--border-color);
442
+ color: var(--muted-color);
443
+ }
444
+
445
+ /* Inline citation chip */
446
+ .velu-chatbot__cite {
447
+ display: inline-flex;
448
+ align-items: center;
449
+ justify-content: center;
450
+ min-inline-size: 1.5em;
451
+ block-size: 1.5em;
452
+ padding: 0 var(--s-5);
453
+ font-family: var(--font-mono);
454
+ font-size: var(--f-h7);
455
+ font-weight: var(--weight-medium);
456
+ color: var(--accent-color);
457
+ background: color-mix(in srgb, var(--accent-color) 14%, transparent);
458
+ border: var(--border-width) solid transparent;
459
+ border-radius: var(--radius-sm);
460
+ text-decoration: none;
461
+ cursor: pointer;
462
+ transition: background 0.12s ease, border-color 0.12s ease;
463
+ }
464
+ .velu-chatbot__cite:hover {
465
+ background: var(--page-bg);
466
+ border-color: var(--accent-color);
467
+ }
468
+
469
+ /* Streaming caret */
470
+ .velu-chatbot__caret {
471
+ display: inline-block;
472
+ inline-size: var(--s-4);
473
+ block-size: 1em;
474
+ vertical-align: text-bottom;
475
+ background: var(--accent-color);
476
+ animation: velu-chatbot-caret 0.9s steps(2) infinite;
477
+ }
478
+ @keyframes velu-chatbot-caret {
479
+ 50% { opacity: 0; }
480
+ }
481
+
482
+ /* Sources card list */
483
+ .velu-chatbot__sources {
484
+ margin-block-start: var(--s-2);
485
+ }
486
+ .velu-chatbot__source {
487
+ display: flex;
488
+ align-items: flex-start;
489
+ gap: var(--s-2);
490
+ padding: var(--s-3) var(--s-2);
491
+ background: var(--page-bg);
492
+ border: var(--border-width) solid var(--border-color);
493
+ border-radius: var(--radius-sm);
494
+ text-decoration: none;
495
+ color: inherit;
496
+ transition: border-color 0.12s ease;
497
+ }
498
+ .velu-chatbot__source:hover {
499
+ border-color: var(--accent-color);
500
+ }
501
+ .velu-chatbot__source-num {
502
+ flex: none;
503
+ display: inline-flex;
504
+ align-items: center;
505
+ justify-content: center;
506
+ inline-size: 1.5em;
507
+ block-size: 1.5em;
508
+ font-family: var(--font-mono);
509
+ font-size: var(--f-h7);
510
+ font-weight: var(--weight-medium);
511
+ color: var(--accent-color);
512
+ background: color-mix(in srgb, var(--accent-color) 14%, transparent);
513
+ border-radius: var(--radius-sm);
514
+ }
515
+ .velu-chatbot__source-meta {
516
+ display: flex;
517
+ flex-direction: column;
518
+ gap: var(--s-6);
519
+ min-inline-size: 0;
520
+ }
521
+ .velu-chatbot__source-title {
522
+ font-size: var(--f-h6);
523
+ font-weight: var(--weight-medium);
524
+ color: var(--text-color);
525
+ }
526
+ .velu-chatbot__source-path {
527
+ font-family: var(--font-mono);
528
+ font-size: var(--f-h7);
529
+ color: var(--muted-color);
530
+ white-space: nowrap;
531
+ overflow: hidden;
532
+ text-overflow: ellipsis;
533
+ }
534
+
535
+ /* Follow-ups */
536
+ .velu-chatbot__followups {
537
+ margin-block-start: var(--s-1);
538
+ }
539
+ .velu-chatbot__followup {
540
+ display: flex;
541
+ align-items: center;
542
+ justify-content: space-between;
543
+ gap: var(--s-2);
544
+ padding: var(--s-3) var(--s-2);
545
+ background: transparent;
546
+ border: var(--border-width) solid var(--border-color);
547
+ border-radius: var(--radius-sm);
548
+ font: inherit;
549
+ font-size: var(--f-h6);
550
+ font-weight: var(--weight-light);
551
+ color: var(--text-color);
552
+ text-align: start;
553
+ cursor: pointer;
554
+ transition: border-color 0.12s ease, color 0.12s ease;
555
+ }
556
+ .velu-chatbot__followup:hover {
557
+ border-color: var(--accent-color);
558
+ color: var(--accent-color);
559
+ }
560
+ .velu-chatbot__followup svg {
561
+ flex: none;
562
+ }
563
+
564
+ /* Message hover actions */
565
+ .velu-chatbot__msg-actions {
566
+ margin-inline-start: calc(var(--s-3) * -1);
567
+ opacity: 0;
568
+ transition: opacity 0.12s ease;
569
+ }
570
+ .velu-chatbot__msg--ai:hover .velu-chatbot__msg-actions,
571
+ /* Keep the row visible once a vote is cast, so the selection stays
572
+ confirmed after the pointer leaves the message. */
573
+ .velu-chatbot__msg-actions:has(.is-active) {
574
+ opacity: 1;
575
+ }
576
+ .velu-chatbot__msg-action {
577
+ display: inline-flex;
578
+ align-items: center;
579
+ justify-content: center;
580
+ inline-size: 1.625em;
581
+ block-size: 1.625em;
582
+ padding: 0;
583
+ background: transparent;
584
+ border: 0;
585
+ border-radius: var(--radius-sm);
586
+ color: var(--muted-color);
587
+ cursor: pointer;
588
+ }
589
+ .velu-chatbot__msg-action:hover {
590
+ background: var(--surface-color);
591
+ color: var(--text-color);
592
+ }
593
+ /* Chosen thumb (up/down) — accent tint so the click is clearly registered. */
594
+ .velu-chatbot__msg-action.is-active {
595
+ background: color-mix(in srgb, var(--accent-color) 12%, transparent);
596
+ color: var(--accent-color);
597
+ }
598
+
599
+ /* ── Composer ───────────────────────────────────────────────────────── */
600
+ .velu-chatbot__composer {
601
+ flex: none;
602
+ padding: var(--s-1);
603
+ background: var(--page-bg);
604
+ /* Section divider — --surface-color hairline matching the
605
+ chatbot's other structural section borders. The input field
606
+ INSIDE the composer keeps its --border-color (it's an
607
+ interactive element, not a section divider). */
608
+ border-block-start: var(--border-width) solid var(--surface-color);
609
+ }
610
+ .velu-chatbot__input-wrap {
611
+ display: flex;
612
+ align-items: flex-end;
613
+ gap: var(--s-3);
614
+ padding: var(--s-3);
615
+ background: var(--page-bg);
616
+ border: var(--border-width) solid var(--border-color);
617
+ border-radius: var(--radius-sm);
618
+ transition: border-color 0.12s ease, box-shadow 0.12s ease;
619
+ }
620
+ .velu-chatbot__input-wrap:focus-within {
621
+ border-color: var(--accent-color);
622
+ box-shadow: 0 0 0 var(--border-width) var(--accent-color);
623
+ }
624
+ .velu-chatbot__input {
625
+ flex: 1 1 auto;
626
+ min-inline-size: 0;
627
+ /* ~4 lines of body leading before the textarea starts scrolling. */
628
+ max-block-size: calc(1em * var(--lh-body) * 4);
629
+ padding: 0;
630
+ border: 0;
631
+ outline: none;
632
+ resize: none;
633
+ background: transparent;
634
+ font: inherit;
635
+ font-size: var(--f-h6);
636
+ font-weight: var(--weight-light);
637
+ line-height: var(--lh-body);
638
+ color: var(--text-color);
639
+ }
640
+ .velu-chatbot__input::placeholder {
641
+ color: var(--muted-color);
642
+ }
643
+ .velu-chatbot__send {
644
+ flex: none;
645
+ display: inline-flex;
646
+ align-items: center;
647
+ justify-content: center;
648
+ inline-size: 1.75em;
649
+ block-size: 1.75em;
650
+ padding: 0;
651
+ border: 0;
652
+ border-radius: var(--radius-sm);
653
+ background: var(--surface-color);
654
+ color: var(--muted-color);
655
+ cursor: pointer;
656
+ transition: background 0.12s ease, color 0.12s ease;
657
+ }
658
+ .velu-chatbot__send.is-active {
659
+ background: var(--accent-color);
660
+ color: #fff;
661
+ }
662
+ .velu-chatbot__send:disabled {
663
+ cursor: not-allowed;
664
+ }
665
+ .velu-chatbot__foot {
666
+ margin-block-start: var(--s-3);
667
+ padding-inline: var(--s-5);
668
+ font-size: var(--f-h6);
669
+ font-weight: var(--weight-light);
670
+ color: var(--muted-color);
671
+ }
672
+ .velu-chatbot__foot kbd {
673
+ font-family: var(--font-mono);
674
+ font-size: var(--f-h7);
675
+ padding: 0 var(--s-5);
676
+ background: var(--page-bg);
677
+ border: var(--border-width) solid var(--border-color);
678
+ border-radius: var(--radius-sm);
679
+ color: var(--muted-color);
680
+ }
681
+
682
+ @media (prefers-reduced-motion: reduce) {
683
+ .velu-chatbot { transition: none; }
684
+ .velu-chatbot__wave rect,
685
+ .velu-chatbot__role.is-thinking span,
686
+ .velu-chatbot__caret { animation: none; }
687
+ }