@livelayer/react 0.1.0 → 0.2.0

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.
@@ -0,0 +1,789 @@
1
+ /* ─────────────────────────────────────────────────────────────────────
2
+ * @livelayer/react — widget stylesheet
3
+ *
4
+ * Scoped under .ll-widget so CSS custom properties don't pollute :root.
5
+ * No Tailwind, no CSS-in-JS runtime. Just vanilla CSS. Import once in
6
+ * your app:
7
+ *
8
+ * import "@livelayer/react/styles.css";
9
+ *
10
+ * Consumer-overridable tokens (set via `branding` prop → inline vars on
11
+ * .ll-widget, or in your own CSS targeting .ll-widget):
12
+ * --ll-color-primary CTA / focus color (default: #0D0D0D)
13
+ * --ll-color-accent Highlight / waveform (default: #8B7355)
14
+ * --ll-color-bg Widget background (default: #FFFFFF)
15
+ * --ll-color-fg Primary text (default: #0D0D0D)
16
+ * --ll-color-muted Secondary text (default: #808080)
17
+ * --ll-color-border Border / divider (default: #E6E6E6)
18
+ * --ll-color-danger Destructive / End conversation (default: #EF4444)
19
+ * --ll-color-listening Listening dot (default: #22C55E)
20
+ * --ll-color-thinking Thinking dot (default: #F59E0B)
21
+ * --ll-color-speaking Speaking dot (default: #22C55E)
22
+ * --ll-color-idle Idle dot (default: #808080)
23
+ * ───────────────────────────────────────────────────────────────────── */
24
+
25
+ .ll-widget {
26
+ --ll-color-primary: #0d0d0d;
27
+ --ll-color-accent: #8b7355;
28
+ --ll-color-bg: #ffffff;
29
+ --ll-color-fg: #0d0d0d;
30
+ --ll-color-muted: #808080;
31
+ --ll-color-border: #e6e6e6;
32
+ --ll-color-border-subtle: #f1f1f1;
33
+ --ll-color-danger: #ef4444;
34
+ --ll-color-listening: #22c55e;
35
+ --ll-color-thinking: #f59e0b;
36
+ --ll-color-speaking: #22c55e;
37
+ --ll-color-idle: #808080;
38
+ --ll-color-chrome-bg: rgba(13, 13, 13, 0.85);
39
+ --ll-color-chrome-fg: rgba(255, 255, 255, 0.95);
40
+ --ll-color-chrome-fg-muted: rgba(255, 255, 255, 0.6);
41
+
42
+ --ll-radius-panel: 20px;
43
+ --ll-radius-card: 12px;
44
+ --ll-radius-pill: 999px;
45
+
46
+ --ll-font: -apple-system, BlinkMacSystemFont, "Segoe UI", "Geist", Roboto,
47
+ "Helvetica Neue", Arial, sans-serif;
48
+ --ll-font-mono: "Geist Mono", ui-monospace, "SF Mono", Menlo, Consolas,
49
+ monospace;
50
+
51
+ --ll-shadow-card: 0 10px 40px rgba(0, 0, 0, 0.12),
52
+ 0 2px 8px rgba(0, 0, 0, 0.06);
53
+ --ll-shadow-pill: 0 4px 12px rgba(0, 0, 0, 0.15);
54
+
55
+ --ll-transition-fast: 150ms ease;
56
+ --ll-transition-base: 200ms ease;
57
+
58
+ font-family: var(--ll-font);
59
+ color: var(--ll-color-fg);
60
+ box-sizing: border-box;
61
+ }
62
+
63
+ .ll-widget *,
64
+ .ll-widget *::before,
65
+ .ll-widget *::after {
66
+ box-sizing: border-box;
67
+ }
68
+
69
+ .ll-widget button {
70
+ font-family: inherit;
71
+ border: none;
72
+ background: none;
73
+ cursor: pointer;
74
+ padding: 0;
75
+ color: inherit;
76
+ }
77
+
78
+ .ll-widget button:disabled {
79
+ cursor: not-allowed;
80
+ }
81
+
82
+ /* Fixed root positioning. Safe-area insets honored. */
83
+ .ll-widget {
84
+ position: fixed;
85
+ pointer-events: none; /* children opt back in */
86
+ }
87
+
88
+ .ll-widget > * {
89
+ pointer-events: auto;
90
+ }
91
+
92
+ .ll-widget[data-position="bottom-right"] {
93
+ right: max(16px, env(safe-area-inset-right));
94
+ bottom: max(16px, env(safe-area-inset-bottom));
95
+ }
96
+ .ll-widget[data-position="bottom-left"] {
97
+ left: max(16px, env(safe-area-inset-left));
98
+ bottom: max(16px, env(safe-area-inset-bottom));
99
+ }
100
+ .ll-widget[data-position="top-right"] {
101
+ right: max(16px, env(safe-area-inset-right));
102
+ top: max(16px, env(safe-area-inset-top));
103
+ }
104
+ .ll-widget[data-position="top-left"] {
105
+ left: max(16px, env(safe-area-inset-left));
106
+ top: max(16px, env(safe-area-inset-top));
107
+ }
108
+ /* "custom" position relies on consumer-supplied style. */
109
+
110
+ /* Mobile: minimized + expanded ignore position, always full-width bottom. */
111
+ .ll-widget--mobile.ll-widget--minimized,
112
+ .ll-widget--mobile.ll-widget--expanded {
113
+ left: 0;
114
+ right: 0;
115
+ bottom: 0;
116
+ top: auto;
117
+ }
118
+
119
+ /* ── Hidden mode: edge chevron tab ────────────────────────────────── */
120
+
121
+ .ll-hidden {
122
+ display: flex;
123
+ align-items: center;
124
+ justify-content: center;
125
+ width: 28px;
126
+ height: 56px;
127
+ background: var(--ll-color-chrome-bg);
128
+ color: var(--ll-color-chrome-fg);
129
+ border-radius: 12px 0 0 12px;
130
+ box-shadow: var(--ll-shadow-pill);
131
+ transition: transform var(--ll-transition-base), width var(--ll-transition-base);
132
+ /* Re-anchor to edge (ignore .ll-widget's 16px corner inset) */
133
+ position: fixed;
134
+ top: 50%;
135
+ transform: translateY(-50%);
136
+ }
137
+
138
+ .ll-hidden:hover {
139
+ width: 32px;
140
+ }
141
+
142
+ .ll-hidden--right {
143
+ right: 0;
144
+ border-radius: 12px 0 0 12px;
145
+ }
146
+ .ll-hidden--left {
147
+ left: 0;
148
+ border-radius: 0 12px 12px 0;
149
+ }
150
+
151
+ .ll-hidden--mobile {
152
+ width: 32px;
153
+ height: 64px;
154
+ }
155
+
156
+ .ll-hidden__chevron {
157
+ width: 14px;
158
+ height: 14px;
159
+ }
160
+
161
+ .ll-hidden--speaking {
162
+ animation: ll-pulse 1.5s ease-in-out infinite;
163
+ }
164
+
165
+ @keyframes ll-pulse {
166
+ 0%, 100% {
167
+ box-shadow: var(--ll-shadow-pill);
168
+ }
169
+ 50% {
170
+ box-shadow: var(--ll-shadow-pill),
171
+ 0 0 0 6px rgba(34, 197, 94, 0.2);
172
+ }
173
+ }
174
+
175
+ /* ── Minimized mode ────────────────────────────────────────────── */
176
+
177
+ /* Desktop pill */
178
+ .ll-minimized--desktop .ll-minimized__surface {
179
+ display: flex;
180
+ align-items: center;
181
+ gap: 12px;
182
+ padding: 8px 12px;
183
+ background: var(--ll-color-chrome-bg);
184
+ color: var(--ll-color-chrome-fg);
185
+ border-radius: var(--ll-radius-pill);
186
+ box-shadow: var(--ll-shadow-pill);
187
+ min-width: 240px;
188
+ max-width: 320px;
189
+ }
190
+
191
+ .ll-minimized__avatar {
192
+ width: 40px;
193
+ height: 40px;
194
+ border-radius: 50%;
195
+ object-fit: cover;
196
+ flex-shrink: 0;
197
+ background: linear-gradient(180deg, #e8e4df 0%, #d4cfc8 100%);
198
+ }
199
+
200
+ .ll-minimized__avatar--placeholder {
201
+ /* avatar is null; show the gradient */
202
+ }
203
+
204
+ .ll-minimized__meta {
205
+ flex: 1;
206
+ min-width: 0;
207
+ display: flex;
208
+ flex-direction: column;
209
+ gap: 2px;
210
+ }
211
+
212
+ .ll-minimized__name {
213
+ font-size: 13px;
214
+ font-weight: 500;
215
+ color: var(--ll-color-chrome-fg);
216
+ white-space: nowrap;
217
+ overflow: hidden;
218
+ text-overflow: ellipsis;
219
+ }
220
+
221
+ .ll-minimized__state {
222
+ font-size: 11px;
223
+ color: var(--ll-color-chrome-fg-muted);
224
+ }
225
+
226
+ .ll-minimized__controls {
227
+ display: flex;
228
+ gap: 4px;
229
+ align-items: center;
230
+ }
231
+
232
+ .ll-minimized__btn {
233
+ width: 32px;
234
+ height: 32px;
235
+ border-radius: 50%;
236
+ display: flex;
237
+ align-items: center;
238
+ justify-content: center;
239
+ transition: background var(--ll-transition-fast);
240
+ color: var(--ll-color-chrome-fg);
241
+ }
242
+
243
+ .ll-minimized__btn:hover {
244
+ background: rgba(255, 255, 255, 0.1);
245
+ }
246
+
247
+ .ll-minimized__btn--close {
248
+ color: var(--ll-color-chrome-fg-muted);
249
+ }
250
+
251
+ .ll-minimized__icon {
252
+ width: 16px;
253
+ height: 16px;
254
+ }
255
+
256
+ /* Mobile dock */
257
+ .ll-minimized--mobile {
258
+ padding-bottom: env(safe-area-inset-bottom);
259
+ }
260
+
261
+ .ll-minimized--mobile .ll-minimized__surface {
262
+ display: flex;
263
+ align-items: center;
264
+ gap: 12px;
265
+ padding: 12px 16px;
266
+ background: var(--ll-color-chrome-bg);
267
+ color: var(--ll-color-chrome-fg);
268
+ width: 100%;
269
+ text-align: left;
270
+ }
271
+
272
+ .ll-minimized--mobile .ll-minimized__avatar {
273
+ width: 48px;
274
+ height: 48px;
275
+ }
276
+
277
+ .ll-minimized--mobile .ll-minimized__waveform {
278
+ flex: 1;
279
+ min-width: 0;
280
+ }
281
+
282
+ .ll-minimized--mobile .ll-minimized__name {
283
+ font-size: 14px;
284
+ }
285
+
286
+ .ll-minimized__icon--expand {
287
+ width: 20px;
288
+ height: 20px;
289
+ }
290
+
291
+ /* ── Waveform ─────────────────────────────────────────────────── */
292
+
293
+ .ll-waveform {
294
+ display: flex;
295
+ align-items: center;
296
+ gap: 2px;
297
+ height: 24px;
298
+ }
299
+
300
+ .ll-waveform__bar {
301
+ width: 3px;
302
+ min-height: 4px;
303
+ border-radius: 2px;
304
+ background: rgba(255, 255, 255, 0.7);
305
+ transition: height 75ms linear;
306
+ }
307
+
308
+ /* ── Agent state pill ─────────────────────────────────────────── */
309
+
310
+ .ll-pill {
311
+ display: inline-flex;
312
+ align-items: center;
313
+ gap: 6px;
314
+ padding: 4px 10px;
315
+ border-radius: var(--ll-radius-pill);
316
+ background: var(--ll-color-chrome-bg);
317
+ backdrop-filter: blur(8px);
318
+ -webkit-backdrop-filter: blur(8px);
319
+ color: var(--ll-color-chrome-fg);
320
+ font-size: 11px;
321
+ font-weight: 500;
322
+ line-height: 1;
323
+ }
324
+
325
+ .ll-pill__dot {
326
+ width: 8px;
327
+ height: 8px;
328
+ border-radius: 50%;
329
+ background: var(--ll-color-idle);
330
+ flex-shrink: 0;
331
+ }
332
+
333
+ .ll-pill--idle .ll-pill__dot {
334
+ background: var(--ll-color-idle);
335
+ }
336
+ .ll-pill--listening .ll-pill__dot {
337
+ background: var(--ll-color-listening);
338
+ }
339
+ .ll-pill--thinking .ll-pill__dot {
340
+ background: var(--ll-color-thinking);
341
+ animation: ll-dot-pulse 1s ease-in-out infinite;
342
+ }
343
+ .ll-pill--speaking .ll-pill__dot {
344
+ background: var(--ll-color-speaking);
345
+ animation: ll-dot-pulse 1.2s ease-in-out infinite;
346
+ }
347
+
348
+ @keyframes ll-dot-pulse {
349
+ 0%, 100% { opacity: 1; }
350
+ 50% { opacity: 0.5; }
351
+ }
352
+
353
+ /* ── Expanded mode ────────────────────────────────────────────── */
354
+
355
+ .ll-expanded {
356
+ display: flex;
357
+ flex-direction: column;
358
+ background: var(--ll-color-bg);
359
+ border-radius: var(--ll-radius-panel);
360
+ box-shadow: var(--ll-shadow-card);
361
+ overflow: hidden;
362
+ }
363
+
364
+ .ll-expanded--desktop {
365
+ width: 400px;
366
+ height: 600px;
367
+ max-height: calc(100vh - 48px);
368
+ }
369
+
370
+ .ll-expanded--mobile {
371
+ width: 100vw;
372
+ height: 80dvh;
373
+ border-radius: 20px 20px 0 0;
374
+ padding-bottom: env(safe-area-inset-bottom);
375
+ animation: ll-slide-up 250ms ease;
376
+ }
377
+
378
+ @keyframes ll-slide-up {
379
+ from {
380
+ transform: translateY(100%);
381
+ }
382
+ to {
383
+ transform: translateY(0);
384
+ }
385
+ }
386
+
387
+ /* Header */
388
+ .ll-expanded__header {
389
+ display: flex;
390
+ align-items: center;
391
+ justify-content: space-between;
392
+ padding: 12px 16px;
393
+ border-bottom: 1px solid var(--ll-color-border-subtle);
394
+ flex-shrink: 0;
395
+ }
396
+
397
+ .ll-expanded__header-left,
398
+ .ll-expanded__header-right {
399
+ display: flex;
400
+ align-items: center;
401
+ gap: 8px;
402
+ }
403
+
404
+ .ll-expanded__logo {
405
+ width: 24px;
406
+ height: 24px;
407
+ border-radius: 4px;
408
+ object-fit: cover;
409
+ }
410
+
411
+ .ll-expanded__name {
412
+ font-size: 14px;
413
+ font-weight: 600;
414
+ color: var(--ll-color-fg);
415
+ }
416
+
417
+ .ll-expanded__team-trigger {
418
+ display: flex;
419
+ align-items: center;
420
+ gap: 4px;
421
+ font-size: 14px;
422
+ font-weight: 600;
423
+ color: var(--ll-color-fg);
424
+ padding: 4px 8px;
425
+ border-radius: var(--ll-radius-pill);
426
+ transition: background var(--ll-transition-fast);
427
+ }
428
+ .ll-expanded__team-trigger:hover {
429
+ background: var(--ll-color-border-subtle);
430
+ }
431
+
432
+ .ll-expanded__team-chevron {
433
+ width: 14px;
434
+ height: 14px;
435
+ transition: transform var(--ll-transition-base);
436
+ }
437
+ .ll-expanded__team-chevron--open {
438
+ transform: rotate(180deg);
439
+ }
440
+
441
+ .ll-expanded__icon-btn {
442
+ width: 32px;
443
+ height: 32px;
444
+ border-radius: 50%;
445
+ display: flex;
446
+ align-items: center;
447
+ justify-content: center;
448
+ color: var(--ll-color-fg);
449
+ transition: background var(--ll-transition-fast);
450
+ }
451
+ .ll-expanded__icon-btn:hover {
452
+ background: var(--ll-color-border-subtle);
453
+ }
454
+
455
+ .ll-expanded__icon {
456
+ width: 18px;
457
+ height: 18px;
458
+ }
459
+
460
+ /* Team menu dropdown */
461
+ .ll-expanded__team-menu {
462
+ position: absolute;
463
+ top: 56px;
464
+ left: 16px;
465
+ right: 16px;
466
+ max-width: 280px;
467
+ background: var(--ll-color-bg);
468
+ border: 1px solid var(--ll-color-border);
469
+ border-radius: var(--ll-radius-card);
470
+ box-shadow: var(--ll-shadow-card);
471
+ overflow: hidden;
472
+ z-index: 10;
473
+ }
474
+
475
+ .ll-expanded__team-item {
476
+ display: flex;
477
+ align-items: center;
478
+ gap: 12px;
479
+ padding: 10px 12px;
480
+ width: 100%;
481
+ text-align: left;
482
+ transition: background var(--ll-transition-fast);
483
+ }
484
+ .ll-expanded__team-item:hover {
485
+ background: var(--ll-color-border-subtle);
486
+ }
487
+ .ll-expanded__team-item--active {
488
+ background: var(--ll-color-border-subtle);
489
+ }
490
+
491
+ .ll-expanded__team-item-avatar {
492
+ width: 36px;
493
+ height: 36px;
494
+ border-radius: 50%;
495
+ object-fit: cover;
496
+ flex-shrink: 0;
497
+ }
498
+
499
+ .ll-expanded__team-item-meta {
500
+ display: flex;
501
+ flex-direction: column;
502
+ gap: 2px;
503
+ min-width: 0;
504
+ }
505
+
506
+ .ll-expanded__team-item-name {
507
+ font-size: 13px;
508
+ font-weight: 500;
509
+ color: var(--ll-color-fg);
510
+ }
511
+ .ll-expanded__team-item-role {
512
+ font-size: 11px;
513
+ color: var(--ll-color-muted);
514
+ }
515
+
516
+ /* Avatar surface */
517
+ .ll-expanded__avatar-surface {
518
+ flex: 1;
519
+ position: relative;
520
+ background: linear-gradient(180deg, #e8e4df 0%, #d4cfc8 100%);
521
+ overflow: hidden;
522
+ }
523
+
524
+ .ll-expanded__avatar-image,
525
+ .ll-expanded__avatar-idle-loop,
526
+ .ll-expanded__avatar-live {
527
+ position: absolute;
528
+ inset: 0;
529
+ width: 100%;
530
+ height: 100%;
531
+ }
532
+
533
+ .ll-expanded__avatar-idle-loop {
534
+ object-fit: cover;
535
+ object-position: top;
536
+ z-index: 1;
537
+ }
538
+
539
+ .ll-expanded__avatar-live {
540
+ z-index: 2;
541
+ }
542
+
543
+ .ll-expanded__avatar-live > video {
544
+ width: 100%;
545
+ height: 100%;
546
+ object-fit: cover;
547
+ object-position: top;
548
+ }
549
+
550
+ .ll-expanded__overlay {
551
+ position: absolute;
552
+ inset: 0;
553
+ z-index: 5;
554
+ display: flex;
555
+ flex-direction: column;
556
+ align-items: center;
557
+ justify-content: center;
558
+ color: white;
559
+ gap: 8px;
560
+ }
561
+
562
+ .ll-expanded__overlay--connecting,
563
+ .ll-expanded__overlay--switching {
564
+ background: rgba(0, 0, 0, 0.3);
565
+ backdrop-filter: blur(2px);
566
+ -webkit-backdrop-filter: blur(2px);
567
+ }
568
+
569
+ .ll-expanded__overlay--gesture,
570
+ .ll-expanded__overlay--play {
571
+ background: rgba(0, 0, 0, 0.4);
572
+ backdrop-filter: blur(4px);
573
+ -webkit-backdrop-filter: blur(4px);
574
+ transition: background var(--ll-transition-fast);
575
+ }
576
+ .ll-expanded__overlay--gesture:hover,
577
+ .ll-expanded__overlay--play:hover {
578
+ background: rgba(0, 0, 0, 0.5);
579
+ }
580
+
581
+ .ll-expanded__play-circle {
582
+ width: 64px;
583
+ height: 64px;
584
+ border-radius: 50%;
585
+ background: rgba(255, 255, 255, 0.95);
586
+ color: var(--ll-color-primary);
587
+ display: flex;
588
+ align-items: center;
589
+ justify-content: center;
590
+ transition: transform var(--ll-transition-fast);
591
+ }
592
+ .ll-expanded__overlay--play:hover .ll-expanded__play-circle {
593
+ transform: scale(1.1);
594
+ }
595
+
596
+ .ll-expanded__overlay-text {
597
+ font-size: 14px;
598
+ font-weight: 500;
599
+ color: white;
600
+ margin: 0;
601
+ }
602
+
603
+ .ll-expanded__overlay-subtext {
604
+ font-size: 12px;
605
+ color: rgba(255, 255, 255, 0.75);
606
+ margin: 0;
607
+ }
608
+
609
+ .ll-expanded__spinner {
610
+ width: 32px;
611
+ height: 32px;
612
+ border: 2px solid rgba(255, 255, 255, 0.3);
613
+ border-top-color: white;
614
+ border-radius: 50%;
615
+ animation: ll-spin 1s linear infinite;
616
+ }
617
+
618
+ .ll-expanded__spinner--small {
619
+ width: 16px;
620
+ height: 16px;
621
+ border-width: 2px;
622
+ border-top-color: currentColor;
623
+ border-color: rgba(13, 13, 13, 0.2);
624
+ border-top-color: var(--ll-color-primary);
625
+ }
626
+
627
+ @keyframes ll-spin {
628
+ to {
629
+ transform: rotate(360deg);
630
+ }
631
+ }
632
+
633
+ .ll-expanded__state-pill-wrap {
634
+ position: absolute;
635
+ top: 12px;
636
+ right: 12px;
637
+ z-index: 6;
638
+ }
639
+
640
+ .ll-expanded__mic-error {
641
+ position: absolute;
642
+ top: 48px;
643
+ left: 12px;
644
+ right: 12px;
645
+ z-index: 6;
646
+ display: flex;
647
+ align-items: center;
648
+ gap: 8px;
649
+ padding: 8px 12px;
650
+ background: rgba(239, 68, 68, 0.9);
651
+ color: white;
652
+ border-radius: 8px;
653
+ font-size: 12px;
654
+ }
655
+
656
+ .ll-expanded__mic-error button {
657
+ color: white;
658
+ font-size: 11px;
659
+ font-weight: 500;
660
+ text-decoration: underline;
661
+ white-space: nowrap;
662
+ }
663
+
664
+ .ll-expanded__transcript {
665
+ position: absolute;
666
+ bottom: 12px;
667
+ left: 12px;
668
+ right: 12px;
669
+ z-index: 6;
670
+ padding: 10px 14px;
671
+ background: rgba(13, 13, 13, 0.45);
672
+ backdrop-filter: blur(8px);
673
+ -webkit-backdrop-filter: blur(8px);
674
+ border-radius: 12px;
675
+ color: white;
676
+ }
677
+
678
+ .ll-expanded__transcript p {
679
+ margin: 0;
680
+ font-size: 14px;
681
+ font-weight: 500;
682
+ line-height: 1.45;
683
+ display: -webkit-box;
684
+ -webkit-line-clamp: 3;
685
+ -webkit-box-orient: vertical;
686
+ overflow: hidden;
687
+ }
688
+
689
+ /* Footer CTA */
690
+ .ll-expanded__footer {
691
+ padding: 12px 16px;
692
+ flex-shrink: 0;
693
+ }
694
+
695
+ .ll-expanded__cta {
696
+ width: 100%;
697
+ padding: 12px 16px;
698
+ border-radius: var(--ll-radius-pill);
699
+ font-size: 14px;
700
+ font-weight: 500;
701
+ display: flex;
702
+ align-items: center;
703
+ justify-content: center;
704
+ gap: 8px;
705
+ transition: opacity var(--ll-transition-fast),
706
+ background var(--ll-transition-fast);
707
+ }
708
+
709
+ .ll-expanded__cta--primary {
710
+ background: var(--ll-color-primary);
711
+ color: white;
712
+ }
713
+ .ll-expanded__cta--primary:hover {
714
+ opacity: 0.88;
715
+ }
716
+
717
+ .ll-expanded__cta--loading {
718
+ background: var(--ll-color-border-subtle);
719
+ color: var(--ll-color-muted);
720
+ opacity: 0.7;
721
+ }
722
+
723
+ .ll-expanded__cta--danger {
724
+ background: transparent;
725
+ color: var(--ll-color-danger);
726
+ border: 1px solid rgba(239, 68, 68, 0.3);
727
+ }
728
+ .ll-expanded__cta--danger:hover {
729
+ background: var(--ll-color-danger);
730
+ color: white;
731
+ }
732
+
733
+ .ll-expanded__cta-icon {
734
+ width: 18px;
735
+ height: 18px;
736
+ }
737
+
738
+ .ll-expanded__retry {
739
+ text-align: center;
740
+ }
741
+ .ll-expanded__error-text {
742
+ font-size: 12px;
743
+ color: var(--ll-color-danger);
744
+ margin: 0 0 8px;
745
+ }
746
+
747
+ /* ── Error boundary fallback ─────────────────────────────────── */
748
+
749
+ .ll-error-boundary {
750
+ position: fixed;
751
+ bottom: 16px;
752
+ right: 16px;
753
+ padding: 16px;
754
+ background: var(--ll-color-bg, white);
755
+ border: 1px solid var(--ll-color-danger, #ef4444);
756
+ border-radius: var(--ll-radius-card, 12px);
757
+ max-width: 320px;
758
+ font-family: var(--ll-font);
759
+ box-shadow: var(--ll-shadow-card);
760
+ }
761
+ .ll-error-boundary__title {
762
+ font-size: 13px;
763
+ font-weight: 600;
764
+ color: var(--ll-color-danger, #ef4444);
765
+ margin: 0 0 4px;
766
+ }
767
+ .ll-error-boundary__message {
768
+ font-size: 12px;
769
+ color: var(--ll-color-muted, #808080);
770
+ margin: 0 0 8px;
771
+ word-break: break-word;
772
+ }
773
+ .ll-error-boundary__retry {
774
+ font-size: 12px;
775
+ color: var(--ll-color-primary, #0d0d0d);
776
+ text-decoration: underline;
777
+ cursor: pointer;
778
+ border: none;
779
+ background: none;
780
+ }
781
+
782
+ /* ── Focus rings (accessibility) ─────────────────────────────── */
783
+
784
+ .ll-widget button:focus-visible,
785
+ .ll-widget [role="button"]:focus-visible,
786
+ .ll-widget [role="menuitem"]:focus-visible {
787
+ outline: 2px solid var(--ll-color-primary);
788
+ outline-offset: 2px;
789
+ }