@cratis/components 4.10.4 → 4.11.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,1287 @@
1
+ @layer cratis-components {
2
+ /* @cratis/components/Chat/styles — requires @cratis/components/styles/base. Areas: Chat. */
3
+ /* ── ./Chat/ChatConversation.css ─────────────────────────────────────── */
4
+ /* Copyright (c) Cratis. All rights reserved. */
5
+ /* Licensed under the MIT license. See LICENSE file in the project root for full license information. */
6
+
7
+ /* Every color comes from the --cratis-* token seam so the conversation follows whatever theme the
8
+ host runs. */
9
+ .cratis-chat-conversation {
10
+ flex: 1;
11
+ min-height: 0;
12
+ display: flex;
13
+ flex-direction: column;
14
+ }
15
+
16
+ .cratis-chat-conversation__messages {
17
+ flex: 1;
18
+ min-height: 0;
19
+ overflow-y: auto;
20
+ padding: 0.75rem;
21
+ display: flex;
22
+ flex-direction: column;
23
+ gap: 0.875rem;
24
+ }
25
+
26
+ .cratis-chat-conversation__empty {
27
+ margin: 0;
28
+ padding: 1rem 0;
29
+ text-align: center;
30
+ font-size: 0.8125rem;
31
+ color: var(--cratis-text-color-secondary);
32
+ }
33
+
34
+ .cratis-chat-message {
35
+ display: flex;
36
+ flex-direction: column;
37
+ gap: 0.25rem;
38
+ }
39
+
40
+ .cratis-chat-message--continuation {
41
+ margin-top: -0.625rem;
42
+ }
43
+
44
+ .cratis-chat-message__author {
45
+ display: flex;
46
+ align-items: center;
47
+ gap: 0.5rem;
48
+ }
49
+
50
+ .cratis-chat-message__author-name {
51
+ font-size: 0.8125rem;
52
+ font-weight: 600;
53
+ color: var(--cratis-text-color);
54
+ }
55
+
56
+ .cratis-chat-message__content {
57
+ display: flex;
58
+ flex-direction: column;
59
+ gap: 0.125rem;
60
+ }
61
+
62
+ .cratis-chat-message__panel {
63
+ position: relative;
64
+ background: var(--cratis-surface-section);
65
+ border: 1px solid var(--cratis-surface-border);
66
+ border-radius: var(--cratis-border-radius);
67
+ padding: 0.5rem 0.625rem;
68
+ }
69
+
70
+ .cratis-chat-message__body {
71
+ margin: 0;
72
+ font-size: 0.8125rem;
73
+ line-height: 1.45;
74
+ color: var(--cratis-text-color);
75
+ white-space: pre-wrap;
76
+ overflow-wrap: anywhere;
77
+ }
78
+
79
+ .cratis-chat-message__mention {
80
+ color: var(--cratis-highlight-text-color);
81
+ background: var(--cratis-highlight-bg);
82
+ border-radius: 4px;
83
+ padding: 0 0.2em;
84
+ font-weight: 600;
85
+ }
86
+
87
+ .cratis-chat-message__mention[data-kind='agent'] {
88
+ text-decoration: underline dotted;
89
+ text-underline-offset: 2px;
90
+ }
91
+
92
+ /* The action buttons hang over the message's top-right corner and only show while the message is
93
+ hovered — or while one of them holds keyboard focus, which is why they hide through opacity
94
+ rather than display (a display:none button can never receive focus in the first place). */
95
+ .cratis-chat-message__actions {
96
+ position: absolute;
97
+ top: -0.75rem;
98
+ right: 0.375rem;
99
+ display: flex;
100
+ gap: 2px;
101
+ padding: 2px;
102
+ background: var(--cratis-surface-overlay);
103
+ border: 1px solid var(--cratis-surface-border);
104
+ border-radius: 6px;
105
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
106
+ opacity: 0;
107
+ pointer-events: none;
108
+ transition: opacity 0.12s;
109
+ }
110
+
111
+ .cratis-chat-message:hover .cratis-chat-message__actions,
112
+ .cratis-chat-message__actions:focus-within {
113
+ opacity: 1;
114
+ pointer-events: auto;
115
+ }
116
+
117
+ .cratis-chat-message__action {
118
+ background: transparent;
119
+ border: none;
120
+ border-radius: 4px;
121
+ width: 1.5rem;
122
+ height: 1.5rem;
123
+ display: flex;
124
+ align-items: center;
125
+ justify-content: center;
126
+ cursor: pointer;
127
+ color: var(--cratis-text-color-secondary);
128
+ font-size: 0.75rem;
129
+ transition:
130
+ background 0.12s,
131
+ color 0.12s;
132
+ }
133
+
134
+ .cratis-chat-message__action:hover {
135
+ background: var(--cratis-surface-hover);
136
+ color: var(--cratis-text-color);
137
+ }
138
+
139
+ .cratis-chat-message__time {
140
+ font-size: 0.6875rem;
141
+ color: var(--cratis-text-color-secondary);
142
+ padding-left: 0.125rem;
143
+ }
144
+
145
+ /* ── ./Chat/ChatSidebar.css ─────────────────────────────────────── */
146
+ /* Copyright (c) Cratis. All rights reserved. */
147
+ /* Licensed under the MIT license. See LICENSE file in the project root for full license information. */
148
+
149
+ /* The overlay layer the sidebar portals into. It is transparent and does not intercept pointer
150
+ events by default (`modal={false}`, the default) — a chat lives next to the work, so the rest
151
+ of the page stays visible and interactive behind it. Only `data-modal='true'` paints a backdrop
152
+ and blocks clicks outside the panel, matching a dialog's masking. */
153
+ .cratis-chat-sidebar__backdrop {
154
+ position: fixed;
155
+ inset: 0;
156
+ z-index: var(--cratis-z-index-dialog);
157
+ pointer-events: none;
158
+ }
159
+
160
+ .cratis-chat-sidebar__backdrop[data-modal='true'] {
161
+ background: var(--cratis-maskbg);
162
+ pointer-events: auto;
163
+ }
164
+
165
+ .cratis-chat-sidebar__backdrop[data-entering] {
166
+ animation: cratis-chat-sidebar-backdrop-fade 160ms ease-out;
167
+ }
168
+
169
+ .cratis-chat-sidebar__backdrop[data-exiting] {
170
+ animation: cratis-chat-sidebar-backdrop-fade 160ms reverse ease-in;
171
+ }
172
+
173
+ @keyframes cratis-chat-sidebar-backdrop-fade {
174
+ from {
175
+ opacity: 0;
176
+ }
177
+
178
+ to {
179
+ opacity: 1;
180
+ }
181
+ }
182
+
183
+ /* Baseline chat sidebar — a panel needs a visible surface even before a theme preset is applied,
184
+ so this ships a token-based default that a preset or `pt` can still override. Every color comes
185
+ from the --cratis-* token seam so the sidebar follows whatever theme the host runs. */
186
+ .cratis-chat-sidebar {
187
+ position: fixed;
188
+ top: 0;
189
+ bottom: 0;
190
+ right: 0;
191
+ display: flex;
192
+ flex-direction: column;
193
+ background: var(--cratis-surface-overlay);
194
+ color: var(--cratis-text-color);
195
+ border-left: 1px solid var(--cratis-surface-border);
196
+ box-shadow: 0 0 24px rgba(0, 0, 0, 0.35);
197
+ pointer-events: auto;
198
+ outline: none;
199
+ transform: translateX(0);
200
+ transition: transform 200ms ease;
201
+ }
202
+
203
+ .cratis-chat-sidebar[data-entering] {
204
+ transform: translateX(100%);
205
+ animation: cratis-chat-sidebar-slide-in 200ms ease-out forwards;
206
+ }
207
+
208
+ .cratis-chat-sidebar[data-exiting] {
209
+ animation: cratis-chat-sidebar-slide-in 200ms ease-in reverse;
210
+ }
211
+
212
+ @keyframes cratis-chat-sidebar-slide-in {
213
+ from {
214
+ transform: translateX(100%);
215
+ }
216
+
217
+ to {
218
+ transform: translateX(0);
219
+ }
220
+ }
221
+
222
+ .cratis-chat-sidebar[data-position='left'] {
223
+ left: 0;
224
+ right: auto;
225
+ border-left: none;
226
+ border-right: 1px solid var(--cratis-surface-border);
227
+ }
228
+
229
+ .cratis-chat-sidebar[data-position='left'][data-entering] {
230
+ animation-name: cratis-chat-sidebar-slide-in-left;
231
+ }
232
+
233
+ .cratis-chat-sidebar[data-position='left'][data-exiting] {
234
+ animation-name: cratis-chat-sidebar-slide-in-left;
235
+ }
236
+
237
+ @keyframes cratis-chat-sidebar-slide-in-left {
238
+ from {
239
+ transform: translateX(-100%);
240
+ }
241
+
242
+ to {
243
+ transform: translateX(0);
244
+ }
245
+ }
246
+
247
+ .cratis-chat-sidebar__header {
248
+ display: flex;
249
+ align-items: center;
250
+ gap: 0.5rem;
251
+ padding: 0.75rem 1rem;
252
+ border-bottom: 1px solid var(--cratis-surface-border);
253
+ flex-shrink: 0;
254
+ }
255
+
256
+ .cratis-chat-sidebar__title {
257
+ flex: 1;
258
+ min-width: 0;
259
+ font-size: 0.9375rem;
260
+ font-weight: 600;
261
+ overflow: hidden;
262
+ text-overflow: ellipsis;
263
+ white-space: nowrap;
264
+ }
265
+
266
+ .cratis-chat-sidebar__title--pending {
267
+ font-style: italic;
268
+ font-weight: 400;
269
+ color: var(--cratis-text-color-secondary);
270
+ }
271
+
272
+ .cratis-chat-sidebar__back,
273
+ .cratis-chat-sidebar__close {
274
+ background: transparent;
275
+ border: none;
276
+ border-radius: var(--cratis-border-radius);
277
+ width: 1.75rem;
278
+ height: 1.75rem;
279
+ display: flex;
280
+ align-items: center;
281
+ justify-content: center;
282
+ cursor: pointer;
283
+ color: var(--cratis-text-color-secondary);
284
+ font-size: 0.875rem;
285
+ flex-shrink: 0;
286
+ transition:
287
+ color 0.12s,
288
+ background 0.12s;
289
+ }
290
+
291
+ .cratis-chat-sidebar__back:hover,
292
+ .cratis-chat-sidebar__close:hover {
293
+ background: var(--cratis-surface-hover);
294
+ color: var(--cratis-text-color);
295
+ }
296
+
297
+ .cratis-chat-sidebar__content {
298
+ flex: 1;
299
+ min-height: 0;
300
+ display: flex;
301
+ flex-direction: column;
302
+ overflow: hidden;
303
+ }
304
+
305
+ /* ── ./Chat/ChatTopicList.css ─────────────────────────────────────── */
306
+ /* Copyright (c) Cratis. All rights reserved. */
307
+ /* Licensed under the MIT license. See LICENSE file in the project root for full license information. */
308
+
309
+ /* Every color comes from the --cratis-* token seam so the list follows whatever theme the host
310
+ runs. */
311
+ .cratis-chat-topics {
312
+ flex: 1;
313
+ min-height: 0;
314
+ display: flex;
315
+ flex-direction: column;
316
+ gap: 0.5rem;
317
+ padding: 0.75rem;
318
+ overflow-y: auto;
319
+ }
320
+
321
+ .cratis-chat-topics__start {
322
+ align-self: flex-start;
323
+ display: inline-flex;
324
+ align-items: center;
325
+ gap: 0.4rem;
326
+ background: var(--cratis-primary-color);
327
+ color: var(--cratis-primary-color-text);
328
+ border: none;
329
+ border-radius: var(--cratis-border-radius);
330
+ padding: 0.4rem 0.75rem;
331
+ font-size: 0.8125rem;
332
+ cursor: pointer;
333
+ transition: background 0.12s;
334
+ }
335
+
336
+ .cratis-chat-topics__start:hover {
337
+ background: var(--cratis-primary-600);
338
+ }
339
+
340
+ .cratis-chat-topics__empty {
341
+ margin: 0;
342
+ padding: 1rem 0;
343
+ text-align: center;
344
+ font-size: 0.8125rem;
345
+ color: var(--cratis-text-color-secondary);
346
+ }
347
+
348
+ .cratis-chat-topics__list {
349
+ display: flex;
350
+ flex-direction: column;
351
+ gap: 2px;
352
+ margin: 0;
353
+ padding: 0;
354
+ list-style: none;
355
+ }
356
+
357
+ .cratis-chat-topics__topic {
358
+ display: flex;
359
+ align-items: center;
360
+ gap: 0.625rem;
361
+ width: 100%;
362
+ padding: 0.5rem 0.625rem;
363
+ background: transparent;
364
+ border: none;
365
+ border-radius: var(--cratis-border-radius);
366
+ text-align: left;
367
+ cursor: pointer;
368
+ color: var(--cratis-text-color);
369
+ transition: background 0.12s;
370
+ }
371
+
372
+ .cratis-chat-topics__topic:hover {
373
+ background: var(--cratis-surface-hover);
374
+ }
375
+
376
+ .cratis-chat-topics__details {
377
+ flex: 1;
378
+ min-width: 0;
379
+ display: flex;
380
+ flex-direction: column;
381
+ gap: 0.125rem;
382
+ }
383
+
384
+ .cratis-chat-topics__name {
385
+ font-size: 0.875rem;
386
+ font-weight: 600;
387
+ overflow: hidden;
388
+ text-overflow: ellipsis;
389
+ white-space: nowrap;
390
+ }
391
+
392
+ .cratis-chat-topics__name--pending {
393
+ font-style: italic;
394
+ font-weight: 400;
395
+ color: var(--cratis-text-color-secondary);
396
+ }
397
+
398
+ .cratis-chat-topics__started-by {
399
+ font-size: 0.75rem;
400
+ color: var(--cratis-text-color-secondary);
401
+ overflow: hidden;
402
+ text-overflow: ellipsis;
403
+ white-space: nowrap;
404
+ }
405
+
406
+ .cratis-chat-topics__activity {
407
+ align-self: flex-start;
408
+ flex-shrink: 0;
409
+ font-size: 0.6875rem;
410
+ color: var(--cratis-text-color-secondary);
411
+ }
412
+
413
+ /* ── ./Chat/Kit/Avatar/PersonAvatarCircle.css ─────────────────────────────────────── */
414
+ /* Copyright (c) Cratis. All rights reserved. */
415
+ /* Licensed under the MIT license. See LICENSE file in the project root for full license information. */
416
+
417
+ .person-avatar-circle {
418
+ display: inline-flex;
419
+ align-items: center;
420
+ justify-content: center;
421
+ border-radius: 50%;
422
+ flex-shrink: 0;
423
+ overflow: hidden;
424
+ }
425
+
426
+ .person-avatar-circle-image {
427
+ display: block;
428
+ width: 100%;
429
+ height: 100%;
430
+ object-fit: cover;
431
+ /* Avatars sit on draggable elements (chat bubbles, cards) — the browser's native image drag
432
+ must never hijack the pointer gesture. draggable={false} covers most engines; WebKit also
433
+ needs the CSS opt-out. */
434
+ -webkit-user-drag: none;
435
+ user-select: none;
436
+ -webkit-user-select: none;
437
+ }
438
+
439
+ .person-avatar-circle-initials {
440
+ color: #fff;
441
+ font-weight: 700;
442
+ letter-spacing: 0.02em;
443
+ font-family: system-ui, sans-serif;
444
+ }
445
+
446
+ /* ── ./Chat/Kit/Chat.css ─────────────────────────────────────── */
447
+ /* Copyright (c) Cratis. All rights reserved. */
448
+ /* Licensed under the MIT license. See LICENSE file in the project root for full license information. */
449
+
450
+ /* Every color comes from the --cratis-* token seam, so the conversation follows whatever theme
451
+ the host runs — light, dark, or its own. */
452
+
453
+ /* ── Chat panel (floating dropdown from the chat button) ─────────────────── */
454
+ .chat-panel {
455
+ position: absolute;
456
+ top: calc(100% + 6px);
457
+ left: 0;
458
+ width: 280px;
459
+ background: var(--cratis-surface-overlay);
460
+ border: 1px solid var(--cratis-surface-border);
461
+ border-radius: 12px;
462
+ box-shadow: 0 12px 32px rgba(0, 0, 0, 0.35);
463
+ display: flex;
464
+ flex-direction: column;
465
+ z-index: 300;
466
+ overflow: hidden;
467
+ }
468
+
469
+ /* ── Panel header ─────────────────────────────────────────────────────────── */
470
+ .chat-panel-header {
471
+ display: flex;
472
+ align-items: center;
473
+ justify-content: space-between;
474
+ padding: 10px 14px 8px;
475
+ border-bottom: 1px solid var(--cratis-surface-border);
476
+ flex-shrink: 0;
477
+ }
478
+
479
+ .chat-panel-title {
480
+ font-size: 13px;
481
+ font-weight: 600;
482
+ color: var(--cratis-text-color);
483
+ }
484
+
485
+ .chat-panel-close {
486
+ background: none;
487
+ border: none;
488
+ font-size: 20px;
489
+ line-height: 1;
490
+ cursor: pointer;
491
+ color: var(--cratis-text-color-secondary);
492
+ padding: 0 2px;
493
+ border-radius: 4px;
494
+ transition: color 0.12s;
495
+ }
496
+
497
+ .chat-panel-close:hover {
498
+ color: var(--cratis-text-color);
499
+ }
500
+
501
+ /* ── Scrollable messages area ─────────────────────────────────────────────── */
502
+ .chat-panel-messages {
503
+ flex: 1;
504
+ max-height: 260px;
505
+ overflow-y: auto;
506
+ padding: 10px 12px;
507
+ display: flex;
508
+ flex-direction: column;
509
+ gap: 14px;
510
+ }
511
+
512
+ .chat-panel-empty {
513
+ font-size: 12px;
514
+ color: var(--cratis-text-color-secondary);
515
+ text-align: center;
516
+ margin: 16px 0;
517
+ }
518
+
519
+ /* ── Individual chat bubble ───────────────────────────────────────────────── */
520
+ .chat-bubble {
521
+ display: flex;
522
+ flex-direction: column;
523
+ gap: 4px;
524
+ }
525
+
526
+ /* Continuation bubbles (same author, no avatar row) get tighter spacing. */
527
+ .chat-bubble--continuation {
528
+ margin-top: -8px;
529
+ }
530
+
531
+ .chat-bubble-author {
532
+ display: flex;
533
+ align-items: center;
534
+ gap: 6px;
535
+ }
536
+
537
+ .chat-bubble-name {
538
+ font-size: 11px;
539
+ font-weight: 600;
540
+ color: var(--cratis-text-color);
541
+ }
542
+
543
+ .chat-bubble-body {
544
+ display: flex;
545
+ flex-direction: column;
546
+ gap: 3px;
547
+ padding-left: 28px;
548
+ }
549
+
550
+ .chat-bubble-text {
551
+ background: var(--cratis-surface-section);
552
+ border-radius: 0 10px 10px 10px;
553
+ padding: 7px 10px;
554
+ margin: 0;
555
+ font-size: 12px;
556
+ color: var(--cratis-text-color);
557
+ line-height: 1.45;
558
+ white-space: pre-wrap;
559
+ word-break: break-word;
560
+ }
561
+
562
+ .chat-bubble-time {
563
+ font-size: 10px;
564
+ color: var(--cratis-text-color-secondary);
565
+ padding-left: 2px;
566
+ }
567
+
568
+ /* ── Docked variant (the chat sidebar provides the frame and the heading) ── */
569
+ .chat-panel--docked {
570
+ position: static;
571
+ width: 100%;
572
+ height: 100%;
573
+ border: none;
574
+ border-radius: 0;
575
+ box-shadow: none;
576
+ }
577
+
578
+ .chat-panel--docked .chat-panel-messages {
579
+ max-height: none;
580
+ }
581
+
582
+ /* ── ./Chat/Kit/ChatBubble.css ─────────────────────────────────────── */
583
+ /* Copyright (c) Cratis. All rights reserved. */
584
+ /* Licensed under the MIT license. See LICENSE file in the project root for full license information. */
585
+
586
+ /* ── Thinking bubbles (small dots at top-right of avatar) ────────────────── */
587
+
588
+ @keyframes thinking-bubble-pop-in {
589
+ 0% {
590
+ transform: scale(0);
591
+ opacity: 0;
592
+ }
593
+ 60% {
594
+ transform: scale(1.3);
595
+ opacity: 1;
596
+ }
597
+ 100% {
598
+ transform: scale(1);
599
+ opacity: 1;
600
+ }
601
+ }
602
+
603
+ @keyframes thinking-bubble-pop-out {
604
+ 0% {
605
+ transform: scale(1);
606
+ opacity: 1;
607
+ }
608
+ 100% {
609
+ transform: scale(0);
610
+ opacity: 0;
611
+ }
612
+ }
613
+
614
+ .thinking-bubbles {
615
+ position: absolute;
616
+ pointer-events: none;
617
+ top: 0;
618
+ left: 0;
619
+ }
620
+
621
+ .thinking-dot {
622
+ border-radius: 50%;
623
+ position: absolute;
624
+ transform: scale(0);
625
+ opacity: 0;
626
+ }
627
+
628
+ /* ── Enter animation ─────────────────────────────────────────────────────── */
629
+
630
+ .thinking-bubbles--entering .thinking-dot--1 {
631
+ animation: thinking-bubble-pop-in 0.25s ease-out forwards;
632
+ }
633
+
634
+ .thinking-bubbles--entering .thinking-dot--2 {
635
+ animation: thinking-bubble-pop-in 0.25s ease-out 0.1s forwards;
636
+ }
637
+
638
+ /* ── Exit animation ──────────────────────────────────────────────────────── */
639
+
640
+ .thinking-bubbles--exiting .thinking-dot--2 {
641
+ transform: scale(1);
642
+ opacity: 1;
643
+ animation: thinking-bubble-pop-out 0.2s ease-in forwards;
644
+ }
645
+
646
+ .thinking-bubbles--exiting .thinking-dot--1 {
647
+ transform: scale(1);
648
+ opacity: 1;
649
+ animation: thinking-bubble-pop-out 0.2s ease-in 0.06s forwards;
650
+ }
651
+
652
+ /* ── ./Chat/Kit/ChatComposer.css ─────────────────────────────────────── */
653
+ /* Copyright (c) Cratis. All rights reserved. */
654
+ /* Licensed under the MIT license. See LICENSE file in the project root for full license information. */
655
+
656
+ /* Every color comes from the --cratis-* token seam, so the compose row follows whatever theme the
657
+ host runs. */
658
+ .chat-composer {
659
+ position: relative;
660
+ display: flex;
661
+ align-items: flex-end;
662
+ gap: 6px;
663
+ padding: 8px 10px 10px;
664
+ border-top: 1px solid var(--cratis-surface-border);
665
+ flex-shrink: 0;
666
+ }
667
+
668
+ .chat-composer__input {
669
+ flex: 1;
670
+ background: var(--cratis-surface-0);
671
+ border: 1px solid var(--cratis-surface-border);
672
+ border-radius: 8px;
673
+ color: var(--cratis-text-color);
674
+ font-size: 12px;
675
+ padding: 6px 8px;
676
+ resize: none;
677
+ outline: none;
678
+ font-family: inherit;
679
+ line-height: 1.4;
680
+ transition: border-color 0.12s;
681
+ }
682
+
683
+ .chat-composer__input::placeholder {
684
+ color: var(--cratis-text-color-secondary);
685
+ }
686
+
687
+ .chat-composer__input:focus {
688
+ border-color: var(--cratis-primary-color);
689
+ }
690
+
691
+ .chat-composer__emoji-toggle {
692
+ background: var(--cratis-surface-100);
693
+ border: 1px solid var(--cratis-surface-border);
694
+ border-radius: 8px;
695
+ width: 32px;
696
+ height: 32px;
697
+ display: flex;
698
+ align-items: center;
699
+ justify-content: center;
700
+ cursor: pointer;
701
+ color: var(--cratis-text-color-secondary);
702
+ font-size: 15px;
703
+ line-height: 1;
704
+ flex-shrink: 0;
705
+ transition:
706
+ background 0.12s,
707
+ color 0.12s;
708
+ }
709
+
710
+ .chat-composer__emoji-toggle:hover {
711
+ background: var(--cratis-surface-hover);
712
+ color: var(--cratis-text-color);
713
+ }
714
+
715
+ .chat-composer__send {
716
+ background: var(--cratis-primary-color);
717
+ border: none;
718
+ border-radius: 8px;
719
+ width: 32px;
720
+ height: 32px;
721
+ display: flex;
722
+ align-items: center;
723
+ justify-content: center;
724
+ cursor: pointer;
725
+ color: var(--cratis-primary-color-text);
726
+ flex-shrink: 0;
727
+ transition:
728
+ background 0.12s,
729
+ opacity 0.12s;
730
+ }
731
+
732
+ .chat-composer__send:hover:not(:disabled) {
733
+ background: var(--cratis-primary-600);
734
+ }
735
+
736
+ .chat-composer__send:disabled {
737
+ opacity: 0.35;
738
+ cursor: default;
739
+ }
740
+
741
+ /* ── ./Chat/Kit/ChatMessageBubble.css ─────────────────────────────────────── */
742
+ /* Copyright (c) Cratis. All rights reserved. */
743
+ /* Licensed under the MIT license. See LICENSE file in the project root for full license information. */
744
+
745
+ /* Every color comes from the --cratis-* token seam, so the bubble follows whatever theme the host
746
+ runs. */
747
+
748
+ /* The positioning context for the corner buttons — the message panel itself, so they straddle its
749
+ bottom edge rather than sitting below whatever else the bubble carries. */
750
+ .chat-bubble-panel {
751
+ position: relative;
752
+ }
753
+
754
+ /* Room below the panel for the half of the buttons that hangs outside it, so they never land on the
755
+ timestamp or the reactions underneath. Only when there are buttons: a bubble without them keeps its
756
+ old height. */
757
+ .chat-bubble-panel--with-actions {
758
+ margin-bottom: 13px;
759
+ }
760
+
761
+ /* And a little room inside, so the half that overlaps the panel sits on its padding rather than on
762
+ the last line of the message. */
763
+ .chat-bubble-panel--with-actions .chat-bubble-text {
764
+ padding-bottom: 14px;
765
+ }
766
+
767
+ /* Straddling the panel's bottom edge in its lower-right corner — half in, half out, so they read as
768
+ belonging to the message without taking a line of their own inside it. */
769
+ .chat-bubble-actions {
770
+ position: absolute;
771
+ right: 10px;
772
+ bottom: -11px;
773
+ display: flex;
774
+ align-items: center;
775
+ gap: 6px;
776
+ }
777
+
778
+ /* A stroke around a solid fill: the button lies across the bubble's edge, so a translucent fill would
779
+ let that edge run straight through it and stop it reading as one round control. The fill is the
780
+ panel's own surface, which is what the half hanging outside is sitting on. */
781
+ .chat-bubble-action {
782
+ display: flex;
783
+ align-items: center;
784
+ justify-content: center;
785
+ width: 22px;
786
+ height: 22px;
787
+ padding: 0;
788
+ border: 1px solid var(--cratis-surface-border);
789
+ border-radius: 999px;
790
+ background: var(--cratis-surface-overlay);
791
+ color: var(--cratis-text-color-secondary);
792
+ font-size: 12px;
793
+ line-height: 1;
794
+ cursor: pointer;
795
+ }
796
+
797
+ .chat-bubble-action:hover,
798
+ .chat-bubble-action:focus-visible {
799
+ border-color: var(--cratis-text-color-secondary);
800
+ background: var(--cratis-surface-hover);
801
+ color: var(--cratis-text-color);
802
+ }
803
+
804
+ /* Once a person has reacted, the button shows their emoji as real state rather than a hover-only
805
+ affordance, so it stays visible without hovering. */
806
+ .chat-bubble-action--own-reaction {
807
+ border-color: var(--cratis-text-color-secondary);
808
+ background: var(--cratis-surface-hover);
809
+ color: var(--cratis-text-color);
810
+ font-size: 13px;
811
+ }
812
+
813
+ /* ── ./Chat/Kit/Emoji/EmojiPicker.css ─────────────────────────────────────── */
814
+ /* Copyright (c) Cratis. All rights reserved. */
815
+ /* Licensed under the MIT license. See LICENSE file in the project root for full license information. */
816
+
817
+ /* Every color comes from the --cratis-* token seam, so the picker follows whatever theme the host
818
+ runs — the same convention ReactionPicker.css and MentionSuggestions.css
819
+ follow. */
820
+ .emoji-picker {
821
+ display: flex;
822
+ flex-direction: column;
823
+ width: 296px;
824
+ background: var(--cratis-surface-overlay);
825
+ border: 1px solid var(--cratis-surface-border);
826
+ border-radius: 12px;
827
+ box-shadow: 0 12px 32px rgba(0, 0, 0, 0.35);
828
+ overflow: hidden;
829
+ }
830
+
831
+ .emoji-picker__grid {
832
+ display: grid;
833
+ grid-template-columns: repeat(8, 1fr);
834
+ gap: 2px;
835
+ height: 232px;
836
+ overflow-y: auto;
837
+ padding: 8px;
838
+ overscroll-behavior: contain;
839
+ }
840
+
841
+ .emoji-picker__emoji {
842
+ display: flex;
843
+ align-items: center;
844
+ justify-content: center;
845
+ width: 100%;
846
+ aspect-ratio: 1;
847
+ padding: 0;
848
+ border: none;
849
+ border-radius: 7px;
850
+ background: transparent;
851
+ font-size: 20px;
852
+ line-height: 1;
853
+ cursor: pointer;
854
+ }
855
+
856
+ .emoji-picker__emoji:hover,
857
+ .emoji-picker__emoji:focus-visible {
858
+ background: var(--cratis-surface-hover);
859
+ }
860
+
861
+ .emoji-picker__emoji--selected {
862
+ background: var(--cratis-highlight-bg);
863
+ }
864
+
865
+ .emoji-picker__tabs {
866
+ display: flex;
867
+ gap: 2px;
868
+ padding: 6px;
869
+ border-top: 1px solid var(--cratis-surface-border);
870
+ background: var(--cratis-surface-ground);
871
+ }
872
+
873
+ .emoji-picker__tab {
874
+ display: flex;
875
+ flex: 1;
876
+ align-items: center;
877
+ justify-content: center;
878
+ height: 26px;
879
+ padding: 0;
880
+ border: none;
881
+ border-radius: 6px;
882
+ background: transparent;
883
+ /* Muted until it is the section being shown, so the row reads as one control rather than nine
884
+ competing emojis. */
885
+ filter: grayscale(1);
886
+ opacity: 0.55;
887
+ font-size: 15px;
888
+ line-height: 1;
889
+ cursor: pointer;
890
+ }
891
+
892
+ .emoji-picker__tab:hover {
893
+ opacity: 0.85;
894
+ }
895
+
896
+ .emoji-picker__tab--current {
897
+ background: var(--cratis-surface-hover);
898
+ filter: none;
899
+ opacity: 1;
900
+ }
901
+
902
+ /* ── ./Chat/Kit/FailedReply.css ─────────────────────────────────────── */
903
+ /* Copyright (c) Cratis. All rights reserved. */
904
+ /* Licensed under the MIT license. See LICENSE file in the project root for full license information. */
905
+
906
+ /* Every color comes from the --cratis-* token seam, so the failure line follows whatever theme
907
+ the host runs. The warning tint is mixed from the orange token because
908
+ the seam ships no alpha variants. */
909
+ .chat-failed-reply {
910
+ display: flex;
911
+ flex-direction: column;
912
+ gap: 4px;
913
+ padding: 6px 8px;
914
+ border-left: 2px solid var(--cratis-orange-500);
915
+ background: color-mix(in srgb, var(--cratis-orange-500) 10%, transparent);
916
+ border-radius: 0 6px 6px 0;
917
+ }
918
+
919
+ .chat-failed-reply__line {
920
+ display: flex;
921
+ align-items: center;
922
+ gap: 6px;
923
+ }
924
+
925
+ .chat-failed-reply__icon {
926
+ font-size: 12px;
927
+ color: var(--cratis-orange-500);
928
+ }
929
+
930
+ .chat-failed-reply__text {
931
+ font-size: 12px;
932
+ color: var(--cratis-text-color);
933
+ }
934
+
935
+ .chat-failed-reply__actions {
936
+ display: flex;
937
+ align-items: center;
938
+ gap: 10px;
939
+ padding-left: 18px;
940
+ }
941
+
942
+ .chat-failed-reply__action {
943
+ background: none;
944
+ border: none;
945
+ padding: 0;
946
+ font-size: 11px;
947
+ color: var(--cratis-text-color);
948
+ text-decoration: underline;
949
+ cursor: pointer;
950
+ }
951
+
952
+ .chat-failed-reply__action:hover {
953
+ color: var(--cratis-text-color);
954
+ }
955
+
956
+ .chat-failed-reply__detail {
957
+ margin: 0;
958
+ padding: 12px;
959
+ border-radius: 6px;
960
+ background: var(--cratis-surface-100);
961
+ color: var(--cratis-text-color);
962
+ font-size: 12px;
963
+ line-height: 1.5;
964
+ white-space: pre-wrap;
965
+ word-break: break-word;
966
+ max-height: 24rem;
967
+ overflow: auto;
968
+ }
969
+
970
+ /* ── ./Chat/Kit/Mentions/MentionSuggestions.css ─────────────────────────────────────── */
971
+ /* Copyright (c) Cratis. All rights reserved. */
972
+ /* Licensed under the MIT license. See LICENSE file in the project root for full license information. */
973
+
974
+ /* Sits above the compose row inside the chat panel. Every color comes from the --cratis-* token
975
+ seam, so the list follows whatever theme the host runs. */
976
+ .mention-suggestions {
977
+ position: absolute;
978
+ bottom: calc(100% + 4px);
979
+ left: 8px;
980
+ right: 8px;
981
+ max-height: 180px;
982
+ overflow-y: auto;
983
+ background: var(--cratis-surface-overlay);
984
+ border: 1px solid var(--cratis-surface-border);
985
+ border-radius: 10px;
986
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
987
+ padding: 4px;
988
+ display: flex;
989
+ flex-direction: column;
990
+ gap: 2px;
991
+ z-index: 400;
992
+ }
993
+
994
+ .mention-suggestions__item {
995
+ display: flex;
996
+ align-items: center;
997
+ gap: 8px;
998
+ width: 100%;
999
+ padding: 5px 8px;
1000
+ border: none;
1001
+ border-radius: 7px;
1002
+ background: transparent;
1003
+ color: var(--cratis-text-color);
1004
+ font-size: 12px;
1005
+ text-align: left;
1006
+ cursor: pointer;
1007
+ }
1008
+
1009
+ .mention-suggestions__item--highlighted {
1010
+ background: var(--cratis-highlight-bg);
1011
+ color: var(--cratis-highlight-text-color);
1012
+ }
1013
+
1014
+ .mention-suggestions__name {
1015
+ overflow: hidden;
1016
+ text-overflow: ellipsis;
1017
+ white-space: nowrap;
1018
+ }
1019
+
1020
+ /* ── ./Chat/Kit/MessageReactions.css ─────────────────────────────────────── */
1021
+ /* Copyright (c) Cratis. All rights reserved. */
1022
+ /* Licensed under the MIT license. See LICENSE file in the project root for full license information. */
1023
+
1024
+ /* Every color comes from the --cratis-* token seam, so the badges follow whatever theme the host
1025
+ runs. */
1026
+ .message-reactions {
1027
+ position: relative;
1028
+ display: flex;
1029
+ align-items: center;
1030
+ gap: 4px;
1031
+ flex-wrap: wrap;
1032
+ }
1033
+
1034
+ .message-reactions__badge-wrapper {
1035
+ position: relative;
1036
+ }
1037
+
1038
+ .message-reactions__badge {
1039
+ display: flex;
1040
+ align-items: center;
1041
+ gap: 2px;
1042
+ padding: 2px 6px 2px 4px;
1043
+ border: 1px solid var(--cratis-surface-border);
1044
+ border-radius: 999px;
1045
+ background: var(--cratis-surface-100);
1046
+ cursor: pointer;
1047
+ }
1048
+
1049
+ .message-reactions__badge:hover {
1050
+ background: var(--cratis-surface-hover);
1051
+ }
1052
+
1053
+ /* A stack of the same emoji from different people overlaps slightly rather than repeating flat. */
1054
+ .message-reactions__stack {
1055
+ display: flex;
1056
+ }
1057
+
1058
+ .message-reactions__glyph {
1059
+ font-size: 12px;
1060
+ line-height: 1;
1061
+ }
1062
+
1063
+ .message-reactions__glyph + .message-reactions__glyph {
1064
+ margin-left: -7px;
1065
+ }
1066
+
1067
+ .message-reactions__count {
1068
+ font-size: 10px;
1069
+ color: var(--cratis-text-color-secondary);
1070
+ }
1071
+
1072
+ /* Positioned by AnchoredOverlay, which portals it to <body> — so this only describes the bubble
1073
+ itself, never where it sits. */
1074
+ .message-reactions__tooltip {
1075
+ min-width: 120px;
1076
+ background: var(--cratis-surface-overlay);
1077
+ border: 1px solid var(--cratis-surface-border);
1078
+ border-radius: 10px;
1079
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
1080
+ padding: 6px 8px;
1081
+ display: flex;
1082
+ flex-direction: column;
1083
+ gap: 4px;
1084
+ }
1085
+
1086
+ .message-reactions__tooltip-row {
1087
+ display: flex;
1088
+ align-items: center;
1089
+ gap: 8px;
1090
+ }
1091
+
1092
+ .message-reactions__tooltip-emoji {
1093
+ font-size: 13px;
1094
+ line-height: 1;
1095
+ }
1096
+
1097
+ .message-reactions__tooltip-name {
1098
+ font-size: 11px;
1099
+ color: var(--cratis-text-color);
1100
+ white-space: nowrap;
1101
+ }
1102
+
1103
+ /* ── ./Chat/Kit/ReactionPicker.css ─────────────────────────────────────── */
1104
+ /* Copyright (c) Cratis. All rights reserved. */
1105
+ /* Licensed under the MIT license. See LICENSE file in the project root for full license information. */
1106
+
1107
+ /* Portaled to <body> by AnchoredOverlay rather than positioned by its parent: the chat panel is a
1108
+ scrolling sidebar, and anything positioned inside it is clipped the moment it reaches the edge.
1109
+ Every color comes from the --cratis-* token seam, so the picker follows whatever theme the host
1110
+ runs. */
1111
+ .reaction-picker {
1112
+ display: flex;
1113
+ flex-direction: column;
1114
+ gap: 4px;
1115
+ align-items: flex-end;
1116
+ }
1117
+
1118
+ .reaction-picker__quick {
1119
+ display: flex;
1120
+ gap: 2px;
1121
+ align-items: center;
1122
+ background: var(--cratis-surface-overlay);
1123
+ border: 1px solid var(--cratis-surface-border);
1124
+ border-radius: 999px;
1125
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
1126
+ padding: 4px;
1127
+ }
1128
+
1129
+ .reaction-picker__options {
1130
+ display: contents;
1131
+ }
1132
+
1133
+ .reaction-picker__item {
1134
+ display: flex;
1135
+ align-items: center;
1136
+ justify-content: center;
1137
+ width: 28px;
1138
+ height: 28px;
1139
+ padding: 0;
1140
+ border: none;
1141
+ border-radius: 999px;
1142
+ background: transparent;
1143
+ font-size: 17px;
1144
+ line-height: 1;
1145
+ cursor: pointer;
1146
+ }
1147
+
1148
+ .reaction-picker__item:hover,
1149
+ .reaction-picker__item:focus-visible {
1150
+ background: var(--cratis-surface-hover);
1151
+ }
1152
+
1153
+ .reaction-picker__item--selected {
1154
+ background: var(--cratis-highlight-bg);
1155
+ }
1156
+
1157
+ /* Deliberately the quiet one on the row: it opens the rest of the emojis rather than being an emoji,
1158
+ so it reads as a control and never competes with the six actual choices next to it. */
1159
+ .reaction-picker__more {
1160
+ display: flex;
1161
+ align-items: center;
1162
+ justify-content: center;
1163
+ flex-shrink: 0;
1164
+ width: 28px;
1165
+ height: 28px;
1166
+ margin-left: 2px;
1167
+ padding: 0;
1168
+ border: none;
1169
+ border-radius: 999px;
1170
+ background: var(--cratis-surface-100);
1171
+ color: var(--cratis-text-color-secondary);
1172
+ font-size: 15px;
1173
+ line-height: 1;
1174
+ cursor: pointer;
1175
+ }
1176
+
1177
+ .reaction-picker__more:hover,
1178
+ .reaction-picker__more:focus-visible,
1179
+ .reaction-picker__more--open {
1180
+ background: var(--cratis-surface-hover);
1181
+ color: var(--cratis-text-color);
1182
+ }
1183
+
1184
+ /* Who has reacted so far, under the quick row — the same list a badge shows, so opening the picker
1185
+ answers "who gave what" without needing a second gesture. */
1186
+ .reaction-picker__given {
1187
+ display: flex;
1188
+ flex-direction: column;
1189
+ gap: 4px;
1190
+ align-self: stretch;
1191
+ max-height: 148px;
1192
+ overflow-y: auto;
1193
+ background: var(--cratis-surface-overlay);
1194
+ border: 1px solid var(--cratis-surface-border);
1195
+ border-radius: 10px;
1196
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
1197
+ padding: 6px 8px;
1198
+ }
1199
+
1200
+ .reaction-picker__given-row {
1201
+ display: flex;
1202
+ align-items: center;
1203
+ gap: 8px;
1204
+ }
1205
+
1206
+ .reaction-picker__given-emoji {
1207
+ font-size: 13px;
1208
+ line-height: 1;
1209
+ }
1210
+
1211
+ .reaction-picker__given-name {
1212
+ font-size: 11px;
1213
+ color: var(--cratis-text-color);
1214
+ white-space: nowrap;
1215
+ }
1216
+
1217
+ /* ── ./Chat/Kit/TypingIndicator.css ─────────────────────────────────────── */
1218
+ /* Copyright (c) Cratis. All rights reserved. */
1219
+ /* Licensed under the MIT license. See LICENSE file in the project root for full license information. */
1220
+
1221
+ /* Every color comes from the --cratis-* token seam, so the indicator follows whatever theme the
1222
+ host runs. */
1223
+ .chat-typing {
1224
+ display: flex;
1225
+ align-items: center;
1226
+ gap: 6px;
1227
+ padding: 2px 2px 4px;
1228
+ }
1229
+
1230
+ .chat-typing__authors {
1231
+ display: flex;
1232
+ align-items: center;
1233
+ }
1234
+
1235
+ .chat-typing__authors > * + * {
1236
+ margin-left: -6px;
1237
+ }
1238
+
1239
+ .chat-typing__label {
1240
+ font-size: 11px;
1241
+ color: var(--cratis-text-color-secondary);
1242
+ }
1243
+
1244
+ .chat-typing__dots {
1245
+ display: inline-flex;
1246
+ align-items: flex-end;
1247
+ gap: 3px;
1248
+ height: 10px;
1249
+ }
1250
+
1251
+ .chat-typing__dot {
1252
+ width: 5px;
1253
+ height: 5px;
1254
+ border-radius: 50%;
1255
+ background: var(--cratis-text-color-secondary);
1256
+ animation: chat-typing-jump 1.2s infinite ease-in-out;
1257
+ }
1258
+
1259
+ .chat-typing__dot:nth-child(2) {
1260
+ animation-delay: 0.18s;
1261
+ }
1262
+
1263
+ .chat-typing__dot:nth-child(3) {
1264
+ animation-delay: 0.36s;
1265
+ }
1266
+
1267
+ @keyframes chat-typing-jump {
1268
+ 0%,
1269
+ 60%,
1270
+ 100% {
1271
+ transform: translateY(0);
1272
+ opacity: 0.45;
1273
+ }
1274
+
1275
+ 30% {
1276
+ transform: translateY(-5px);
1277
+ opacity: 1;
1278
+ }
1279
+ }
1280
+
1281
+ @media (prefers-reduced-motion: reduce) {
1282
+ .chat-typing__dot {
1283
+ animation: none;
1284
+ }
1285
+ }
1286
+
1287
+ }