@perxel/spotlight 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +174 -2
  3. package/dist/build/index.d.ts +32 -0
  4. package/dist/build/index.js +68 -0
  5. package/dist/chat/index.d.ts +3 -0
  6. package/dist/chat/index.js +5 -0
  7. package/dist/chat/parts.d.ts +57 -0
  8. package/dist/chat/parts.js +69 -0
  9. package/dist/chat/useAskChat.d.ts +67 -0
  10. package/dist/chat/useAskChat.js +200 -0
  11. package/dist/core/copy.d.ts +4 -0
  12. package/dist/core/copy.js +40 -0
  13. package/dist/core/fold.d.ts +10 -0
  14. package/dist/core/fold.js +23 -0
  15. package/dist/core/messages.d.ts +24 -0
  16. package/dist/core/messages.js +72 -0
  17. package/dist/core/search.d.ts +73 -0
  18. package/dist/core/search.js +210 -0
  19. package/dist/core/storage.d.ts +17 -0
  20. package/dist/core/storage.js +48 -0
  21. package/dist/gate/index.d.ts +2 -0
  22. package/dist/gate/index.js +5 -0
  23. package/dist/gate/parts.d.ts +179 -0
  24. package/dist/gate/parts.js +282 -0
  25. package/dist/gate/useSearchAsk.d.ts +90 -0
  26. package/dist/gate/useSearchAsk.js +122 -0
  27. package/dist/index.d.ts +7 -0
  28. package/dist/index.js +8 -0
  29. package/dist/liquid/context.d.ts +26 -0
  30. package/dist/liquid/context.js +6 -0
  31. package/dist/liquid/gate.d.ts +8 -0
  32. package/dist/liquid/gate.js +52 -0
  33. package/dist/liquid/icons.d.ts +12 -0
  34. package/dist/liquid/icons.js +15 -0
  35. package/dist/liquid/index.d.ts +9 -0
  36. package/dist/liquid/index.js +19 -0
  37. package/dist/liquid/liquid.css +1007 -0
  38. package/dist/liquid/panel.d.ts +8 -0
  39. package/dist/liquid/panel.js +186 -0
  40. package/dist/liquid/util.d.ts +6 -0
  41. package/dist/liquid/util.js +16 -0
  42. package/dist/react/index.d.ts +6 -0
  43. package/dist/react/index.js +10 -0
  44. package/dist/react/provider.d.ts +52 -0
  45. package/dist/react/provider.js +218 -0
  46. package/dist/react/turnstile.d.ts +16 -0
  47. package/dist/react/turnstile.js +100 -0
  48. package/dist/server/handler.d.ts +102 -0
  49. package/dist/server/handler.js +274 -0
  50. package/dist/server/index.d.ts +4 -0
  51. package/dist/server/index.js +5 -0
  52. package/dist/server/sources.d.ts +79 -0
  53. package/dist/server/sources.js +315 -0
  54. package/dist/server/turnstile.d.ts +15 -0
  55. package/dist/server/turnstile.js +29 -0
  56. package/dist/server/types.d.ts +34 -0
  57. package/dist/server/types.js +1 -0
  58. package/dist/types.d.ts +207 -0
  59. package/dist/types.js +7 -0
  60. package/package.json +102 -4
@@ -0,0 +1,1007 @@
1
+ /* @perxel/spotlight: the liquid skin. Plain CSS, `sp-` classes, themed with the variables below
2
+ (set them on `:root` or on the skin's `className`). Neutral defaults: grey glass, system font. */
3
+
4
+ :root {
5
+ /* The accent: marks in results, the highlighted row's tint, the tide, send button hover. */
6
+ --sp-accent: #1f6feb;
7
+ /* Text on the accent-tinted glass (the highlighted row). */
8
+ --sp-ink: #0b1f4d;
9
+ --sp-text: #111;
10
+ --sp-muted: #4e4e4e;
11
+ --sp-subtle: #777;
12
+ /* The panel's reading area, the user's bubble, a quiet fill (errors, kbd). */
13
+ --sp-surface: #fff;
14
+ --sp-bubble: #f2f2f2;
15
+ --sp-quiet: #f6f6f6;
16
+ /* The fill button (send, stop): background and hover. */
17
+ --sp-button: #111;
18
+ --sp-button-text: #fff;
19
+ --sp-button-hover: var(--sp-accent);
20
+ /* The glass: fill, rim, inner glow, lift (outer shadow). */
21
+ --sp-glass-fill: rgba(255, 255, 255, .5);
22
+ --sp-glass-edge: rgba(255, 255, 255, .8);
23
+ --sp-glow: rgba(255, 255, 255, .7);
24
+ --sp-lift: 0 0 0 .5px rgba(0, 0, 0, .14), 0 2px 6px rgba(0, 0, 0, .06), 0 8px 24px rgba(0, 0, 0, .08);
25
+ /* The glass over the page (gate) is denser: text sits on it over any background. */
26
+ --sp-gate-fill: rgba(255, 255, 255, .66);
27
+ /* The panel header pill, on the white reading area. */
28
+ --sp-pill-fill: rgba(242, 242, 242, .6);
29
+ --sp-font: ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
30
+ --sp-font-display: var(--sp-font);
31
+ --sp-display-weight: 400;
32
+ /* The page behind the gate. */
33
+ --sp-dim: rgba(0, 0, 0, .2);
34
+ --sp-dim-blur: 12px;
35
+ /* Stacking: the gate at `--sp-z`, the panel one under, the phone backdrop two under. */
36
+ --sp-z: 59;
37
+ }
38
+
39
+ /* The skin sizes boxes border-box, whatever the page's own reset (or none). */
40
+ .sp-overlay,
41
+ .sp-gate,
42
+ .sp-gate *,
43
+ .sp-backdrop,
44
+ .sp-panel,
45
+ .sp-panel *,
46
+ .sp-panel *::before,
47
+ .sp-panel *::after {
48
+ box-sizing: border-box;
49
+ }
50
+
51
+ /* ------------------------------------------------------------------ */
52
+ /* The glass. `.sp-liquid` is the white tone; `.sp-liquid-accent` the */
53
+ /* pale accent tone. A bright rim along the top, a lighter top half, a */
54
+ /* faint dark rim at the bottom, a soft inner glow, a hairline + lift. */
55
+ /* ------------------------------------------------------------------ */
56
+ .sp-liquid {
57
+ --sp-fill: var(--sp-glass-fill);
58
+ --sp-edge: var(--sp-glass-edge);
59
+ --sp-inner: var(--sp-glow);
60
+ --sp-shadow: var(--sp-lift);
61
+ background: linear-gradient(to bottom, rgba(255, 255, 255, .35), rgba(255, 255, 255, 0) 55%), var(--sp-fill);
62
+ -webkit-backdrop-filter: blur(16px) saturate(2);
63
+ backdrop-filter: blur(16px) saturate(2);
64
+ box-shadow: inset 0 0 0 1px var(--sp-edge),
65
+ inset 0 1.5px 0 #fff,
66
+ inset 0 -1px 0 color-mix(in srgb, var(--sp-ink) 8%, transparent),
67
+ inset 0 0 14px var(--sp-inner),
68
+ var(--sp-shadow);
69
+ transition: box-shadow .15s;
70
+ }
71
+
72
+ .sp-liquid-accent {
73
+ --sp-fill: color-mix(in srgb, var(--sp-accent) 12%, rgba(255, 255, 255, .7));
74
+ --sp-edge: color-mix(in srgb, var(--sp-accent) 28%, transparent);
75
+ --sp-inner: color-mix(in srgb, var(--sp-accent) 22%, transparent);
76
+ color: var(--sp-ink);
77
+ }
78
+
79
+ /* Over the page: denser and blurrier. */
80
+ .sp-glass {
81
+ --sp-fill: var(--sp-gate-fill);
82
+ -webkit-backdrop-filter: blur(24px) saturate(1.8);
83
+ backdrop-filter: blur(24px) saturate(1.8);
84
+ }
85
+
86
+ @media (prefers-reduced-transparency: reduce) {
87
+ .sp-liquid {
88
+ background: #fff;
89
+ -webkit-backdrop-filter: none;
90
+ backdrop-filter: none;
91
+ }
92
+
93
+ .sp-liquid-accent {
94
+ background: color-mix(in srgb, var(--sp-accent) 12%, #fff);
95
+ }
96
+ }
97
+
98
+ /* The fill button (send, stop). */
99
+ .sp-fill {
100
+ display: inline-grid;
101
+ place-items: center;
102
+ flex-shrink: 0;
103
+ border: 1px solid var(--sp-button);
104
+ border-radius: 9999px;
105
+ padding: 0;
106
+ background: var(--sp-button);
107
+ color: var(--sp-button-text);
108
+ cursor: pointer;
109
+ transition: background-color .15s, border-color .15s, transform .15s;
110
+ }
111
+
112
+ .sp-fill svg {
113
+ width: 16px;
114
+ height: 16px;
115
+ }
116
+
117
+ .sp-fill:disabled {
118
+ opacity: .4;
119
+ cursor: default;
120
+ }
121
+
122
+ @media (hover: hover) {
123
+ .sp-fill:not(:disabled):hover {
124
+ background: var(--sp-button-hover);
125
+ border-color: var(--sp-button-hover);
126
+ }
127
+ }
128
+
129
+ .sp-fill:not(:disabled):active {
130
+ transform: scale(.96);
131
+ }
132
+
133
+ .sp-truncate {
134
+ min-width: 0;
135
+ overflow: hidden;
136
+ text-overflow: ellipsis;
137
+ white-space: nowrap;
138
+ }
139
+
140
+ /* A thin scrollbar inside the skin. */
141
+ .sp-scroll {
142
+ overflow-y: auto;
143
+ overscroll-behavior: contain;
144
+ scrollbar-width: thin;
145
+ scrollbar-color: rgba(0, 0, 0, .2) transparent;
146
+ }
147
+
148
+ .sp-scroll::-webkit-scrollbar {
149
+ width: 6px;
150
+ }
151
+
152
+ .sp-scroll::-webkit-scrollbar-thumb {
153
+ border-radius: 9999px;
154
+ background: rgba(0, 0, 0, .2);
155
+ }
156
+
157
+ /* ------------------------------------------------------------------ */
158
+ /* The gate */
159
+ /* ------------------------------------------------------------------ */
160
+ .sp-overlay {
161
+ position: fixed;
162
+ inset: 0;
163
+ z-index: var(--sp-z);
164
+ background: var(--sp-dim);
165
+ -webkit-backdrop-filter: blur(var(--sp-dim-blur));
166
+ backdrop-filter: blur(var(--sp-dim-blur));
167
+ }
168
+
169
+ .sp-gate {
170
+ position: fixed;
171
+ inset-inline: 0.75rem;
172
+ top: 4rem;
173
+ z-index: var(--sp-z);
174
+ display: flex;
175
+ flex-direction: column;
176
+ gap: 0.5rem;
177
+ max-width: 640px;
178
+ margin-inline: auto;
179
+ color: var(--sp-text);
180
+ font-family: var(--sp-font);
181
+ outline: none;
182
+ }
183
+
184
+ @media (min-width: 64rem) {
185
+ .sp-gate {
186
+ top: 14vh;
187
+ max-height: calc(86vh - 3rem);
188
+ }
189
+ }
190
+
191
+ @media (prefers-reduced-motion: no-preference) {
192
+ .sp-overlay[data-state="open"] { animation: sp-fade-in .2s both; }
193
+ .sp-overlay[data-state="closed"] { animation: sp-fade-out .2s both; }
194
+ .sp-gate[data-state="open"] { animation: sp-gate-in .2s both; }
195
+ .sp-gate[data-state="closed"] { animation: sp-gate-out .2s both; }
196
+ }
197
+
198
+ @keyframes sp-fade-in { from { opacity: 0; } }
199
+ @keyframes sp-fade-out { to { opacity: 0; } }
200
+ @keyframes sp-gate-in { from { opacity: 0; transform: translateY(-8px) scale(.98); } }
201
+ @keyframes sp-gate-out { to { opacity: 0; transform: scale(.98); } }
202
+
203
+ /* Close: phones only, a glass circle floating above the box's top right corner. */
204
+ .sp-gate-close {
205
+ position: absolute;
206
+ right: 0;
207
+ bottom: 100%;
208
+ margin-bottom: 0.75rem;
209
+ display: flex;
210
+ align-items: center;
211
+ justify-content: center;
212
+ width: 2.5rem;
213
+ height: 2.5rem;
214
+ border: 0;
215
+ border-radius: 9999px;
216
+ color: var(--sp-muted);
217
+ cursor: pointer;
218
+ transition: transform .15s, color .15s;
219
+ }
220
+
221
+ .sp-gate-close svg {
222
+ width: 1.25rem;
223
+ height: 1.25rem;
224
+ }
225
+
226
+ .sp-gate-close:hover { color: var(--sp-text); }
227
+ .sp-gate-close:active { transform: scale(.9); }
228
+
229
+ @media (min-width: 64rem) {
230
+ .sp-gate-close { display: none; }
231
+ }
232
+
233
+ /* The box: a glass pill. */
234
+ .sp-gate-box {
235
+ display: flex;
236
+ flex-shrink: 0;
237
+ align-items: center;
238
+ gap: 0.75rem;
239
+ border-radius: 9999px;
240
+ padding-left: 1.25rem;
241
+ padding-right: 0.625rem;
242
+ }
243
+
244
+ .sp-gate-box:focus-within {
245
+ --sp-fill: color-mix(in srgb, var(--sp-gate-fill), #fff 40%);
246
+ }
247
+
248
+ @media (min-width: 64rem) {
249
+ .sp-gate-box { padding-right: 1.25rem; }
250
+ }
251
+
252
+ .sp-gate-icon {
253
+ width: 1.25rem;
254
+ height: 1.25rem;
255
+ flex-shrink: 0;
256
+ color: var(--sp-muted);
257
+ }
258
+
259
+ /* 1rem below 64rem: iOS Safari zooms the page into any smaller field. */
260
+ .sp-gate-input {
261
+ flex: 1;
262
+ min-width: 0;
263
+ height: 3.5rem;
264
+ border: 0;
265
+ padding: 0;
266
+ background: transparent;
267
+ color: inherit;
268
+ font: inherit;
269
+ font-size: 1rem;
270
+ outline: none;
271
+ }
272
+
273
+ .sp-gate-input::placeholder {
274
+ color: var(--sp-muted);
275
+ opacity: 1;
276
+ }
277
+
278
+ @media (min-width: 64rem) {
279
+ .sp-gate-input {
280
+ height: 4rem;
281
+ font-size: 1.125rem;
282
+ }
283
+ }
284
+
285
+ .sp-gate-send {
286
+ width: 2.25rem;
287
+ height: 2.25rem;
288
+ }
289
+
290
+ .sp-gate-hints {
291
+ display: flex;
292
+ flex-shrink: 0;
293
+ align-items: center;
294
+ color: var(--sp-muted);
295
+ font-size: 13px;
296
+ }
297
+
298
+ .sp-kbd {
299
+ display: flex;
300
+ align-items: center;
301
+ justify-content: center;
302
+ height: 1.5rem;
303
+ min-width: 1.5rem;
304
+ border-radius: 6px;
305
+ padding-inline: 0.375rem;
306
+ background: rgba(0, 0, 0, .06);
307
+ font-family: inherit;
308
+ }
309
+
310
+ .sp-kbd svg {
311
+ width: 0.875rem;
312
+ height: 0.875rem;
313
+ }
314
+
315
+ .sp-kbd-arrows { margin-right: 0.75rem; }
316
+
317
+ .sp-hint-enter {
318
+ display: flex;
319
+ align-items: center;
320
+ gap: 0.375rem;
321
+ white-space: nowrap;
322
+ }
323
+
324
+ /* A hint: always mounted, it slides open (width through a one-column grid) and fades in. */
325
+ .sp-hint {
326
+ display: grid;
327
+ grid-template-columns: 0fr;
328
+ opacity: 0;
329
+ transform: translateX(4px);
330
+ transition: grid-template-columns .25s cubic-bezier(.2, .8, .2, 1), opacity .2s, transform .25s cubic-bezier(.2, .8, .2, 1);
331
+ }
332
+
333
+ .sp-hint > * {
334
+ min-width: 0;
335
+ overflow: hidden;
336
+ }
337
+
338
+ .sp-hint[data-on] {
339
+ grid-template-columns: 1fr;
340
+ opacity: 1;
341
+ transform: none;
342
+ }
343
+
344
+ @media (prefers-reduced-motion: no-preference) {
345
+ .sp-fade-in { animation: sp-fade-in .2s both; }
346
+ }
347
+
348
+ /* The results: one glass group. */
349
+ .sp-gate-results {
350
+ flex-shrink: 0;
351
+ overflow: hidden;
352
+ border-radius: 22px;
353
+ }
354
+
355
+ .sp-gate-results > .sp-scroll {
356
+ position: relative;
357
+ max-height: 60svh;
358
+ padding: 0.375rem;
359
+ }
360
+
361
+ @media (min-width: 64rem) {
362
+ .sp-gate-results > .sp-scroll { max-height: min(60vh, 440px); }
363
+ }
364
+
365
+ .sp-gate-list {
366
+ display: flex;
367
+ flex-direction: column;
368
+ gap: 0.125rem;
369
+ }
370
+
371
+ .sp-row {
372
+ position: relative;
373
+ z-index: 1;
374
+ display: flex;
375
+ width: 100%;
376
+ align-items: center;
377
+ justify-content: space-between;
378
+ gap: 0.75rem;
379
+ border: 0;
380
+ border-radius: 16px;
381
+ padding: 0.625rem 1rem;
382
+ background: transparent;
383
+ color: var(--sp-text);
384
+ font: inherit;
385
+ font-size: 15px;
386
+ text-align: left;
387
+ text-decoration: none;
388
+ outline: none;
389
+ cursor: pointer;
390
+ transition: transform .15s, background-color .15s;
391
+ }
392
+
393
+ .sp-row:hover:not([aria-selected="true"]) {
394
+ background-color: rgba(255, 255, 255, .6);
395
+ }
396
+
397
+ .sp-row:active { transform: scale(.985); }
398
+
399
+ @media (prefers-reduced-motion: no-preference) {
400
+ .sp-row { animation: sp-rise .3s both; }
401
+ }
402
+
403
+ @keyframes sp-rise { from { opacity: 0; transform: translateY(-4px); } }
404
+
405
+ .sp-row-text {
406
+ display: flex;
407
+ min-width: 0;
408
+ flex-direction: column;
409
+ }
410
+
411
+ .sp-row-title { line-height: 1.25; }
412
+
413
+ .sp-row-sub {
414
+ margin-top: 0.125rem;
415
+ color: var(--sp-muted);
416
+ font-size: 13px;
417
+ line-height: 1.25;
418
+ }
419
+
420
+ .sp-row mark {
421
+ background: transparent;
422
+ color: var(--sp-accent);
423
+ }
424
+
425
+ .sp-row-kind {
426
+ display: flex;
427
+ flex-shrink: 0;
428
+ align-items: center;
429
+ gap: 0.5rem;
430
+ color: var(--sp-muted);
431
+ font-size: 13px;
432
+ }
433
+
434
+ .sp-row-arrow {
435
+ width: 1rem;
436
+ height: 1rem;
437
+ flex-shrink: 0;
438
+ color: var(--sp-subtle);
439
+ transition: color .15s;
440
+ }
441
+
442
+ .sp-row:hover .sp-row-arrow { color: var(--sp-text); }
443
+ .sp-row[aria-selected="true"] .sp-row-arrow { color: var(--sp-ink); }
444
+
445
+ /* The arrow-key highlight: one piece of the accent glass sliding under the rows. */
446
+ .sp-hl {
447
+ --sp-shadow: 0 0 #0000;
448
+ position: absolute;
449
+ top: 0;
450
+ inset-inline: 6px;
451
+ z-index: 0;
452
+ border-radius: 16px;
453
+ pointer-events: none;
454
+ opacity: 0;
455
+ -webkit-backdrop-filter: none;
456
+ backdrop-filter: none;
457
+ transition: transform .22s cubic-bezier(.2, .8, .2, 1), height .22s cubic-bezier(.2, .8, .2, 1), opacity .15s;
458
+ }
459
+
460
+ .sp-hl[data-on] { opacity: 1; }
461
+ .sp-hl[data-jump] { transition: opacity .15s; }
462
+
463
+ /* ------------------------------------------------------------------ */
464
+ /* The panel */
465
+ /* ------------------------------------------------------------------ */
466
+ .sp-backdrop {
467
+ position: fixed;
468
+ inset: 0;
469
+ z-index: calc(var(--sp-z) - 2);
470
+ background: rgba(0, 0, 0, .3);
471
+ }
472
+
473
+ @media (prefers-reduced-motion: no-preference) {
474
+ .sp-backdrop[data-state="open"] { animation: sp-fade-in .3s both; }
475
+ .sp-backdrop[data-state="closed"] { animation: sp-fade-out .3s both; }
476
+ }
477
+
478
+ .sp-backdrop[data-state="closed"] { pointer-events: none; }
479
+
480
+ /* Phones: a bottom sheet. Desktop: a floating card, bottom right. */
481
+ .sp-panel {
482
+ --sp-top: 3.5rem;
483
+ --sp-bottom: 7rem;
484
+ --sp-pill: 52px;
485
+ position: fixed;
486
+ inset-inline: 0;
487
+ bottom: 0;
488
+ z-index: calc(var(--sp-z) - 1);
489
+ display: flex;
490
+ flex-direction: column;
491
+ height: 92svh;
492
+ overflow: hidden;
493
+ border-top: 1px solid rgba(0, 0, 0, .1);
494
+ border-radius: 20px 20px 0 0;
495
+ background: var(--sp-surface);
496
+ box-shadow: 0 -8px 32px rgba(0, 0, 0, .12);
497
+ color: var(--sp-text);
498
+ font-family: var(--sp-font);
499
+ outline: none;
500
+ }
501
+
502
+ @media (min-width: 64rem) {
503
+ .sp-panel {
504
+ --sp-pill: 40px;
505
+ inset-inline: auto;
506
+ right: 1.5rem;
507
+ bottom: 1.5rem;
508
+ width: 420px;
509
+ height: min(640px, calc(100dvh - 48px));
510
+ border: 1px solid rgba(0, 0, 0, .15);
511
+ border-radius: 24px;
512
+ box-shadow: 0 2px 4px rgba(0, 0, 0, .06), 0 8px 24px rgba(0, 0, 0, .08), 0 24px 64px rgba(0, 0, 0, .10);
513
+ }
514
+ }
515
+
516
+ @media (prefers-reduced-motion: no-preference) {
517
+ .sp-panel[data-state="open"] { animation: sp-sheet-in .3s both; }
518
+ .sp-panel[data-state="closed"] { animation: sp-sheet-out .3s both; }
519
+
520
+ @media (min-width: 64rem) {
521
+ .sp-panel[data-state="open"] { animation: sp-card-in .2s both; }
522
+ .sp-panel[data-state="closed"] { animation: sp-card-out .2s both; }
523
+ }
524
+ }
525
+
526
+ @keyframes sp-sheet-in { from { transform: translateY(100%); } }
527
+ @keyframes sp-sheet-out { to { transform: translateY(100%); } }
528
+ @keyframes sp-card-in { from { opacity: 0; transform: translateY(16px); } }
529
+ @keyframes sp-card-out { to { opacity: 0; transform: translateY(16px); } }
530
+
531
+ /* The header: a glass pill over the conversation; behind it white down to the pill's middle,
532
+ fading out by its bottom edge, so messages show blurred only through its lower half. */
533
+ .sp-panel-head {
534
+ position: absolute;
535
+ inset-inline: 0;
536
+ top: 0;
537
+ z-index: 10;
538
+ padding: 0.375rem 0.75rem 0.25rem;
539
+ touch-action: none;
540
+ }
541
+
542
+ .sp-panel-head::before,
543
+ .sp-panel-foot::before {
544
+ content: "";
545
+ position: absolute;
546
+ inset: 0;
547
+ z-index: -1;
548
+ pointer-events: none;
549
+ }
550
+
551
+ .sp-panel-head::before {
552
+ background: linear-gradient(to bottom, var(--sp-surface) calc(100% - 0.25rem - var(--sp-pill) / 2), transparent calc(100% - 0.25rem));
553
+ }
554
+
555
+ @media (min-width: 64rem) {
556
+ .sp-panel-head {
557
+ padding-top: 0.5rem;
558
+ touch-action: auto;
559
+ }
560
+ }
561
+
562
+ .sp-grab {
563
+ width: 2.25rem;
564
+ height: 0.25rem;
565
+ margin: 0 auto 6px;
566
+ border-radius: 9999px;
567
+ background: rgba(0, 0, 0, .15);
568
+ }
569
+
570
+ @media (min-width: 64rem) {
571
+ .sp-grab { display: none; }
572
+ }
573
+
574
+ .sp-panel-pill {
575
+ --sp-fill: var(--sp-pill-fill);
576
+ display: flex;
577
+ align-items: center;
578
+ gap: 0.125rem;
579
+ border-radius: 9999px;
580
+ padding: 0.25rem 0.25rem 0.25rem 1rem;
581
+ }
582
+
583
+ .sp-panel-title {
584
+ display: flex;
585
+ flex: 1;
586
+ min-width: 0;
587
+ align-items: center;
588
+ gap: 0.5rem;
589
+ margin: 0;
590
+ font-family: var(--sp-font-display);
591
+ font-size: 1.125rem;
592
+ font-weight: var(--sp-display-weight);
593
+ line-height: 1.3;
594
+ }
595
+
596
+ .sp-logo {
597
+ display: inline-flex;
598
+ width: 1.125rem;
599
+ height: 1.125rem;
600
+ flex-shrink: 0;
601
+ }
602
+
603
+ .sp-logo > * {
604
+ width: 100%;
605
+ height: 100%;
606
+ }
607
+
608
+ .sp-icon-button {
609
+ display: grid;
610
+ place-items: center;
611
+ width: 2.75rem;
612
+ height: 2.75rem;
613
+ border: 0;
614
+ border-radius: 9999px;
615
+ padding: 0;
616
+ background: transparent;
617
+ color: var(--sp-muted);
618
+ cursor: pointer;
619
+ transition: background-color .15s, color .15s;
620
+ }
621
+
622
+ .sp-icon-button svg {
623
+ width: 18px;
624
+ height: 18px;
625
+ }
626
+
627
+ .sp-icon-button:hover {
628
+ background: rgba(0, 0, 0, .05);
629
+ color: var(--sp-text);
630
+ }
631
+
632
+ .sp-back { margin-left: -12px; }
633
+
634
+ @media (min-width: 64rem) {
635
+ .sp-icon-button {
636
+ width: 2rem;
637
+ height: 2rem;
638
+ }
639
+ }
640
+
641
+ /* The conversation. */
642
+ .sp-messages {
643
+ position: relative;
644
+ flex: 1;
645
+ min-height: 0;
646
+ overflow-y: hidden;
647
+ }
648
+
649
+ .sp-messages-viewport {
650
+ height: 100%;
651
+ }
652
+
653
+ .sp-messages-content {
654
+ display: flex;
655
+ flex-direction: column;
656
+ gap: 1rem;
657
+ padding: calc(var(--sp-top) + 0.5rem) 1rem calc(var(--sp-bottom) + 1rem);
658
+ }
659
+
660
+ .sp-privacy {
661
+ font-size: 12px;
662
+ font-weight: 500;
663
+ line-height: 1.375;
664
+ color: var(--sp-ink);
665
+ }
666
+
667
+ .sp-privacy a {
668
+ color: inherit;
669
+ text-decoration: underline;
670
+ text-underline-offset: 0.125rem;
671
+ }
672
+
673
+ .sp-message {
674
+ display: flex;
675
+ flex-direction: column;
676
+ gap: 0.5rem;
677
+ width: 100%;
678
+ }
679
+
680
+ .sp-message[data-role="user"] {
681
+ max-width: 85%;
682
+ margin-left: auto;
683
+ align-items: flex-end;
684
+ }
685
+
686
+ .sp-message-gap { margin-top: 0.75rem; }
687
+
688
+ .sp-bubble,
689
+ .sp-answer {
690
+ margin: 0;
691
+ font-size: 15px;
692
+ line-height: 1.625;
693
+ letter-spacing: .15px;
694
+ }
695
+
696
+ .sp-bubble {
697
+ border-radius: 18px;
698
+ padding: 0.5rem 0.875rem;
699
+ background: var(--sp-bubble);
700
+ white-space: pre-wrap;
701
+ overflow-wrap: anywhere;
702
+ }
703
+
704
+ .sp-answer { overflow-wrap: anywhere; }
705
+
706
+ .sp-md > :first-child { margin-top: 0; }
707
+ .sp-md > :last-child { margin-bottom: 0; }
708
+ .sp-md p { margin: 0.5rem 0; }
709
+ .sp-md ul { margin: 0.5rem 0; padding-left: 1.25rem; list-style: disc; }
710
+ .sp-md ol { margin: 0.5rem 0; padding-left: 1.25rem; list-style: decimal; }
711
+ .sp-md li { margin: 0.125rem 0; }
712
+ .sp-md strong { font-weight: 600; }
713
+ .sp-md a { color: inherit; text-decoration: underline; text-underline-offset: 0.125rem; }
714
+ .sp-md code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .9em; }
715
+
716
+ .sp-sources {
717
+ color: var(--sp-subtle);
718
+ font-size: 13px;
719
+ }
720
+
721
+ .sp-sources summary {
722
+ display: flex;
723
+ width: fit-content;
724
+ align-items: center;
725
+ gap: 0.375rem;
726
+ font-weight: 500;
727
+ list-style: none;
728
+ cursor: pointer;
729
+ }
730
+
731
+ .sp-sources summary::-webkit-details-marker { display: none; }
732
+
733
+ .sp-chevron {
734
+ width: 0.875rem;
735
+ height: 0.875rem;
736
+ transition: transform .15s;
737
+ }
738
+
739
+ .sp-sources[open] .sp-chevron { transform: rotate(180deg); }
740
+
741
+ .sp-sources-list {
742
+ display: flex;
743
+ flex-direction: column;
744
+ gap: 0.375rem;
745
+ margin-top: 0.5rem;
746
+ }
747
+
748
+ .sp-sources-list a {
749
+ display: flex;
750
+ align-items: center;
751
+ gap: 0.5rem;
752
+ color: var(--sp-muted);
753
+ text-decoration: none;
754
+ text-underline-offset: 0.125rem;
755
+ }
756
+
757
+ .sp-sources-list a:hover { text-decoration: underline; }
758
+
759
+ .sp-sources-list svg {
760
+ width: 0.875rem;
761
+ height: 0.875rem;
762
+ flex-shrink: 0;
763
+ }
764
+
765
+ /* The row under an answer that reopens the view it prepared. */
766
+ .sp-action-row {
767
+ display: flex;
768
+ width: 100%;
769
+ align-items: center;
770
+ justify-content: space-between;
771
+ gap: 0.75rem;
772
+ margin-top: 0.25rem;
773
+ border: 1px solid rgba(0, 0, 0, .1);
774
+ border-radius: 12px;
775
+ padding: 0.625rem 0.875rem;
776
+ background: transparent;
777
+ color: var(--sp-muted);
778
+ font: inherit;
779
+ font-size: 14px;
780
+ text-align: left;
781
+ cursor: pointer;
782
+ transition: border-color .15s, background-color .15s, color .15s;
783
+ }
784
+
785
+ .sp-action-row svg {
786
+ width: 1rem;
787
+ height: 1rem;
788
+ flex-shrink: 0;
789
+ color: var(--sp-subtle);
790
+ }
791
+
792
+ .sp-action-row:hover {
793
+ border-color: rgba(0, 0, 0, .2);
794
+ background: var(--sp-bubble);
795
+ color: var(--sp-text);
796
+ }
797
+
798
+ .sp-waiting {
799
+ display: flex;
800
+ align-items: center;
801
+ gap: 0.625rem;
802
+ color: var(--sp-subtle);
803
+ font-size: 14px;
804
+ }
805
+
806
+ .sp-waiting .sp-logo {
807
+ width: 1rem;
808
+ height: 1rem;
809
+ }
810
+
811
+ @media (prefers-reduced-motion: no-preference) {
812
+ .sp-pulse { animation: sp-pulse 2s cubic-bezier(.4, 0, .6, 1) infinite; }
813
+ }
814
+
815
+ @keyframes sp-pulse { 50% { opacity: .5; } }
816
+
817
+ .sp-error {
818
+ margin: 0;
819
+ border-radius: 12px;
820
+ padding: 0.625rem 0.875rem;
821
+ background: var(--sp-quiet);
822
+ color: var(--sp-muted);
823
+ font-size: 14px;
824
+ }
825
+
826
+ .sp-scroll-button {
827
+ position: absolute;
828
+ left: 50%;
829
+ bottom: calc(var(--sp-bottom) + 0.75rem);
830
+ display: grid;
831
+ place-items: center;
832
+ width: 2rem;
833
+ height: 2rem;
834
+ transform: translateX(-50%);
835
+ border: 1px solid rgba(0, 0, 0, .15);
836
+ border-radius: 9999px;
837
+ padding: 0;
838
+ background: #fff;
839
+ box-shadow: 0 2px 8px rgba(0, 0, 0, .08);
840
+ color: var(--sp-text);
841
+ cursor: pointer;
842
+ }
843
+
844
+ .sp-scroll-button svg {
845
+ width: 1rem;
846
+ height: 1rem;
847
+ }
848
+
849
+ /* The composer: one glass box at the bottom, over a white gradient (and the tide). */
850
+ .sp-panel-foot {
851
+ position: absolute;
852
+ inset-inline: 0;
853
+ bottom: 0;
854
+ z-index: 10;
855
+ margin: 0;
856
+ padding: 0.25rem 0.75rem max(0.75rem, env(safe-area-inset-bottom));
857
+ }
858
+
859
+ .sp-panel-foot::before {
860
+ top: -24px;
861
+ background: linear-gradient(to top, var(--sp-surface) calc(100% - 2.5rem), transparent);
862
+ }
863
+
864
+ .sp-panel-foot > .sp-textarea,
865
+ .sp-panel-foot > .sp-composer-row {
866
+ position: relative;
867
+ }
868
+
869
+ /* The textarea and the row under it share one glass box, drawn behind both. */
870
+ .sp-panel-foot::after {
871
+ content: "";
872
+ position: absolute;
873
+ inset: 0.25rem 0.75rem max(0.75rem, env(safe-area-inset-bottom));
874
+ z-index: 0;
875
+ border-radius: 20px;
876
+ pointer-events: none;
877
+ background: linear-gradient(to bottom, rgba(255, 255, 255, .35), rgba(255, 255, 255, 0) 55%), var(--sp-glass-fill);
878
+ -webkit-backdrop-filter: blur(16px) saturate(2);
879
+ backdrop-filter: blur(16px) saturate(2);
880
+ box-shadow: inset 0 0 0 1px var(--sp-glass-edge),
881
+ inset 0 0.09375rem 0 #fff,
882
+ inset 0 -1px 0 color-mix(in srgb, var(--sp-ink) 8%, transparent),
883
+ inset 0 0 0.875rem var(--sp-glow),
884
+ var(--sp-lift);
885
+ }
886
+
887
+ .sp-panel-foot:focus-within::after {
888
+ background: linear-gradient(to bottom, rgba(255, 255, 255, .35), rgba(255, 255, 255, 0) 55%), rgba(255, 255, 255, .7);
889
+ }
890
+
891
+ .sp-textarea {
892
+ z-index: 1;
893
+ display: block;
894
+ width: 100%;
895
+ min-height: 2.75rem;
896
+ max-height: 10rem;
897
+ field-sizing: content;
898
+ resize: none;
899
+ border: 0;
900
+ padding: 0.75rem 1rem 0.25rem;
901
+ background: transparent;
902
+ color: inherit;
903
+ font: inherit;
904
+ font-size: 1rem;
905
+ outline: none;
906
+ }
907
+
908
+ .sp-textarea::placeholder {
909
+ color: var(--sp-subtle);
910
+ opacity: 1;
911
+ }
912
+
913
+ @media (min-width: 64rem) {
914
+ .sp-textarea { font-size: 0.9375rem; }
915
+ }
916
+
917
+ .sp-composer-row {
918
+ z-index: 1;
919
+ display: flex;
920
+ align-items: center;
921
+ justify-content: space-between;
922
+ gap: 0.5rem;
923
+ padding: 0.5rem 0.5rem 0.5rem 1rem;
924
+ }
925
+
926
+ .sp-submit {
927
+ width: 2rem;
928
+ height: 2rem;
929
+ }
930
+
931
+ .sp-stop-icon {
932
+ width: 0.75rem !important;
933
+ height: 0.75rem !important;
934
+ fill: currentColor;
935
+ }
936
+
937
+ /* The tide: soft pools of the accent rising from the panel's bottom edge behind the composer,
938
+ drifting sideways and flattening out of step (`transform` only, still under reduced motion). */
939
+ .sp-tide {
940
+ position: absolute;
941
+ inset: -24px 0 0;
942
+ z-index: -1;
943
+ overflow: hidden;
944
+ pointer-events: none;
945
+ background: linear-gradient(to top, color-mix(in srgb, var(--sp-accent) 14%, transparent), transparent 70%);
946
+ }
947
+
948
+ .sp-tide > span {
949
+ position: absolute;
950
+ left: -30%;
951
+ bottom: -80px;
952
+ width: 120%;
953
+ height: 180px;
954
+ border-radius: 50%;
955
+ background: radial-gradient(closest-side, color-mix(in srgb, var(--sp-accent) 26%, transparent), transparent);
956
+ transform-origin: 50% 100%;
957
+ will-change: transform;
958
+ }
959
+
960
+ .sp-tide > span:nth-child(2) {
961
+ left: auto;
962
+ right: -30%;
963
+ }
964
+
965
+ .sp-tide > span:nth-child(3) {
966
+ left: 15%;
967
+ width: 70%;
968
+ height: 150px;
969
+ bottom: -55px;
970
+ background: radial-gradient(closest-side, color-mix(in srgb, var(--sp-accent) 20%, transparent), transparent);
971
+ }
972
+
973
+ @media (prefers-reduced-motion: no-preference) {
974
+ .sp-tide > span { animation: sp-swell-a 7s ease-in-out infinite; }
975
+ .sp-tide > span:nth-child(2) { animation-name: sp-swell-b; animation-duration: 9s; }
976
+ .sp-tide > span:nth-child(3) { animation-name: sp-swell-c; animation-duration: 5.5s; }
977
+ }
978
+
979
+ @keyframes sp-swell-a {
980
+ 0%, 100% { transform: translate3d(0, 0, 0) scale(1, .75); }
981
+ 30% { transform: translate3d(18%, 0, 0) scale(1.15, 1); }
982
+ 60% { transform: translate3d(32%, 0, 0) scale(1, .8); }
983
+ 80% { transform: translate3d(12%, 0, 0) scale(1.1, .95); }
984
+ }
985
+
986
+ @keyframes sp-swell-b {
987
+ 0%, 100% { transform: translate3d(0, 0, 0) scale(1.1, 1); }
988
+ 25% { transform: translate3d(-14%, 0, 0) scale(.95, .75); }
989
+ 55% { transform: translate3d(-34%, 0, 0) scale(1.15, 1); }
990
+ 80% { transform: translate3d(-18%, 0, 0) scale(1, .8); }
991
+ }
992
+
993
+ @keyframes sp-swell-c {
994
+ 0%, 100% { transform: translate3d(-30%, 0, 0) scale(.9, .75); }
995
+ 50% { transform: translate3d(35%, 0, 0) scale(1.2, 1); }
996
+ }
997
+
998
+ /* An action's view (a form) fills the panel under the header. */
999
+ .sp-view {
1000
+ display: flex;
1001
+ flex: 1;
1002
+ min-height: 0;
1003
+ flex-direction: column;
1004
+ overflow-y: auto;
1005
+ overscroll-behavior: contain;
1006
+ padding-top: var(--sp-top);
1007
+ }