@cratis/components 4.10.4 → 4.11.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.
@@ -0,0 +1,3122 @@
1
+ @layer cratis-components {
2
+ /* @cratis/components/Canvas/styles — requires @cratis/components/styles/base. Areas: Canvas, Chat, Common, Dialogs, Notifications. */
3
+ /* ── ./Canvas/Canvas.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
+ /* ── Canvas surface ──────────────────────────────────────────────────────── */
8
+
9
+ /* The canvas owns every touch on it: without this the browser claims one- and two-finger gestures
10
+ for its own page scroll and pinch-zoom, so on a tablet the board could neither be panned nor
11
+ zoomed. Inner scroll containers (the fixed editor overlays) still scroll — the browser stops
12
+ combining touch-action at the element that actually scrolls. */
13
+ .canvas-surface {
14
+ touch-action: none;
15
+ /* Individual items opt out of this where they need to (an editable textarea sets
16
+ user-select: text explicitly), but the canvas as a whole must never let a drag start a native
17
+ text/element selection instead of a move or resize — a freshly created item can be dragged
18
+ before its own CSS has had a chance to matter to a gesture that started a pixel outside its
19
+ still-settling bounds, which would otherwise start a native selection. */
20
+ -webkit-user-select: none;
21
+ user-select: none;
22
+ }
23
+
24
+ /* ── Canvas control bar ──────────────────────────────────────────────────── */
25
+ .canvas-controls-glass {
26
+ position: relative;
27
+ display: inline-flex;
28
+ }
29
+
30
+ /* Fallback for large boards where a full-scene compositor capture is too expensive: a frosted pill
31
+ using the GPU-composited backdrop-filter instead of re-rasterizing every interaction frame. */
32
+ .canvas-controls-glass--plain {
33
+ background: color-mix(in srgb, var(--cratis-surface-card) 78%, transparent);
34
+ backdrop-filter: blur(12px);
35
+ -webkit-backdrop-filter: blur(12px);
36
+ border: 1px solid var(--cratis-surface-border);
37
+ border-radius: 999px;
38
+ }
39
+
40
+ /* The glass surface behind supplies the chrome - the bar itself stays clear. */
41
+ .canvas-controls-bar {
42
+ position: relative;
43
+ z-index: 1;
44
+ background: transparent;
45
+ border: 1px solid transparent;
46
+ }
47
+
48
+ .canvas-controls-separator {
49
+ display: inline-block;
50
+ width: 1px;
51
+ height: 20px;
52
+ background: var(--cratis-surface-border);
53
+ border-radius: 1px;
54
+ margin-inline: 2px;
55
+ flex-shrink: 0;
56
+ }
57
+
58
+ /* ── Canvas control icon button ──────────────────────────────────────────── */
59
+ .canvas-controls-icon-btn {
60
+ color: var(--cratis-text-color-secondary);
61
+ background: transparent;
62
+ border: none;
63
+ transition: background 0.15s, color 0.15s;
64
+ }
65
+
66
+ .canvas-controls-icon-btn:hover {
67
+ background: var(--cratis-surface-100);
68
+ color: var(--cratis-text-color);
69
+ }
70
+
71
+ .canvas-controls-icon-btn--active {
72
+ background: var(--cratis-surface-100);
73
+ color: var(--cratis-text-color);
74
+ }
75
+
76
+ /* ── Canvas control zoom percentage button ───────────────────────────────── */
77
+ .canvas-controls-zoom-btn {
78
+ color: var(--cratis-text-color-secondary);
79
+ background: transparent;
80
+ border: none;
81
+ transition: background 0.15s, color 0.15s;
82
+ }
83
+
84
+ .canvas-controls-zoom-btn:hover {
85
+ background: var(--cratis-surface-100);
86
+ color: var(--cratis-text-color);
87
+ }
88
+
89
+ /* ── Canvas minimap ──────────────────────────────────────────────────────── */
90
+ .canvas-minimap {
91
+ background: var(--cratis-surface-hover);
92
+ border: 1px solid var(--cratis-surface-border);
93
+ box-shadow: 0 4px 24px rgba(0, 0, 0, 0.3);
94
+ }
95
+
96
+ .canvas-minimap-viewport {
97
+ border: 2px solid var(--cratis-primary-color);
98
+ background: color-mix(in srgb, var(--cratis-primary-color) 12%, transparent);
99
+ }
100
+
101
+ /* ── ./Canvas/shapes/Note/Note.css ─────────────────────────────────────── */
102
+ /*
103
+ * Copyright (c) Cratis. All rights reserved.
104
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
105
+ */
106
+
107
+ /* Patrick Hand is embedded rather than merely named: it ships on no operating system, and neither
108
+ does the 'Comic Sans MS' fallback on iOS/iPadOS — there the stack fell through to the generic
109
+ `cursive`, which Safari resolves to Snell Roundhand, a formal script that looks nothing like a
110
+ handwritten note. Self-hosting the woff2 subsets (the same ones Google Fonts serves) makes notes
111
+ render identically on every platform. Licensed under the SIL Open Font License — see
112
+ PatrickHand-OFL.txt. */
113
+ @font-face {
114
+ font-family: 'Patrick Hand';
115
+ font-style: normal;
116
+ font-weight: 400;
117
+ font-display: swap;
118
+ src: url('./PatrickHand-latin.woff2') format('woff2');
119
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
120
+ }
121
+
122
+ @font-face {
123
+ font-family: 'Patrick Hand';
124
+ font-style: normal;
125
+ font-weight: 400;
126
+ font-display: swap;
127
+ src: url('./PatrickHand-latin-ext.woff2') format('woff2');
128
+ unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
129
+ }
130
+
131
+ @font-face {
132
+ font-family: 'Patrick Hand';
133
+ font-style: normal;
134
+ font-weight: 400;
135
+ font-display: swap;
136
+ src: url('./PatrickHand-vietnamese.woff2') format('woff2');
137
+ unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
138
+ }
139
+
140
+ .canvas-note {
141
+ /* One source of truth for the note font: the visible text, the textarea being edited and the
142
+ off-screen measurer must resolve to the exact same face or the fitted font size is wrong. */
143
+ --canvas-note-font-family: 'Patrick Hand', 'Comic Sans MS', cursive, sans-serif;
144
+
145
+ position: relative;
146
+ background-color: #fffe9e;
147
+ box-shadow: 2px 3px 8px rgba(0, 0, 0, 0.2), 1px 1px 2px rgba(0, 0, 0, 0.1);
148
+ box-sizing: border-box;
149
+ cursor: grab;
150
+ -webkit-user-select: none;
151
+ user-select: none;
152
+ overflow: visible;
153
+ border-radius: 1px;
154
+ }
155
+
156
+ .canvas-note:active {
157
+ cursor: grabbing;
158
+ }
159
+
160
+ .canvas-note--selected {
161
+ outline: 2px solid #aaa;
162
+ outline-offset: 2px;
163
+ }
164
+
165
+ /* A note showing all of its text grows past its own box and over whatever is below it, so it has to
166
+ read as lifted off the board rather than run into its neighbors. The height it grows to is measured
167
+ from the text and set inline; only the lift and the easing belong here. */
168
+ .canvas-note--expanded {
169
+ box-shadow: 4px 8px 20px rgba(0, 0, 0, 0.3), 1px 2px 4px rgba(0, 0, 0, 0.15);
170
+ transition: height 120ms ease;
171
+ }
172
+
173
+ .canvas-note__text {
174
+ margin: 0;
175
+ padding: 12px;
176
+ width: 100%;
177
+ height: 100%;
178
+ box-sizing: border-box;
179
+ overflow: hidden;
180
+ line-height: 1.4;
181
+ white-space: pre-wrap;
182
+ word-break: break-word;
183
+ font-family: var(--canvas-note-font-family);
184
+ color: #333;
185
+ pointer-events: none;
186
+ display: flex;
187
+ align-items: center;
188
+ justify-content: center;
189
+ text-align: center;
190
+ }
191
+
192
+ .canvas-note__textarea {
193
+ position: absolute;
194
+ top: 0;
195
+ left: 0;
196
+ width: 100%;
197
+ height: 100%;
198
+ background: transparent;
199
+ border: none;
200
+ outline: none;
201
+ resize: none;
202
+ padding: 12px;
203
+ box-sizing: border-box;
204
+ font-family: var(--canvas-note-font-family);
205
+ color: #333;
206
+ cursor: text;
207
+ text-align: center;
208
+ word-break: break-word;
209
+ overflow-wrap: break-word;
210
+ overflow: hidden;
211
+ /* The note itself is user-select: none so drags never start a selection; the text being edited
212
+ must still be selectable. */
213
+ user-select: text;
214
+ -webkit-user-select: text;
215
+ }
216
+
217
+ /* Off-screen element used to measure text fitting. Mirrors the visible text
218
+ styles exactly so the browser computes identical line breaks. */
219
+ .canvas-note__measurer {
220
+ position: absolute;
221
+ visibility: hidden;
222
+ pointer-events: none;
223
+ left: -9999px;
224
+ top: -9999px;
225
+ padding: 0;
226
+ margin: 0;
227
+ box-sizing: border-box;
228
+ font-family: var(--canvas-note-font-family);
229
+ white-space: pre-wrap;
230
+ word-break: break-word;
231
+ overflow-wrap: break-word;
232
+ color: #333;
233
+ }
234
+
235
+ .canvas-note__expand {
236
+ position: absolute;
237
+ bottom: 4px;
238
+ left: 50%;
239
+ transform: translateX(-50%);
240
+ display: flex;
241
+ align-items: center;
242
+ justify-content: center;
243
+ width: 20px;
244
+ height: 20px;
245
+ padding: 0;
246
+ border: 1px solid rgba(0, 0, 0, 0.25);
247
+ border-radius: 50%;
248
+ background: rgba(255, 255, 255, 0.75);
249
+ color: #333;
250
+ font-size: 10px;
251
+ cursor: pointer;
252
+ pointer-events: auto;
253
+ }
254
+
255
+ .canvas-note__handle {
256
+ position: absolute;
257
+ width: 8px;
258
+ height: 8px;
259
+ background: #fff;
260
+ border: 1.5px solid #888;
261
+ border-radius: 1px;
262
+ z-index: 10;
263
+ box-sizing: border-box;
264
+ }
265
+
266
+ /* ── ./Canvas/shapes/Region/Region.css ─────────────────────────────────────── */
267
+ /*
268
+ * Copyright (c) Cratis. All rights reserved.
269
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
270
+ */
271
+
272
+ .canvas-region {
273
+ position: relative;
274
+ border: 2px dashed rgba(100, 150, 240, 0.55);
275
+ border-radius: 4px;
276
+ background: rgba(100, 150, 240, 0.06);
277
+ box-sizing: border-box;
278
+ -webkit-user-select: none;
279
+ user-select: none;
280
+ cursor: grab;
281
+ /*
282
+ * A host typically raises a selected/dragging region above other board items (via CanvasItem's
283
+ * zIndex) so its title bar stays visible over cards/notes drifting under it — but that means this
284
+ * backdrop would otherwise sit on top of, and swallow clicks meant for, whatever is positioned
285
+ * inside or near it. Let presses pass straight through the backdrop to whatever's underneath; only
286
+ * the title bar and resize handles below opt back into being interactive.
287
+ */
288
+ pointer-events: none;
289
+ }
290
+
291
+ .canvas-region:active {
292
+ cursor: grabbing;
293
+ }
294
+
295
+ .canvas-region--selected {
296
+ border-color: rgba(100, 150, 240, 0.9);
297
+ }
298
+
299
+ /*
300
+ * Container for the region's member items. Positioned at the region's top-left (region.x/region.y)
301
+ * with no border or padding, so member coordinates share the same origin used when converting between
302
+ * absolute and region-relative positions. Zero-sized; only its absolutely-positioned children are
303
+ * interactive.
304
+ */
305
+ .canvas-region__items {
306
+ position: absolute;
307
+ top: 0;
308
+ left: 0;
309
+ }
310
+
311
+ /* Title bar sits above the region box */
312
+ .canvas-region__title-bar {
313
+ position: absolute;
314
+ top: -28px;
315
+ left: -2px;
316
+ height: 28px;
317
+ padding: 0 8px;
318
+ background: rgba(100, 150, 240, 0.72);
319
+ border-radius: 4px 4px 0 0;
320
+ display: flex;
321
+ align-items: center;
322
+ cursor: grab;
323
+ white-space: nowrap;
324
+ pointer-events: auto;
325
+ }
326
+
327
+ .canvas-region__title-bar:active {
328
+ cursor: grabbing;
329
+ }
330
+
331
+ .canvas-region__name {
332
+ font-size: 12px;
333
+ font-family: Inter, Helvetica, Arial, sans-serif;
334
+ font-weight: 600;
335
+ color: #fff;
336
+ pointer-events: none;
337
+ }
338
+
339
+ .canvas-region__name-input {
340
+ font-size: 12px;
341
+ font-family: Inter, Helvetica, Arial, sans-serif;
342
+ font-weight: 600;
343
+ color: #fff;
344
+ background: transparent;
345
+ border: none;
346
+ border-bottom: 1px solid rgba(255, 255, 255, 0.6);
347
+ outline: none;
348
+ padding: 0;
349
+ min-width: 80px;
350
+ cursor: text;
351
+ }
352
+
353
+ .canvas-region__handle {
354
+ position: absolute;
355
+ width: 8px;
356
+ height: 8px;
357
+ background: #fff;
358
+ border: 1.5px solid rgba(100, 150, 240, 0.8);
359
+ border-radius: 1px;
360
+ z-index: 10;
361
+ box-sizing: border-box;
362
+ pointer-events: auto;
363
+ }
364
+
365
+ /* ── ./Chat/ChatConversation.css ─────────────────────────────────────── */
366
+ /* Copyright (c) Cratis. All rights reserved. */
367
+ /* Licensed under the MIT license. See LICENSE file in the project root for full license information. */
368
+
369
+ /* Every color comes from the --cratis-* token seam so the conversation follows whatever theme the
370
+ host runs. */
371
+ .cratis-chat-conversation {
372
+ flex: 1;
373
+ min-height: 0;
374
+ display: flex;
375
+ flex-direction: column;
376
+ }
377
+
378
+ .cratis-chat-conversation__messages {
379
+ flex: 1;
380
+ min-height: 0;
381
+ overflow-y: auto;
382
+ padding: 0.75rem;
383
+ display: flex;
384
+ flex-direction: column;
385
+ gap: 0.875rem;
386
+ }
387
+
388
+ .cratis-chat-conversation__empty {
389
+ margin: 0;
390
+ padding: 1rem 0;
391
+ text-align: center;
392
+ font-size: 0.8125rem;
393
+ color: var(--cratis-text-color-secondary);
394
+ }
395
+
396
+ .cratis-chat-message {
397
+ display: flex;
398
+ flex-direction: column;
399
+ gap: 0.25rem;
400
+ }
401
+
402
+ .cratis-chat-message--continuation {
403
+ margin-top: -0.625rem;
404
+ }
405
+
406
+ .cratis-chat-message__author {
407
+ display: flex;
408
+ align-items: center;
409
+ gap: 0.5rem;
410
+ }
411
+
412
+ .cratis-chat-message__author-name {
413
+ font-size: 0.8125rem;
414
+ font-weight: 600;
415
+ color: var(--cratis-text-color);
416
+ }
417
+
418
+ .cratis-chat-message__content {
419
+ display: flex;
420
+ flex-direction: column;
421
+ gap: 0.125rem;
422
+ }
423
+
424
+ .cratis-chat-message__panel {
425
+ position: relative;
426
+ background: var(--cratis-surface-section);
427
+ border: 1px solid var(--cratis-surface-border);
428
+ border-radius: var(--cratis-border-radius);
429
+ padding: 0.5rem 0.625rem;
430
+ }
431
+
432
+ .cratis-chat-message__body {
433
+ margin: 0;
434
+ font-size: 0.8125rem;
435
+ line-height: 1.45;
436
+ color: var(--cratis-text-color);
437
+ white-space: pre-wrap;
438
+ overflow-wrap: anywhere;
439
+ }
440
+
441
+ .cratis-chat-message__mention {
442
+ color: var(--cratis-highlight-text-color);
443
+ background: var(--cratis-highlight-bg);
444
+ border-radius: 4px;
445
+ padding: 0 0.2em;
446
+ font-weight: 600;
447
+ }
448
+
449
+ .cratis-chat-message__mention[data-kind='agent'] {
450
+ text-decoration: underline dotted;
451
+ text-underline-offset: 2px;
452
+ }
453
+
454
+ /* The action buttons hang over the message's top-right corner and only show while the message is
455
+ hovered — or while one of them holds keyboard focus, which is why they hide through opacity
456
+ rather than display (a display:none button can never receive focus in the first place). */
457
+ .cratis-chat-message__actions {
458
+ position: absolute;
459
+ top: -0.75rem;
460
+ right: 0.375rem;
461
+ display: flex;
462
+ gap: 2px;
463
+ padding: 2px;
464
+ background: var(--cratis-surface-overlay);
465
+ border: 1px solid var(--cratis-surface-border);
466
+ border-radius: 6px;
467
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
468
+ opacity: 0;
469
+ pointer-events: none;
470
+ transition: opacity 0.12s;
471
+ }
472
+
473
+ .cratis-chat-message:hover .cratis-chat-message__actions,
474
+ .cratis-chat-message__actions:focus-within {
475
+ opacity: 1;
476
+ pointer-events: auto;
477
+ }
478
+
479
+ .cratis-chat-message__action {
480
+ background: transparent;
481
+ border: none;
482
+ border-radius: 4px;
483
+ width: 1.5rem;
484
+ height: 1.5rem;
485
+ display: flex;
486
+ align-items: center;
487
+ justify-content: center;
488
+ cursor: pointer;
489
+ color: var(--cratis-text-color-secondary);
490
+ font-size: 0.75rem;
491
+ transition:
492
+ background 0.12s,
493
+ color 0.12s;
494
+ }
495
+
496
+ .cratis-chat-message__action:hover {
497
+ background: var(--cratis-surface-hover);
498
+ color: var(--cratis-text-color);
499
+ }
500
+
501
+ .cratis-chat-message__time {
502
+ font-size: 0.6875rem;
503
+ color: var(--cratis-text-color-secondary);
504
+ padding-left: 0.125rem;
505
+ }
506
+
507
+ /* ── ./Chat/ChatSidebar.css ─────────────────────────────────────── */
508
+ /* Copyright (c) Cratis. All rights reserved. */
509
+ /* Licensed under the MIT license. See LICENSE file in the project root for full license information. */
510
+
511
+ /* The overlay layer the sidebar portals into. It is transparent and does not intercept pointer
512
+ events by default (`modal={false}`, the default) — a chat lives next to the work, so the rest
513
+ of the page stays visible and interactive behind it. Only `data-modal='true'` paints a backdrop
514
+ and blocks clicks outside the panel, matching a dialog's masking. */
515
+ .cratis-chat-sidebar__backdrop {
516
+ position: fixed;
517
+ inset: 0;
518
+ z-index: var(--cratis-z-index-dialog);
519
+ pointer-events: none;
520
+ }
521
+
522
+ .cratis-chat-sidebar__backdrop[data-modal='true'] {
523
+ background: var(--cratis-maskbg);
524
+ pointer-events: auto;
525
+ }
526
+
527
+ .cratis-chat-sidebar__backdrop[data-entering] {
528
+ animation: cratis-chat-sidebar-backdrop-fade 160ms ease-out;
529
+ }
530
+
531
+ .cratis-chat-sidebar__backdrop[data-exiting] {
532
+ animation: cratis-chat-sidebar-backdrop-fade 160ms reverse ease-in;
533
+ }
534
+
535
+ @keyframes cratis-chat-sidebar-backdrop-fade {
536
+ from {
537
+ opacity: 0;
538
+ }
539
+
540
+ to {
541
+ opacity: 1;
542
+ }
543
+ }
544
+
545
+ /* Baseline chat sidebar — a panel needs a visible surface even before a theme preset is applied,
546
+ so this ships a token-based default that a preset or `pt` can still override. Every color comes
547
+ from the --cratis-* token seam so the sidebar follows whatever theme the host runs. */
548
+ .cratis-chat-sidebar {
549
+ position: fixed;
550
+ top: 0;
551
+ bottom: 0;
552
+ right: 0;
553
+ display: flex;
554
+ flex-direction: column;
555
+ background: var(--cratis-surface-overlay);
556
+ color: var(--cratis-text-color);
557
+ border-left: 1px solid var(--cratis-surface-border);
558
+ box-shadow: 0 0 24px rgba(0, 0, 0, 0.35);
559
+ pointer-events: auto;
560
+ outline: none;
561
+ transform: translateX(0);
562
+ transition: transform 200ms ease;
563
+ }
564
+
565
+ .cratis-chat-sidebar[data-entering] {
566
+ transform: translateX(100%);
567
+ animation: cratis-chat-sidebar-slide-in 200ms ease-out forwards;
568
+ }
569
+
570
+ .cratis-chat-sidebar[data-exiting] {
571
+ animation: cratis-chat-sidebar-slide-in 200ms ease-in reverse;
572
+ }
573
+
574
+ @keyframes cratis-chat-sidebar-slide-in {
575
+ from {
576
+ transform: translateX(100%);
577
+ }
578
+
579
+ to {
580
+ transform: translateX(0);
581
+ }
582
+ }
583
+
584
+ .cratis-chat-sidebar[data-position='left'] {
585
+ left: 0;
586
+ right: auto;
587
+ border-left: none;
588
+ border-right: 1px solid var(--cratis-surface-border);
589
+ }
590
+
591
+ .cratis-chat-sidebar[data-position='left'][data-entering] {
592
+ animation-name: cratis-chat-sidebar-slide-in-left;
593
+ }
594
+
595
+ .cratis-chat-sidebar[data-position='left'][data-exiting] {
596
+ animation-name: cratis-chat-sidebar-slide-in-left;
597
+ }
598
+
599
+ @keyframes cratis-chat-sidebar-slide-in-left {
600
+ from {
601
+ transform: translateX(-100%);
602
+ }
603
+
604
+ to {
605
+ transform: translateX(0);
606
+ }
607
+ }
608
+
609
+ .cratis-chat-sidebar__header {
610
+ display: flex;
611
+ align-items: center;
612
+ gap: 0.5rem;
613
+ padding: 0.75rem 1rem;
614
+ border-bottom: 1px solid var(--cratis-surface-border);
615
+ flex-shrink: 0;
616
+ }
617
+
618
+ .cratis-chat-sidebar__title {
619
+ flex: 1;
620
+ min-width: 0;
621
+ font-size: 0.9375rem;
622
+ font-weight: 600;
623
+ overflow: hidden;
624
+ text-overflow: ellipsis;
625
+ white-space: nowrap;
626
+ }
627
+
628
+ .cratis-chat-sidebar__title--pending {
629
+ font-style: italic;
630
+ font-weight: 400;
631
+ color: var(--cratis-text-color-secondary);
632
+ }
633
+
634
+ .cratis-chat-sidebar__back,
635
+ .cratis-chat-sidebar__close {
636
+ background: transparent;
637
+ border: none;
638
+ border-radius: var(--cratis-border-radius);
639
+ width: 1.75rem;
640
+ height: 1.75rem;
641
+ display: flex;
642
+ align-items: center;
643
+ justify-content: center;
644
+ cursor: pointer;
645
+ color: var(--cratis-text-color-secondary);
646
+ font-size: 0.875rem;
647
+ flex-shrink: 0;
648
+ transition:
649
+ color 0.12s,
650
+ background 0.12s;
651
+ }
652
+
653
+ .cratis-chat-sidebar__back:hover,
654
+ .cratis-chat-sidebar__close:hover {
655
+ background: var(--cratis-surface-hover);
656
+ color: var(--cratis-text-color);
657
+ }
658
+
659
+ .cratis-chat-sidebar__content {
660
+ flex: 1;
661
+ min-height: 0;
662
+ display: flex;
663
+ flex-direction: column;
664
+ overflow: hidden;
665
+ }
666
+
667
+ /* ── ./Chat/ChatTopicList.css ─────────────────────────────────────── */
668
+ /* Copyright (c) Cratis. All rights reserved. */
669
+ /* Licensed under the MIT license. See LICENSE file in the project root for full license information. */
670
+
671
+ /* Every color comes from the --cratis-* token seam so the list follows whatever theme the host
672
+ runs. */
673
+ .cratis-chat-topics {
674
+ flex: 1;
675
+ min-height: 0;
676
+ display: flex;
677
+ flex-direction: column;
678
+ gap: 0.5rem;
679
+ padding: 0.75rem;
680
+ overflow-y: auto;
681
+ }
682
+
683
+ .cratis-chat-topics__start {
684
+ align-self: flex-start;
685
+ display: inline-flex;
686
+ align-items: center;
687
+ gap: 0.4rem;
688
+ background: var(--cratis-primary-color);
689
+ color: var(--cratis-primary-color-text);
690
+ border: none;
691
+ border-radius: var(--cratis-border-radius);
692
+ padding: 0.4rem 0.75rem;
693
+ font-size: 0.8125rem;
694
+ cursor: pointer;
695
+ transition: background 0.12s;
696
+ }
697
+
698
+ .cratis-chat-topics__start:hover {
699
+ background: var(--cratis-primary-600);
700
+ }
701
+
702
+ .cratis-chat-topics__empty {
703
+ margin: 0;
704
+ padding: 1rem 0;
705
+ text-align: center;
706
+ font-size: 0.8125rem;
707
+ color: var(--cratis-text-color-secondary);
708
+ }
709
+
710
+ .cratis-chat-topics__list {
711
+ display: flex;
712
+ flex-direction: column;
713
+ gap: 2px;
714
+ margin: 0;
715
+ padding: 0;
716
+ list-style: none;
717
+ }
718
+
719
+ .cratis-chat-topics__topic {
720
+ display: flex;
721
+ align-items: center;
722
+ gap: 0.625rem;
723
+ width: 100%;
724
+ padding: 0.5rem 0.625rem;
725
+ background: transparent;
726
+ border: none;
727
+ border-radius: var(--cratis-border-radius);
728
+ text-align: left;
729
+ cursor: pointer;
730
+ color: var(--cratis-text-color);
731
+ transition: background 0.12s;
732
+ }
733
+
734
+ .cratis-chat-topics__topic:hover {
735
+ background: var(--cratis-surface-hover);
736
+ }
737
+
738
+ .cratis-chat-topics__details {
739
+ flex: 1;
740
+ min-width: 0;
741
+ display: flex;
742
+ flex-direction: column;
743
+ gap: 0.125rem;
744
+ }
745
+
746
+ .cratis-chat-topics__name {
747
+ font-size: 0.875rem;
748
+ font-weight: 600;
749
+ overflow: hidden;
750
+ text-overflow: ellipsis;
751
+ white-space: nowrap;
752
+ }
753
+
754
+ .cratis-chat-topics__name--pending {
755
+ font-style: italic;
756
+ font-weight: 400;
757
+ color: var(--cratis-text-color-secondary);
758
+ }
759
+
760
+ .cratis-chat-topics__started-by {
761
+ font-size: 0.75rem;
762
+ color: var(--cratis-text-color-secondary);
763
+ overflow: hidden;
764
+ text-overflow: ellipsis;
765
+ white-space: nowrap;
766
+ }
767
+
768
+ .cratis-chat-topics__activity {
769
+ align-self: flex-start;
770
+ flex-shrink: 0;
771
+ font-size: 0.6875rem;
772
+ color: var(--cratis-text-color-secondary);
773
+ }
774
+
775
+ /* ── ./Chat/Kit/Avatar/PersonAvatarCircle.css ─────────────────────────────────────── */
776
+ /* Copyright (c) Cratis. All rights reserved. */
777
+ /* Licensed under the MIT license. See LICENSE file in the project root for full license information. */
778
+
779
+ .person-avatar-circle {
780
+ display: inline-flex;
781
+ align-items: center;
782
+ justify-content: center;
783
+ border-radius: 50%;
784
+ flex-shrink: 0;
785
+ overflow: hidden;
786
+ }
787
+
788
+ .person-avatar-circle-image {
789
+ display: block;
790
+ width: 100%;
791
+ height: 100%;
792
+ object-fit: cover;
793
+ /* Avatars sit on draggable elements (chat bubbles, cards) — the browser's native image drag
794
+ must never hijack the pointer gesture. draggable={false} covers most engines; WebKit also
795
+ needs the CSS opt-out. */
796
+ -webkit-user-drag: none;
797
+ user-select: none;
798
+ -webkit-user-select: none;
799
+ }
800
+
801
+ .person-avatar-circle-initials {
802
+ color: #fff;
803
+ font-weight: 700;
804
+ letter-spacing: 0.02em;
805
+ font-family: system-ui, sans-serif;
806
+ }
807
+
808
+ /* ── ./Chat/Kit/Chat.css ─────────────────────────────────────── */
809
+ /* Copyright (c) Cratis. All rights reserved. */
810
+ /* Licensed under the MIT license. See LICENSE file in the project root for full license information. */
811
+
812
+ /* Every color comes from the --cratis-* token seam, so the conversation follows whatever theme
813
+ the host runs — light, dark, or its own. */
814
+
815
+ /* ── Chat panel (floating dropdown from the chat button) ─────────────────── */
816
+ .chat-panel {
817
+ position: absolute;
818
+ top: calc(100% + 6px);
819
+ left: 0;
820
+ width: 280px;
821
+ background: var(--cratis-surface-overlay);
822
+ border: 1px solid var(--cratis-surface-border);
823
+ border-radius: 12px;
824
+ box-shadow: 0 12px 32px rgba(0, 0, 0, 0.35);
825
+ display: flex;
826
+ flex-direction: column;
827
+ z-index: 300;
828
+ overflow: hidden;
829
+ }
830
+
831
+ /* ── Panel header ─────────────────────────────────────────────────────────── */
832
+ .chat-panel-header {
833
+ display: flex;
834
+ align-items: center;
835
+ justify-content: space-between;
836
+ padding: 10px 14px 8px;
837
+ border-bottom: 1px solid var(--cratis-surface-border);
838
+ flex-shrink: 0;
839
+ }
840
+
841
+ .chat-panel-title {
842
+ font-size: 13px;
843
+ font-weight: 600;
844
+ color: var(--cratis-text-color);
845
+ }
846
+
847
+ .chat-panel-close {
848
+ background: none;
849
+ border: none;
850
+ font-size: 20px;
851
+ line-height: 1;
852
+ cursor: pointer;
853
+ color: var(--cratis-text-color-secondary);
854
+ padding: 0 2px;
855
+ border-radius: 4px;
856
+ transition: color 0.12s;
857
+ }
858
+
859
+ .chat-panel-close:hover {
860
+ color: var(--cratis-text-color);
861
+ }
862
+
863
+ /* ── Scrollable messages area ─────────────────────────────────────────────── */
864
+ .chat-panel-messages {
865
+ flex: 1;
866
+ max-height: 260px;
867
+ overflow-y: auto;
868
+ padding: 10px 12px;
869
+ display: flex;
870
+ flex-direction: column;
871
+ gap: 14px;
872
+ }
873
+
874
+ .chat-panel-empty {
875
+ font-size: 12px;
876
+ color: var(--cratis-text-color-secondary);
877
+ text-align: center;
878
+ margin: 16px 0;
879
+ }
880
+
881
+ /* ── Individual chat bubble ───────────────────────────────────────────────── */
882
+ .chat-bubble {
883
+ display: flex;
884
+ flex-direction: column;
885
+ gap: 4px;
886
+ }
887
+
888
+ /* Continuation bubbles (same author, no avatar row) get tighter spacing. */
889
+ .chat-bubble--continuation {
890
+ margin-top: -8px;
891
+ }
892
+
893
+ .chat-bubble-author {
894
+ display: flex;
895
+ align-items: center;
896
+ gap: 6px;
897
+ }
898
+
899
+ .chat-bubble-name {
900
+ font-size: 11px;
901
+ font-weight: 600;
902
+ color: var(--cratis-text-color);
903
+ }
904
+
905
+ .chat-bubble-body {
906
+ display: flex;
907
+ flex-direction: column;
908
+ gap: 3px;
909
+ padding-left: 28px;
910
+ }
911
+
912
+ .chat-bubble-text {
913
+ background: var(--cratis-surface-section);
914
+ border-radius: 0 10px 10px 10px;
915
+ padding: 7px 10px;
916
+ margin: 0;
917
+ font-size: 12px;
918
+ color: var(--cratis-text-color);
919
+ line-height: 1.45;
920
+ white-space: pre-wrap;
921
+ word-break: break-word;
922
+ }
923
+
924
+ .chat-bubble-time {
925
+ font-size: 10px;
926
+ color: var(--cratis-text-color-secondary);
927
+ padding-left: 2px;
928
+ }
929
+
930
+ /* ── Docked variant (the chat sidebar provides the frame and the heading) ── */
931
+ .chat-panel--docked {
932
+ position: static;
933
+ width: 100%;
934
+ height: 100%;
935
+ border: none;
936
+ border-radius: 0;
937
+ box-shadow: none;
938
+ }
939
+
940
+ .chat-panel--docked .chat-panel-messages {
941
+ max-height: none;
942
+ }
943
+
944
+ /* ── ./Chat/Kit/ChatBubble.css ─────────────────────────────────────── */
945
+ /* Copyright (c) Cratis. All rights reserved. */
946
+ /* Licensed under the MIT license. See LICENSE file in the project root for full license information. */
947
+
948
+ /* ── Thinking bubbles (small dots at top-right of avatar) ────────────────── */
949
+
950
+ @keyframes thinking-bubble-pop-in {
951
+ 0% {
952
+ transform: scale(0);
953
+ opacity: 0;
954
+ }
955
+ 60% {
956
+ transform: scale(1.3);
957
+ opacity: 1;
958
+ }
959
+ 100% {
960
+ transform: scale(1);
961
+ opacity: 1;
962
+ }
963
+ }
964
+
965
+ @keyframes thinking-bubble-pop-out {
966
+ 0% {
967
+ transform: scale(1);
968
+ opacity: 1;
969
+ }
970
+ 100% {
971
+ transform: scale(0);
972
+ opacity: 0;
973
+ }
974
+ }
975
+
976
+ .thinking-bubbles {
977
+ position: absolute;
978
+ pointer-events: none;
979
+ top: 0;
980
+ left: 0;
981
+ }
982
+
983
+ .thinking-dot {
984
+ border-radius: 50%;
985
+ position: absolute;
986
+ transform: scale(0);
987
+ opacity: 0;
988
+ }
989
+
990
+ /* ── Enter animation ─────────────────────────────────────────────────────── */
991
+
992
+ .thinking-bubbles--entering .thinking-dot--1 {
993
+ animation: thinking-bubble-pop-in 0.25s ease-out forwards;
994
+ }
995
+
996
+ .thinking-bubbles--entering .thinking-dot--2 {
997
+ animation: thinking-bubble-pop-in 0.25s ease-out 0.1s forwards;
998
+ }
999
+
1000
+ /* ── Exit animation ──────────────────────────────────────────────────────── */
1001
+
1002
+ .thinking-bubbles--exiting .thinking-dot--2 {
1003
+ transform: scale(1);
1004
+ opacity: 1;
1005
+ animation: thinking-bubble-pop-out 0.2s ease-in forwards;
1006
+ }
1007
+
1008
+ .thinking-bubbles--exiting .thinking-dot--1 {
1009
+ transform: scale(1);
1010
+ opacity: 1;
1011
+ animation: thinking-bubble-pop-out 0.2s ease-in 0.06s forwards;
1012
+ }
1013
+
1014
+ /* ── ./Chat/Kit/ChatComposer.css ─────────────────────────────────────── */
1015
+ /* Copyright (c) Cratis. All rights reserved. */
1016
+ /* Licensed under the MIT license. See LICENSE file in the project root for full license information. */
1017
+
1018
+ /* Every color comes from the --cratis-* token seam, so the compose row follows whatever theme the
1019
+ host runs. */
1020
+ .chat-composer {
1021
+ position: relative;
1022
+ display: flex;
1023
+ align-items: flex-end;
1024
+ gap: 6px;
1025
+ padding: 8px 10px 10px;
1026
+ border-top: 1px solid var(--cratis-surface-border);
1027
+ flex-shrink: 0;
1028
+ }
1029
+
1030
+ .chat-composer__input {
1031
+ flex: 1;
1032
+ background: var(--cratis-surface-0);
1033
+ border: 1px solid var(--cratis-surface-border);
1034
+ border-radius: 8px;
1035
+ color: var(--cratis-text-color);
1036
+ font-size: 12px;
1037
+ padding: 6px 8px;
1038
+ resize: none;
1039
+ outline: none;
1040
+ font-family: inherit;
1041
+ line-height: 1.4;
1042
+ transition: border-color 0.12s;
1043
+ }
1044
+
1045
+ .chat-composer__input::placeholder {
1046
+ color: var(--cratis-text-color-secondary);
1047
+ }
1048
+
1049
+ .chat-composer__input:focus {
1050
+ border-color: var(--cratis-primary-color);
1051
+ }
1052
+
1053
+ .chat-composer__emoji-toggle {
1054
+ background: var(--cratis-surface-100);
1055
+ border: 1px solid var(--cratis-surface-border);
1056
+ border-radius: 8px;
1057
+ width: 32px;
1058
+ height: 32px;
1059
+ display: flex;
1060
+ align-items: center;
1061
+ justify-content: center;
1062
+ cursor: pointer;
1063
+ color: var(--cratis-text-color-secondary);
1064
+ font-size: 15px;
1065
+ line-height: 1;
1066
+ flex-shrink: 0;
1067
+ transition:
1068
+ background 0.12s,
1069
+ color 0.12s;
1070
+ }
1071
+
1072
+ .chat-composer__emoji-toggle:hover {
1073
+ background: var(--cratis-surface-hover);
1074
+ color: var(--cratis-text-color);
1075
+ }
1076
+
1077
+ .chat-composer__send {
1078
+ background: var(--cratis-primary-color);
1079
+ border: none;
1080
+ border-radius: 8px;
1081
+ width: 32px;
1082
+ height: 32px;
1083
+ display: flex;
1084
+ align-items: center;
1085
+ justify-content: center;
1086
+ cursor: pointer;
1087
+ color: var(--cratis-primary-color-text);
1088
+ flex-shrink: 0;
1089
+ transition:
1090
+ background 0.12s,
1091
+ opacity 0.12s;
1092
+ }
1093
+
1094
+ .chat-composer__send:hover:not(:disabled) {
1095
+ background: var(--cratis-primary-600);
1096
+ }
1097
+
1098
+ .chat-composer__send:disabled {
1099
+ opacity: 0.35;
1100
+ cursor: default;
1101
+ }
1102
+
1103
+ /* ── ./Chat/Kit/ChatMessageBubble.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
+ /* Every color comes from the --cratis-* token seam, so the bubble follows whatever theme the host
1108
+ runs. */
1109
+
1110
+ /* The positioning context for the corner buttons — the message panel itself, so they straddle its
1111
+ bottom edge rather than sitting below whatever else the bubble carries. */
1112
+ .chat-bubble-panel {
1113
+ position: relative;
1114
+ }
1115
+
1116
+ /* Room below the panel for the half of the buttons that hangs outside it, so they never land on the
1117
+ timestamp or the reactions underneath. Only when there are buttons: a bubble without them keeps its
1118
+ old height. */
1119
+ .chat-bubble-panel--with-actions {
1120
+ margin-bottom: 13px;
1121
+ }
1122
+
1123
+ /* And a little room inside, so the half that overlaps the panel sits on its padding rather than on
1124
+ the last line of the message. */
1125
+ .chat-bubble-panel--with-actions .chat-bubble-text {
1126
+ padding-bottom: 14px;
1127
+ }
1128
+
1129
+ /* Straddling the panel's bottom edge in its lower-right corner — half in, half out, so they read as
1130
+ belonging to the message without taking a line of their own inside it. */
1131
+ .chat-bubble-actions {
1132
+ position: absolute;
1133
+ right: 10px;
1134
+ bottom: -11px;
1135
+ display: flex;
1136
+ align-items: center;
1137
+ gap: 6px;
1138
+ }
1139
+
1140
+ /* A stroke around a solid fill: the button lies across the bubble's edge, so a translucent fill would
1141
+ let that edge run straight through it and stop it reading as one round control. The fill is the
1142
+ panel's own surface, which is what the half hanging outside is sitting on. */
1143
+ .chat-bubble-action {
1144
+ display: flex;
1145
+ align-items: center;
1146
+ justify-content: center;
1147
+ width: 22px;
1148
+ height: 22px;
1149
+ padding: 0;
1150
+ border: 1px solid var(--cratis-surface-border);
1151
+ border-radius: 999px;
1152
+ background: var(--cratis-surface-overlay);
1153
+ color: var(--cratis-text-color-secondary);
1154
+ font-size: 12px;
1155
+ line-height: 1;
1156
+ cursor: pointer;
1157
+ }
1158
+
1159
+ .chat-bubble-action:hover,
1160
+ .chat-bubble-action:focus-visible {
1161
+ border-color: var(--cratis-text-color-secondary);
1162
+ background: var(--cratis-surface-hover);
1163
+ color: var(--cratis-text-color);
1164
+ }
1165
+
1166
+ /* Once a person has reacted, the button shows their emoji as real state rather than a hover-only
1167
+ affordance, so it stays visible without hovering. */
1168
+ .chat-bubble-action--own-reaction {
1169
+ border-color: var(--cratis-text-color-secondary);
1170
+ background: var(--cratis-surface-hover);
1171
+ color: var(--cratis-text-color);
1172
+ font-size: 13px;
1173
+ }
1174
+
1175
+ /* ── ./Chat/Kit/Emoji/EmojiPicker.css ─────────────────────────────────────── */
1176
+ /* Copyright (c) Cratis. All rights reserved. */
1177
+ /* Licensed under the MIT license. See LICENSE file in the project root for full license information. */
1178
+
1179
+ /* Every color comes from the --cratis-* token seam, so the picker follows whatever theme the host
1180
+ runs — the same convention ReactionPicker.css and MentionSuggestions.css
1181
+ follow. */
1182
+ .emoji-picker {
1183
+ display: flex;
1184
+ flex-direction: column;
1185
+ width: 296px;
1186
+ background: var(--cratis-surface-overlay);
1187
+ border: 1px solid var(--cratis-surface-border);
1188
+ border-radius: 12px;
1189
+ box-shadow: 0 12px 32px rgba(0, 0, 0, 0.35);
1190
+ overflow: hidden;
1191
+ }
1192
+
1193
+ .emoji-picker__grid {
1194
+ display: grid;
1195
+ grid-template-columns: repeat(8, 1fr);
1196
+ gap: 2px;
1197
+ height: 232px;
1198
+ overflow-y: auto;
1199
+ padding: 8px;
1200
+ overscroll-behavior: contain;
1201
+ }
1202
+
1203
+ .emoji-picker__emoji {
1204
+ display: flex;
1205
+ align-items: center;
1206
+ justify-content: center;
1207
+ width: 100%;
1208
+ aspect-ratio: 1;
1209
+ padding: 0;
1210
+ border: none;
1211
+ border-radius: 7px;
1212
+ background: transparent;
1213
+ font-size: 20px;
1214
+ line-height: 1;
1215
+ cursor: pointer;
1216
+ }
1217
+
1218
+ .emoji-picker__emoji:hover,
1219
+ .emoji-picker__emoji:focus-visible {
1220
+ background: var(--cratis-surface-hover);
1221
+ }
1222
+
1223
+ .emoji-picker__emoji--selected {
1224
+ background: var(--cratis-highlight-bg);
1225
+ }
1226
+
1227
+ .emoji-picker__tabs {
1228
+ display: flex;
1229
+ gap: 2px;
1230
+ padding: 6px;
1231
+ border-top: 1px solid var(--cratis-surface-border);
1232
+ background: var(--cratis-surface-ground);
1233
+ }
1234
+
1235
+ .emoji-picker__tab {
1236
+ display: flex;
1237
+ flex: 1;
1238
+ align-items: center;
1239
+ justify-content: center;
1240
+ height: 26px;
1241
+ padding: 0;
1242
+ border: none;
1243
+ border-radius: 6px;
1244
+ background: transparent;
1245
+ /* Muted until it is the section being shown, so the row reads as one control rather than nine
1246
+ competing emojis. */
1247
+ filter: grayscale(1);
1248
+ opacity: 0.55;
1249
+ font-size: 15px;
1250
+ line-height: 1;
1251
+ cursor: pointer;
1252
+ }
1253
+
1254
+ .emoji-picker__tab:hover {
1255
+ opacity: 0.85;
1256
+ }
1257
+
1258
+ .emoji-picker__tab--current {
1259
+ background: var(--cratis-surface-hover);
1260
+ filter: none;
1261
+ opacity: 1;
1262
+ }
1263
+
1264
+ /* ── ./Chat/Kit/FailedReply.css ─────────────────────────────────────── */
1265
+ /* Copyright (c) Cratis. All rights reserved. */
1266
+ /* Licensed under the MIT license. See LICENSE file in the project root for full license information. */
1267
+
1268
+ /* Every color comes from the --cratis-* token seam, so the failure line follows whatever theme
1269
+ the host runs. The warning tint is mixed from the orange token because
1270
+ the seam ships no alpha variants. */
1271
+ .chat-failed-reply {
1272
+ display: flex;
1273
+ flex-direction: column;
1274
+ gap: 4px;
1275
+ padding: 6px 8px;
1276
+ border-left: 2px solid var(--cratis-orange-500);
1277
+ background: color-mix(in srgb, var(--cratis-orange-500) 10%, transparent);
1278
+ border-radius: 0 6px 6px 0;
1279
+ }
1280
+
1281
+ .chat-failed-reply__line {
1282
+ display: flex;
1283
+ align-items: center;
1284
+ gap: 6px;
1285
+ }
1286
+
1287
+ .chat-failed-reply__icon {
1288
+ font-size: 12px;
1289
+ color: var(--cratis-orange-500);
1290
+ }
1291
+
1292
+ .chat-failed-reply__text {
1293
+ font-size: 12px;
1294
+ color: var(--cratis-text-color);
1295
+ }
1296
+
1297
+ .chat-failed-reply__actions {
1298
+ display: flex;
1299
+ align-items: center;
1300
+ gap: 10px;
1301
+ padding-left: 18px;
1302
+ }
1303
+
1304
+ .chat-failed-reply__action {
1305
+ background: none;
1306
+ border: none;
1307
+ padding: 0;
1308
+ font-size: 11px;
1309
+ color: var(--cratis-text-color);
1310
+ text-decoration: underline;
1311
+ cursor: pointer;
1312
+ }
1313
+
1314
+ .chat-failed-reply__action:hover {
1315
+ color: var(--cratis-text-color);
1316
+ }
1317
+
1318
+ .chat-failed-reply__detail {
1319
+ margin: 0;
1320
+ padding: 12px;
1321
+ border-radius: 6px;
1322
+ background: var(--cratis-surface-100);
1323
+ color: var(--cratis-text-color);
1324
+ font-size: 12px;
1325
+ line-height: 1.5;
1326
+ white-space: pre-wrap;
1327
+ word-break: break-word;
1328
+ max-height: 24rem;
1329
+ overflow: auto;
1330
+ }
1331
+
1332
+ /* ── ./Chat/Kit/Mentions/MentionSuggestions.css ─────────────────────────────────────── */
1333
+ /* Copyright (c) Cratis. All rights reserved. */
1334
+ /* Licensed under the MIT license. See LICENSE file in the project root for full license information. */
1335
+
1336
+ /* Sits above the compose row inside the chat panel. Every color comes from the --cratis-* token
1337
+ seam, so the list follows whatever theme the host runs. */
1338
+ .mention-suggestions {
1339
+ position: absolute;
1340
+ bottom: calc(100% + 4px);
1341
+ left: 8px;
1342
+ right: 8px;
1343
+ max-height: 180px;
1344
+ overflow-y: auto;
1345
+ background: var(--cratis-surface-overlay);
1346
+ border: 1px solid var(--cratis-surface-border);
1347
+ border-radius: 10px;
1348
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
1349
+ padding: 4px;
1350
+ display: flex;
1351
+ flex-direction: column;
1352
+ gap: 2px;
1353
+ z-index: 400;
1354
+ }
1355
+
1356
+ .mention-suggestions__item {
1357
+ display: flex;
1358
+ align-items: center;
1359
+ gap: 8px;
1360
+ width: 100%;
1361
+ padding: 5px 8px;
1362
+ border: none;
1363
+ border-radius: 7px;
1364
+ background: transparent;
1365
+ color: var(--cratis-text-color);
1366
+ font-size: 12px;
1367
+ text-align: left;
1368
+ cursor: pointer;
1369
+ }
1370
+
1371
+ .mention-suggestions__item--highlighted {
1372
+ background: var(--cratis-highlight-bg);
1373
+ color: var(--cratis-highlight-text-color);
1374
+ }
1375
+
1376
+ .mention-suggestions__name {
1377
+ overflow: hidden;
1378
+ text-overflow: ellipsis;
1379
+ white-space: nowrap;
1380
+ }
1381
+
1382
+ /* ── ./Chat/Kit/MessageReactions.css ─────────────────────────────────────── */
1383
+ /* Copyright (c) Cratis. All rights reserved. */
1384
+ /* Licensed under the MIT license. See LICENSE file in the project root for full license information. */
1385
+
1386
+ /* Every color comes from the --cratis-* token seam, so the badges follow whatever theme the host
1387
+ runs. */
1388
+ .message-reactions {
1389
+ position: relative;
1390
+ display: flex;
1391
+ align-items: center;
1392
+ gap: 4px;
1393
+ flex-wrap: wrap;
1394
+ }
1395
+
1396
+ .message-reactions__badge-wrapper {
1397
+ position: relative;
1398
+ }
1399
+
1400
+ .message-reactions__badge {
1401
+ display: flex;
1402
+ align-items: center;
1403
+ gap: 2px;
1404
+ padding: 2px 6px 2px 4px;
1405
+ border: 1px solid var(--cratis-surface-border);
1406
+ border-radius: 999px;
1407
+ background: var(--cratis-surface-100);
1408
+ cursor: pointer;
1409
+ }
1410
+
1411
+ .message-reactions__badge:hover {
1412
+ background: var(--cratis-surface-hover);
1413
+ }
1414
+
1415
+ /* A stack of the same emoji from different people overlaps slightly rather than repeating flat. */
1416
+ .message-reactions__stack {
1417
+ display: flex;
1418
+ }
1419
+
1420
+ .message-reactions__glyph {
1421
+ font-size: 12px;
1422
+ line-height: 1;
1423
+ }
1424
+
1425
+ .message-reactions__glyph + .message-reactions__glyph {
1426
+ margin-left: -7px;
1427
+ }
1428
+
1429
+ .message-reactions__count {
1430
+ font-size: 10px;
1431
+ color: var(--cratis-text-color-secondary);
1432
+ }
1433
+
1434
+ /* Positioned by AnchoredOverlay, which portals it to <body> — so this only describes the bubble
1435
+ itself, never where it sits. */
1436
+ .message-reactions__tooltip {
1437
+ min-width: 120px;
1438
+ background: var(--cratis-surface-overlay);
1439
+ border: 1px solid var(--cratis-surface-border);
1440
+ border-radius: 10px;
1441
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
1442
+ padding: 6px 8px;
1443
+ display: flex;
1444
+ flex-direction: column;
1445
+ gap: 4px;
1446
+ }
1447
+
1448
+ .message-reactions__tooltip-row {
1449
+ display: flex;
1450
+ align-items: center;
1451
+ gap: 8px;
1452
+ }
1453
+
1454
+ .message-reactions__tooltip-emoji {
1455
+ font-size: 13px;
1456
+ line-height: 1;
1457
+ }
1458
+
1459
+ .message-reactions__tooltip-name {
1460
+ font-size: 11px;
1461
+ color: var(--cratis-text-color);
1462
+ white-space: nowrap;
1463
+ }
1464
+
1465
+ /* ── ./Chat/Kit/ReactionPicker.css ─────────────────────────────────────── */
1466
+ /* Copyright (c) Cratis. All rights reserved. */
1467
+ /* Licensed under the MIT license. See LICENSE file in the project root for full license information. */
1468
+
1469
+ /* Portaled to <body> by AnchoredOverlay rather than positioned by its parent: the chat panel is a
1470
+ scrolling sidebar, and anything positioned inside it is clipped the moment it reaches the edge.
1471
+ Every color comes from the --cratis-* token seam, so the picker follows whatever theme the host
1472
+ runs. */
1473
+ .reaction-picker {
1474
+ display: flex;
1475
+ flex-direction: column;
1476
+ gap: 4px;
1477
+ align-items: flex-end;
1478
+ }
1479
+
1480
+ .reaction-picker__quick {
1481
+ display: flex;
1482
+ gap: 2px;
1483
+ align-items: center;
1484
+ background: var(--cratis-surface-overlay);
1485
+ border: 1px solid var(--cratis-surface-border);
1486
+ border-radius: 999px;
1487
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
1488
+ padding: 4px;
1489
+ }
1490
+
1491
+ .reaction-picker__options {
1492
+ display: contents;
1493
+ }
1494
+
1495
+ .reaction-picker__item {
1496
+ display: flex;
1497
+ align-items: center;
1498
+ justify-content: center;
1499
+ width: 28px;
1500
+ height: 28px;
1501
+ padding: 0;
1502
+ border: none;
1503
+ border-radius: 999px;
1504
+ background: transparent;
1505
+ font-size: 17px;
1506
+ line-height: 1;
1507
+ cursor: pointer;
1508
+ }
1509
+
1510
+ .reaction-picker__item:hover,
1511
+ .reaction-picker__item:focus-visible {
1512
+ background: var(--cratis-surface-hover);
1513
+ }
1514
+
1515
+ .reaction-picker__item--selected {
1516
+ background: var(--cratis-highlight-bg);
1517
+ }
1518
+
1519
+ /* Deliberately the quiet one on the row: it opens the rest of the emojis rather than being an emoji,
1520
+ so it reads as a control and never competes with the six actual choices next to it. */
1521
+ .reaction-picker__more {
1522
+ display: flex;
1523
+ align-items: center;
1524
+ justify-content: center;
1525
+ flex-shrink: 0;
1526
+ width: 28px;
1527
+ height: 28px;
1528
+ margin-left: 2px;
1529
+ padding: 0;
1530
+ border: none;
1531
+ border-radius: 999px;
1532
+ background: var(--cratis-surface-100);
1533
+ color: var(--cratis-text-color-secondary);
1534
+ font-size: 15px;
1535
+ line-height: 1;
1536
+ cursor: pointer;
1537
+ }
1538
+
1539
+ .reaction-picker__more:hover,
1540
+ .reaction-picker__more:focus-visible,
1541
+ .reaction-picker__more--open {
1542
+ background: var(--cratis-surface-hover);
1543
+ color: var(--cratis-text-color);
1544
+ }
1545
+
1546
+ /* Who has reacted so far, under the quick row — the same list a badge shows, so opening the picker
1547
+ answers "who gave what" without needing a second gesture. */
1548
+ .reaction-picker__given {
1549
+ display: flex;
1550
+ flex-direction: column;
1551
+ gap: 4px;
1552
+ align-self: stretch;
1553
+ max-height: 148px;
1554
+ overflow-y: auto;
1555
+ background: var(--cratis-surface-overlay);
1556
+ border: 1px solid var(--cratis-surface-border);
1557
+ border-radius: 10px;
1558
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
1559
+ padding: 6px 8px;
1560
+ }
1561
+
1562
+ .reaction-picker__given-row {
1563
+ display: flex;
1564
+ align-items: center;
1565
+ gap: 8px;
1566
+ }
1567
+
1568
+ .reaction-picker__given-emoji {
1569
+ font-size: 13px;
1570
+ line-height: 1;
1571
+ }
1572
+
1573
+ .reaction-picker__given-name {
1574
+ font-size: 11px;
1575
+ color: var(--cratis-text-color);
1576
+ white-space: nowrap;
1577
+ }
1578
+
1579
+ /* ── ./Chat/Kit/TypingIndicator.css ─────────────────────────────────────── */
1580
+ /* Copyright (c) Cratis. All rights reserved. */
1581
+ /* Licensed under the MIT license. See LICENSE file in the project root for full license information. */
1582
+
1583
+ /* Every color comes from the --cratis-* token seam, so the indicator follows whatever theme the
1584
+ host runs. */
1585
+ .chat-typing {
1586
+ display: flex;
1587
+ align-items: center;
1588
+ gap: 6px;
1589
+ padding: 2px 2px 4px;
1590
+ }
1591
+
1592
+ .chat-typing__authors {
1593
+ display: flex;
1594
+ align-items: center;
1595
+ }
1596
+
1597
+ .chat-typing__authors > * + * {
1598
+ margin-left: -6px;
1599
+ }
1600
+
1601
+ .chat-typing__label {
1602
+ font-size: 11px;
1603
+ color: var(--cratis-text-color-secondary);
1604
+ }
1605
+
1606
+ .chat-typing__dots {
1607
+ display: inline-flex;
1608
+ align-items: flex-end;
1609
+ gap: 3px;
1610
+ height: 10px;
1611
+ }
1612
+
1613
+ .chat-typing__dot {
1614
+ width: 5px;
1615
+ height: 5px;
1616
+ border-radius: 50%;
1617
+ background: var(--cratis-text-color-secondary);
1618
+ animation: chat-typing-jump 1.2s infinite ease-in-out;
1619
+ }
1620
+
1621
+ .chat-typing__dot:nth-child(2) {
1622
+ animation-delay: 0.18s;
1623
+ }
1624
+
1625
+ .chat-typing__dot:nth-child(3) {
1626
+ animation-delay: 0.36s;
1627
+ }
1628
+
1629
+ @keyframes chat-typing-jump {
1630
+ 0%,
1631
+ 60%,
1632
+ 100% {
1633
+ transform: translateY(0);
1634
+ opacity: 0.45;
1635
+ }
1636
+
1637
+ 30% {
1638
+ transform: translateY(-5px);
1639
+ opacity: 1;
1640
+ }
1641
+ }
1642
+
1643
+ @media (prefers-reduced-motion: reduce) {
1644
+ .chat-typing__dot {
1645
+ animation: none;
1646
+ }
1647
+ }
1648
+
1649
+ /* ── ./Common/ActionMenubar.css ─────────────────────────────────────── */
1650
+ /* Copyright (c) Cratis. All rights reserved. */
1651
+ /* Licensed under the MIT license. See LICENSE file in the project root for full license information. */
1652
+
1653
+ .cratis-action-menubar {
1654
+ display: flex;
1655
+ align-items: center;
1656
+ gap: 0.25rem;
1657
+ flex-wrap: wrap;
1658
+ }
1659
+
1660
+ /* ── ./Common/Button.css ─────────────────────────────────────── */
1661
+ /* Copyright (c) Cratis. All rights reserved. */
1662
+ /* Licensed under the MIT license. See LICENSE file in the project root for full license information. */
1663
+
1664
+ .cratis-button {
1665
+ display: inline-flex;
1666
+ box-sizing: border-box;
1667
+ min-height: var(--cratis-control-height);
1668
+ padding: 0.75rem 1.25rem;
1669
+ align-items: center;
1670
+ justify-content: center;
1671
+ gap: 0.45rem;
1672
+ border: 1px solid var(--cratis-action-background);
1673
+ border-radius: var(--cratis-border-radius);
1674
+ background: var(--cratis-action-background);
1675
+ color: var(--cratis-action-text);
1676
+ font: inherit;
1677
+ font-weight: 600;
1678
+ line-height: 1;
1679
+ cursor: pointer;
1680
+ transition:
1681
+ background-color 140ms ease,
1682
+ border-color 140ms ease,
1683
+ color 140ms ease,
1684
+ opacity 140ms ease;
1685
+ }
1686
+ .cratis-button:not([data-tone])[data-variant='solid']:hover:not(:disabled) {
1687
+ border-color: var(--cratis-action-background-hover);
1688
+ background: var(--cratis-action-background-hover);
1689
+ }
1690
+ .cratis-button:not([data-tone])[data-variant='solid']:active:not(:disabled) {
1691
+ border-color: var(--cratis-action-background-active);
1692
+ background: var(--cratis-action-background-active);
1693
+ }
1694
+ .cratis-button:is([data-variant='outline'], [data-variant='ghost']):hover:not(:disabled) {
1695
+ background: color-mix(in srgb, currentColor 4%, transparent);
1696
+ }
1697
+ .cratis-button:is([data-variant='outline'], [data-variant='ghost']):active:not(
1698
+ :disabled
1699
+ ) {
1700
+ background: color-mix(in srgb, currentColor 16%, transparent);
1701
+ }
1702
+ .cratis-button:focus-visible {
1703
+ outline: none;
1704
+ box-shadow: var(--cratis-focus-ring);
1705
+ }
1706
+ .cratis-button:disabled {
1707
+ cursor: not-allowed;
1708
+ opacity: var(--cratis-disabled-opacity);
1709
+ }
1710
+ .cratis-button[data-size='small'] {
1711
+ min-height: var(--cratis-control-height-small);
1712
+ padding: 0.5rem 0.75rem;
1713
+ font-size: 0.8rem;
1714
+ }
1715
+ .cratis-button[data-size='large'] {
1716
+ min-height: var(--cratis-control-height-large);
1717
+ padding: 0.875rem 1.5rem;
1718
+ font-size: 1rem;
1719
+ }
1720
+ .cratis-button[data-shape='pill'],
1721
+ .cratis-button[data-icon-only='true'] {
1722
+ border-radius: 999px;
1723
+ }
1724
+ .cratis-button[data-icon-only='true'] {
1725
+ width: var(--cratis-control-height);
1726
+ padding: 0;
1727
+ }
1728
+ .cratis-button[data-size='small'][data-icon-only='true'] {
1729
+ width: var(--cratis-control-height-small);
1730
+ }
1731
+ .cratis-button[data-size='large'][data-icon-only='true'] {
1732
+ width: var(--cratis-control-height-large);
1733
+ }
1734
+
1735
+ .cratis-button[data-variant='outline'] {
1736
+ border-color: currentColor;
1737
+ background: transparent;
1738
+ color: var(--cratis-primary-color);
1739
+ }
1740
+ .cratis-button[data-variant='ghost'] {
1741
+ border-color: transparent;
1742
+ background: transparent;
1743
+ color: var(--cratis-text-color);
1744
+ }
1745
+ .cratis-button[data-variant='link'] {
1746
+ min-height: auto;
1747
+ padding: 0;
1748
+ border-color: transparent;
1749
+ background: transparent;
1750
+ color: var(--cratis-primary-color);
1751
+ text-decoration: underline;
1752
+ }
1753
+
1754
+ .cratis-button[data-tone='neutral'] {
1755
+ --cratis-button-tone: var(--cratis-text-color);
1756
+ --cratis-button-fill: var(--cratis-surface-100);
1757
+ --cratis-button-fill-text: var(--cratis-text-color);
1758
+ }
1759
+ .cratis-button[data-tone='accent'] {
1760
+ --cratis-button-tone: var(--cratis-info-background);
1761
+ --cratis-button-fill: var(--cratis-info-background);
1762
+ --cratis-button-fill-text: var(--cratis-info-text);
1763
+ }
1764
+ .cratis-button[data-tone='positive'] {
1765
+ --cratis-button-tone: var(--cratis-success-background);
1766
+ --cratis-button-fill: var(--cratis-success-background);
1767
+ --cratis-button-fill-text: var(--cratis-success-text);
1768
+ }
1769
+ .cratis-button[data-tone='caution'] {
1770
+ --cratis-button-tone: var(--cratis-warning-background);
1771
+ --cratis-button-fill: var(--cratis-warning-background);
1772
+ --cratis-button-fill-text: var(--cratis-warning-text);
1773
+ }
1774
+ .cratis-button[data-tone='critical'] {
1775
+ --cratis-button-tone: var(--cratis-danger-background);
1776
+ --cratis-button-fill: var(--cratis-danger-background);
1777
+ --cratis-button-fill-text: var(--cratis-danger-text);
1778
+ }
1779
+ .cratis-button[data-tone='accent'][data-severity='help'] {
1780
+ --cratis-button-tone: var(--cratis-primary-600);
1781
+ --cratis-button-fill: var(--cratis-primary-600);
1782
+ --cratis-button-fill-text: var(--cratis-primary-600-text);
1783
+ }
1784
+ .cratis-button[data-tone='neutral'][data-severity='contrast'] {
1785
+ --cratis-button-tone: var(--cratis-text-color);
1786
+ --cratis-button-fill: var(--cratis-text-color);
1787
+ --cratis-button-fill-text: var(--cratis-surface-0);
1788
+ }
1789
+ .cratis-button[data-tone][data-variant='solid'] {
1790
+ border-color: var(--cratis-button-fill);
1791
+ background: var(--cratis-button-fill);
1792
+ color: var(--cratis-button-fill-text);
1793
+ }
1794
+ .cratis-button[data-tone][data-variant='solid']:hover:not(:disabled) {
1795
+ border-color: color-mix(in srgb, var(--cratis-button-fill) 88%, black);
1796
+ background: color-mix(in srgb, var(--cratis-button-fill) 88%, black);
1797
+ }
1798
+ .cratis-button[data-tone][data-variant='solid']:active:not(:disabled) {
1799
+ border-color: color-mix(in srgb, var(--cratis-button-fill) 76%, black);
1800
+ background: color-mix(in srgb, var(--cratis-button-fill) 76%, black);
1801
+ }
1802
+ .cratis-button[data-tone]:is(
1803
+ [data-variant='outline'],
1804
+ [data-variant='ghost'],
1805
+ [data-variant='link']
1806
+ ) {
1807
+ color: var(--cratis-button-tone);
1808
+ }
1809
+
1810
+ .cratis-button__icon,
1811
+ .cratis-button__spinner {
1812
+ display: inline-flex;
1813
+ flex: 0 0 auto;
1814
+ }
1815
+ .cratis-button__spinner {
1816
+ width: 1em;
1817
+ height: 1em;
1818
+ border: 2px solid currentColor;
1819
+ border-right-color: transparent;
1820
+ border-radius: 50%;
1821
+ animation: cratis-button-spin 700ms linear infinite;
1822
+ }
1823
+
1824
+ @keyframes cratis-button-spin {
1825
+ to {
1826
+ transform: rotate(360deg);
1827
+ }
1828
+ }
1829
+
1830
+ @media (prefers-reduced-motion: reduce) {
1831
+ .cratis-button,
1832
+ .cratis-button__spinner {
1833
+ transition: none;
1834
+ animation: none;
1835
+ }
1836
+ }
1837
+
1838
+ @media (forced-colors: active) {
1839
+ .cratis-button {
1840
+ border-color: ButtonText;
1841
+ }
1842
+ .cratis-button:focus-visible {
1843
+ outline: 2px solid Highlight;
1844
+ outline-offset: 2px;
1845
+ box-shadow: none;
1846
+ }
1847
+ }
1848
+
1849
+ /* ── ./Common/ComboBox.css ─────────────────────────────────────── */
1850
+ /* Copyright (c) Cratis. All rights reserved. */
1851
+ /* Licensed under the MIT license. See LICENSE file in the project root for full license information. */
1852
+
1853
+ .cratis-combobox {
1854
+ display: inline-flex;
1855
+ flex-direction: column;
1856
+ min-width: 10rem;
1857
+ color: var(--cratis-text-color);
1858
+ }
1859
+ .cratis-combobox__control {
1860
+ display: flex;
1861
+ min-height: var(--cratis-control-height);
1862
+ border: 1px solid var(--cratis-control-border);
1863
+ border-radius: var(--cratis-border-radius);
1864
+ background: var(--cratis-control-background);
1865
+ }
1866
+ .cratis-combobox__control:focus-within {
1867
+ border-color: var(--cratis-primary-color);
1868
+ box-shadow: var(--cratis-focus-ring);
1869
+ }
1870
+ .cratis-combobox[data-invalid='true'] .cratis-combobox__control {
1871
+ border-color: var(--cratis-red-500);
1872
+ }
1873
+ .cratis-combobox[data-disabled='true'] {
1874
+ cursor: not-allowed;
1875
+ opacity: var(--cratis-disabled-opacity);
1876
+ }
1877
+ .cratis-combobox__input,
1878
+ .cratis-combobox__trigger {
1879
+ border: 0;
1880
+ background: transparent;
1881
+ color: inherit;
1882
+ font: inherit;
1883
+ }
1884
+ .cratis-combobox__input {
1885
+ flex: 1 1 auto;
1886
+ min-width: 0;
1887
+ padding: 0.625rem 0.75rem;
1888
+ outline: 0;
1889
+ }
1890
+ .cratis-combobox__trigger {
1891
+ padding: 0 0.75rem;
1892
+ color: var(--cratis-text-color-secondary);
1893
+ }
1894
+ .cratis-combobox__popover {
1895
+ width: var(--trigger-width);
1896
+ overflow: auto;
1897
+ border: 1px solid var(--cratis-surface-border);
1898
+ border-radius: var(--cratis-border-radius);
1899
+ background: var(--cratis-surface-overlay);
1900
+ box-shadow: var(--cratis-shadow-overlay);
1901
+ }
1902
+ .cratis-combobox__listbox {
1903
+ max-height: 18rem;
1904
+ padding: 0.25rem;
1905
+ }
1906
+ .cratis-combobox__option,
1907
+ .cratis-combobox__action,
1908
+ .cratis-combobox__message {
1909
+ display: flex;
1910
+ gap: 0.5rem;
1911
+ padding: 0.5rem 0.75rem;
1912
+ border-radius: var(--cratis-border-radius);
1913
+ }
1914
+ .cratis-combobox__listbox[data-option-layout='stacked'] .cratis-combobox__option {
1915
+ flex-direction: column;
1916
+ }
1917
+ .cratis-combobox__option[data-focused],
1918
+ .cratis-combobox__action[data-focused] {
1919
+ background: var(--cratis-surface-hover);
1920
+ }
1921
+ .cratis-combobox__option[data-selected='true'] {
1922
+ background: var(--cratis-highlight-bg);
1923
+ color: var(--cratis-highlight-text-color);
1924
+ }
1925
+ .cratis-combobox__option[data-disabled='true'] {
1926
+ opacity: var(--cratis-disabled-opacity);
1927
+ }
1928
+ .cratis-combobox__option-description,
1929
+ .cratis-combobox__message,
1930
+ .cratis-combobox__description {
1931
+ color: var(--cratis-text-color-secondary);
1932
+ }
1933
+ .cratis-combobox__action {
1934
+ border-top: 1px solid var(--cratis-surface-border);
1935
+ color: var(--cratis-primary-color);
1936
+ }
1937
+ .cratis-combobox__description,
1938
+ .cratis-combobox__error {
1939
+ margin-top: 0.25rem;
1940
+ font-size: 0.875em;
1941
+ }
1942
+ .cratis-combobox__error {
1943
+ color: var(--cratis-red-500);
1944
+ }
1945
+
1946
+ /* ── ./Common/Collections.css ─────────────────────────────────────── */
1947
+ /* Copyright (c) Cratis. All rights reserved. */
1948
+ /* Licensed under the MIT license. See LICENSE file in the project root for full license information. */
1949
+
1950
+ /* Toggle group, tabs, tag group and breadcrumbs share a token vocabulary and a focus treatment, so
1951
+ they share one stylesheet rather than repeating it four times in the published aggregate. */
1952
+
1953
+ .cratis-toggle-group,
1954
+ .cratis-tabs__list,
1955
+ .cratis-tag-group,
1956
+ .cratis-breadcrumbs {
1957
+ display: flex;
1958
+ align-items: center;
1959
+ color: var(--cratis-text-color);
1960
+ }
1961
+ .cratis-toggle-group__option:focus-visible,
1962
+ .cratis-tabs__tab:focus-visible,
1963
+ .cratis-tag-group__tag:focus-visible,
1964
+ .cratis-tag-group__remove:focus-visible,
1965
+ .cratis-tag-group__input:focus-visible,
1966
+ .cratis-breadcrumbs__link:focus-visible {
1967
+ outline: none;
1968
+ box-shadow: var(--cratis-focus-ring);
1969
+ }
1970
+ .cratis-toggle-group[data-disabled],
1971
+ .cratis-tabs[data-disabled] {
1972
+ cursor: not-allowed;
1973
+ opacity: var(--cratis-disabled-opacity);
1974
+ }
1975
+ /* A disabled tag group keeps its values readable — they are information, not an inactive control,
1976
+ and fading them takes their contrast below the threshold. Only the affordances dim. */
1977
+ .cratis-tag-group[data-disabled] {
1978
+ cursor: not-allowed;
1979
+ background: var(--cratis-surface-ground);
1980
+ }
1981
+ .cratis-tag-group[data-disabled] .cratis-tag-group__tag {
1982
+ border-style: dashed;
1983
+ }
1984
+
1985
+ /* Toggle group */
1986
+ .cratis-toggle-group {
1987
+ gap: 0.125rem;
1988
+ padding: 0.125rem;
1989
+ border: 1px solid var(--cratis-surface-border);
1990
+ border-radius: var(--cratis-border-radius);
1991
+ background: var(--cratis-surface-ground);
1992
+ }
1993
+ .cratis-toggle-group__option {
1994
+ display: inline-flex;
1995
+ align-items: center;
1996
+ gap: 0.35rem;
1997
+ padding: 0.25rem 0.75rem;
1998
+ border: 0;
1999
+ border-radius: calc(var(--cratis-border-radius) - 1px);
2000
+ background: transparent;
2001
+ color: inherit;
2002
+ font: inherit;
2003
+ cursor: pointer;
2004
+ }
2005
+ .cratis-toggle-group__option[data-selected] {
2006
+ background: var(--cratis-surface-card);
2007
+ box-shadow: var(--cratis-shadow-card, none);
2008
+ font-weight: 600;
2009
+ }
2010
+ .cratis-toggle-group__option[data-disabled] {
2011
+ cursor: not-allowed;
2012
+ opacity: var(--cratis-disabled-opacity);
2013
+ }
2014
+ .cratis-toggle-group__icon {
2015
+ display: inline-flex;
2016
+ }
2017
+ .cratis-toggle-group[data-invalid] {
2018
+ border-color: var(--cratis-red-500);
2019
+ }
2020
+
2021
+ /* Tabs */
2022
+ .cratis-tabs {
2023
+ display: flex;
2024
+ flex-direction: column;
2025
+ }
2026
+ .cratis-tabs__list {
2027
+ gap: 0.25rem;
2028
+ border-bottom: 1px solid var(--cratis-surface-border);
2029
+ }
2030
+ .cratis-tabs__tab {
2031
+ padding: 0.5rem 0.75rem;
2032
+ border: 0;
2033
+ border-bottom: 2px solid transparent;
2034
+ background: transparent;
2035
+ color: var(--cratis-text-color-secondary);
2036
+ font: inherit;
2037
+ cursor: pointer;
2038
+ }
2039
+ .cratis-tabs__tab[data-selected] {
2040
+ border-bottom-color: var(--cratis-primary-color);
2041
+ color: var(--cratis-text-color);
2042
+ font-weight: 600;
2043
+ }
2044
+ .cratis-tabs__tab[data-disabled] {
2045
+ cursor: not-allowed;
2046
+ opacity: var(--cratis-disabled-opacity);
2047
+ }
2048
+ .cratis-tabs__panel {
2049
+ padding: 0.75rem 0;
2050
+ outline: none;
2051
+ }
2052
+
2053
+ /* Tag group */
2054
+ .cratis-tag-group {
2055
+ flex-wrap: wrap;
2056
+ gap: 0.35rem;
2057
+ min-height: var(--cratis-control-height);
2058
+ padding: 0.25rem 0.5rem;
2059
+ border: 1px solid var(--cratis-control-border);
2060
+ border-radius: var(--cratis-border-radius);
2061
+ background: var(--cratis-control-background);
2062
+ }
2063
+ .cratis-tag-group:focus-within {
2064
+ border-color: var(--cratis-primary-color);
2065
+ box-shadow: var(--cratis-focus-ring);
2066
+ }
2067
+ .cratis-tag-group[data-invalid] {
2068
+ border-color: var(--cratis-red-500);
2069
+ }
2070
+ .cratis-tag-group__group,
2071
+ .cratis-tag-group__list {
2072
+ display: contents;
2073
+ }
2074
+ .cratis-tag-group__tag {
2075
+ display: inline-flex;
2076
+ align-items: center;
2077
+ gap: 0.25rem;
2078
+ padding: 0.1rem 0.5rem;
2079
+ border: 1px solid var(--cratis-surface-border);
2080
+ border-radius: 999px;
2081
+ background: var(--cratis-surface-ground);
2082
+ font-size: 0.9em;
2083
+ }
2084
+ .cratis-tag-group__remove {
2085
+ display: inline-flex;
2086
+ align-items: center;
2087
+ justify-content: center;
2088
+ padding: 0;
2089
+ border: 0;
2090
+ background: transparent;
2091
+ color: var(--cratis-text-color-secondary);
2092
+ line-height: 1;
2093
+ cursor: pointer;
2094
+ }
2095
+ .cratis-tag-group__remove:hover {
2096
+ color: var(--cratis-text-color);
2097
+ }
2098
+ .cratis-tag-group__input {
2099
+ flex: 1 1 6rem;
2100
+ min-width: 4rem;
2101
+ border: 0;
2102
+ background: transparent;
2103
+ color: inherit;
2104
+ font: inherit;
2105
+ outline: none;
2106
+ }
2107
+
2108
+ /* Breadcrumbs */
2109
+ .cratis-breadcrumbs {
2110
+ flex-wrap: wrap;
2111
+ gap: 0.35rem;
2112
+ padding: 0;
2113
+ margin: 0;
2114
+ list-style: none;
2115
+ }
2116
+ .cratis-breadcrumbs__item {
2117
+ display: inline-flex;
2118
+ align-items: center;
2119
+ gap: 0.35rem;
2120
+ }
2121
+ .cratis-breadcrumbs__link {
2122
+ border-radius: var(--cratis-border-radius);
2123
+ color: var(--cratis-text-color-secondary);
2124
+ text-decoration: none;
2125
+ cursor: pointer;
2126
+ }
2127
+ .cratis-breadcrumbs__link:hover {
2128
+ color: var(--cratis-text-color);
2129
+ text-decoration: underline;
2130
+ }
2131
+ .cratis-breadcrumbs__item[data-selected] {
2132
+ color: var(--cratis-text-color);
2133
+ font-weight: 600;
2134
+ }
2135
+ .cratis-breadcrumbs__separator {
2136
+ color: var(--cratis-text-color-secondary);
2137
+ }
2138
+
2139
+ @media (forced-colors: active) {
2140
+ .cratis-toggle-group__option[data-selected],
2141
+ .cratis-tabs__tab[data-selected] {
2142
+ forced-color-adjust: none;
2143
+ background: Highlight;
2144
+ color: HighlightText;
2145
+ }
2146
+ }
2147
+
2148
+ /* ── ./Common/DatePickerInput.css ─────────────────────────────────────── */
2149
+ /* Copyright (c) Cratis. All rights reserved. */
2150
+ /* Licensed under the MIT license. See LICENSE file in the project root for full license information. */
2151
+
2152
+ .cratis-date-picker {
2153
+ display: inline-flex;
2154
+ width: 100%;
2155
+ color: var(--cratis-text-color);
2156
+ }
2157
+ .cratis-date-picker__picker {
2158
+ width: 100%;
2159
+ }
2160
+ .cratis-date-picker__group {
2161
+ position: relative;
2162
+ display: flex;
2163
+ width: 100%;
2164
+ box-sizing: border-box;
2165
+ min-height: var(--cratis-control-height);
2166
+ align-items: center;
2167
+ overflow: hidden;
2168
+ border: 1px solid var(--cratis-control-border);
2169
+ border-radius: var(--cratis-border-radius);
2170
+ background: var(--cratis-control-background);
2171
+ }
2172
+ .cratis-date-picker__group[data-focus-within] {
2173
+ border-color: var(--cratis-primary-color);
2174
+ box-shadow: var(--cratis-focus-ring);
2175
+ }
2176
+ .cratis-date-picker[data-invalid='true'] .cratis-date-picker__group {
2177
+ border-color: var(--cratis-red-500);
2178
+ }
2179
+ .cratis-date-picker[data-disabled='true'] {
2180
+ opacity: var(--cratis-disabled-opacity);
2181
+ }
2182
+ .cratis-date-picker__input {
2183
+ display: flex;
2184
+ flex: 1;
2185
+ flex-wrap: wrap;
2186
+ min-width: 0;
2187
+ padding: 0.625rem 0.75rem;
2188
+ }
2189
+ .cratis-date-picker__group[data-empty='true']:not([data-focus-within])
2190
+ .cratis-date-picker__input {
2191
+ opacity: 0;
2192
+ }
2193
+ .cratis-date-picker__placeholder {
2194
+ position: absolute;
2195
+ inset-inline-start: 0.7rem;
2196
+ color: var(--cratis-text-color-secondary);
2197
+ pointer-events: none;
2198
+ }
2199
+ .cratis-date-picker__group[data-focus-within] .cratis-date-picker__placeholder {
2200
+ display: none;
2201
+ }
2202
+ .cratis-date-picker__segment {
2203
+ padding: 0 0.1rem;
2204
+ border-radius: 0.2rem;
2205
+ outline: none;
2206
+ font-variant-numeric: tabular-nums;
2207
+ }
2208
+ .cratis-date-picker__segment:not([data-type='literal']) {
2209
+ display: flex;
2210
+ min-width: 24px;
2211
+ min-height: 24px;
2212
+ align-items: center;
2213
+ justify-content: center;
2214
+ }
2215
+ .cratis-date-picker__segment[data-placeholder] {
2216
+ color: var(--cratis-text-color-secondary);
2217
+ }
2218
+ .cratis-date-picker__segment[data-focused] {
2219
+ background: var(--cratis-highlight-bg);
2220
+ color: var(--cratis-highlight-text-color);
2221
+ }
2222
+ .cratis-date-picker__trigger {
2223
+ align-self: stretch;
2224
+ min-width: var(--cratis-control-height);
2225
+ border: 0;
2226
+ border-inline-start: 1px solid var(--cratis-control-border);
2227
+ background: transparent;
2228
+ color: var(--cratis-text-color-secondary);
2229
+ cursor: pointer;
2230
+ }
2231
+ .cratis-date-picker__trigger:hover {
2232
+ background: var(--cratis-surface-hover);
2233
+ color: var(--cratis-text-color);
2234
+ }
2235
+ .cratis-date-picker__trigger:focus-visible {
2236
+ outline: none;
2237
+ box-shadow: inset var(--cratis-focus-ring);
2238
+ }
2239
+ .cratis-date-picker__popover {
2240
+ z-index: var(--cratis-z-index-overlay);
2241
+ overflow: auto;
2242
+ border: 1px solid var(--cratis-surface-border);
2243
+ border-radius: var(--cratis-border-radius);
2244
+ background: var(--cratis-surface-overlay);
2245
+ color: var(--cratis-text-color);
2246
+ box-shadow: var(--cratis-shadow-overlay);
2247
+ }
2248
+ .cratis-date-picker__dialog {
2249
+ padding: 0.75rem;
2250
+ outline: none;
2251
+ }
2252
+ .cratis-date-picker__calendar {
2253
+ width: max-content;
2254
+ }
2255
+ .cratis-date-picker__header {
2256
+ display: grid;
2257
+ grid-template-columns: 2rem 1fr 2rem;
2258
+ align-items: center;
2259
+ gap: 0.5rem;
2260
+ margin-bottom: 0.5rem;
2261
+ }
2262
+ .cratis-date-picker__heading {
2263
+ margin: 0;
2264
+ text-align: center;
2265
+ font-size: 0.95rem;
2266
+ }
2267
+ .cratis-date-picker__nav,
2268
+ .cratis-date-picker__action {
2269
+ min-height: 2rem;
2270
+ border: 1px solid var(--cratis-surface-border);
2271
+ border-radius: calc(var(--cratis-border-radius) * 0.75);
2272
+ background: transparent;
2273
+ color: inherit;
2274
+ cursor: pointer;
2275
+ }
2276
+ .cratis-date-picker__nav:hover,
2277
+ .cratis-date-picker__action:hover {
2278
+ background: var(--cratis-surface-hover);
2279
+ }
2280
+ .cratis-date-picker__nav:focus-visible,
2281
+ .cratis-date-picker__action:focus-visible {
2282
+ outline: none;
2283
+ box-shadow: var(--cratis-focus-ring);
2284
+ }
2285
+ .cratis-date-picker__grid {
2286
+ border-collapse: collapse;
2287
+ }
2288
+ .cratis-date-picker__grid th {
2289
+ width: 2.25rem;
2290
+ height: 2rem;
2291
+ color: var(--cratis-text-color-secondary);
2292
+ font-size: 0.75rem;
2293
+ font-weight: 500;
2294
+ }
2295
+ .cratis-date-picker__cell {
2296
+ display: flex;
2297
+ width: 2.5rem;
2298
+ height: 2.5rem;
2299
+ align-items: center;
2300
+ justify-content: center;
2301
+ border-radius: 50%;
2302
+ outline: none;
2303
+ cursor: default;
2304
+ }
2305
+ .cratis-date-picker__cell[data-hovered] {
2306
+ background: var(--cratis-surface-hover);
2307
+ }
2308
+ .cratis-date-picker__cell[data-selected] {
2309
+ background: var(--cratis-action-background);
2310
+ color: var(--cratis-action-text);
2311
+ }
2312
+ .cratis-date-picker__cell[data-focused] {
2313
+ box-shadow: var(--cratis-focus-ring);
2314
+ }
2315
+ .cratis-date-picker__cell[data-disabled],
2316
+ .cratis-date-picker__cell[data-outside-month] {
2317
+ opacity: 0.45;
2318
+ }
2319
+ .cratis-date-picker__button-bar {
2320
+ display: flex;
2321
+ justify-content: flex-end;
2322
+ gap: 0.5rem;
2323
+ margin-top: 0.6rem;
2324
+ padding-top: 0.6rem;
2325
+ border-top: 1px solid var(--cratis-surface-border);
2326
+ }
2327
+ .cratis-date-picker__action {
2328
+ padding: 0.35rem 0.65rem;
2329
+ }
2330
+
2331
+ @media (forced-colors: active) {
2332
+ .cratis-date-picker__group,
2333
+ .cratis-date-picker__popover,
2334
+ .cratis-date-picker__nav,
2335
+ .cratis-date-picker__action {
2336
+ border-color: CanvasText;
2337
+ }
2338
+ .cratis-date-picker__cell[data-selected] {
2339
+ background: Highlight;
2340
+ color: HighlightText;
2341
+ }
2342
+ }
2343
+
2344
+ /* ── ./Common/FormElement.css ─────────────────────────────────────── */
2345
+ /* Copyright (c) Cratis. All rights reserved. */
2346
+ /* Licensed under the MIT license. See LICENSE file in the project root for full license information. */
2347
+
2348
+ .cratis-form-element {
2349
+ display: flex;
2350
+ flex: 1 1 auto;
2351
+ align-items: stretch;
2352
+ }
2353
+
2354
+ .cratis-form-element__addon {
2355
+ display: inline-flex;
2356
+ align-items: center;
2357
+ justify-content: center;
2358
+ padding: 0 0.75rem;
2359
+ color: var(--cratis-text-color-secondary);
2360
+ background: var(--cratis-surface-100);
2361
+ border: 1px solid var(--cratis-control-border);
2362
+ border-right: 0;
2363
+ border-top-left-radius: var(--cratis-border-radius);
2364
+ border-bottom-left-radius: var(--cratis-border-radius);
2365
+ }
2366
+
2367
+ .cratis-form-element > *:not(.cratis-form-element__addon) {
2368
+ flex: 1 1 auto;
2369
+ min-width: 0;
2370
+ }
2371
+
2372
+ /* ── ./Common/NumberInput.css ─────────────────────────────────────── */
2373
+ /* Copyright (c) Cratis. All rights reserved. */
2374
+ /* Licensed under the MIT license. See LICENSE file in the project root for full license information. */
2375
+
2376
+ .cratis-number-input {
2377
+ box-sizing: border-box;
2378
+ width: 100%;
2379
+ max-width: 100%;
2380
+ color: var(--cratis-text-color);
2381
+ }
2382
+ .cratis-number-input__group {
2383
+ display: flex;
2384
+ box-sizing: border-box;
2385
+ min-width: 0;
2386
+ min-height: var(--cratis-control-height);
2387
+ align-items: stretch;
2388
+ overflow: hidden;
2389
+ border: 1px solid var(--cratis-control-border);
2390
+ border-radius: var(--cratis-border-radius);
2391
+ background: var(--cratis-control-background);
2392
+ box-shadow: var(--cratis-shadow-subtle);
2393
+ }
2394
+ .cratis-number-input__control,
2395
+ .cratis-number-input__steps {
2396
+ display: flex;
2397
+ min-width: 0;
2398
+ align-items: stretch;
2399
+ }
2400
+ .cratis-number-input__control {
2401
+ flex: 1 1 auto;
2402
+ }
2403
+ .cratis-number-input__steps {
2404
+ flex: 0 0 auto;
2405
+ }
2406
+ .cratis-number-input__group:focus-within {
2407
+ border-color: var(--cratis-primary-color);
2408
+ box-shadow: var(--cratis-focus-ring);
2409
+ }
2410
+ .cratis-number-input[data-invalid='true'] .cratis-number-input__group {
2411
+ border-color: var(--cratis-red-500);
2412
+ }
2413
+ .cratis-number-input[data-disabled='true'] {
2414
+ cursor: not-allowed;
2415
+ opacity: var(--cratis-disabled-opacity);
2416
+ }
2417
+ .cratis-number-input:is([data-disabled='true'], [data-readonly='true'])
2418
+ .cratis-number-input__group {
2419
+ background: var(--cratis-surface-100);
2420
+ }
2421
+ .cratis-number-input__input {
2422
+ box-sizing: border-box;
2423
+ width: 100%;
2424
+ min-width: 0;
2425
+ min-height: var(--cratis-control-height);
2426
+ padding: 0.625rem 0.75rem;
2427
+ flex: 1 1 auto;
2428
+ border: 0;
2429
+ outline: 0;
2430
+ background: transparent;
2431
+ color: var(--cratis-text-color);
2432
+ font: inherit;
2433
+ font-variant-numeric: tabular-nums;
2434
+ }
2435
+ .cratis-number-input__input::placeholder {
2436
+ color: var(--cratis-text-color-secondary);
2437
+ }
2438
+ .cratis-number-input__adornment {
2439
+ display: inline-flex;
2440
+ min-width: 0;
2441
+ padding-inline: 0.75rem;
2442
+ align-items: center;
2443
+ color: var(--cratis-number-input-adornment-color);
2444
+ white-space: nowrap;
2445
+ }
2446
+ .cratis-number-input__prefix {
2447
+ padding-inline-end: 0;
2448
+ }
2449
+ .cratis-number-input__suffix {
2450
+ padding-inline-start: 0;
2451
+ }
2452
+ .cratis-number-input__step {
2453
+ display: inline-flex;
2454
+ box-sizing: border-box;
2455
+ width: var(--cratis-control-height);
2456
+ min-width: 2.75rem;
2457
+ min-height: var(--cratis-control-height);
2458
+ padding: 0;
2459
+ align-items: center;
2460
+ justify-content: center;
2461
+ border: 0;
2462
+ border-inline-start: 1px solid var(--cratis-control-border);
2463
+ background: var(--cratis-number-input-step-background);
2464
+ color: var(--cratis-text-color);
2465
+ font: inherit;
2466
+ cursor: pointer;
2467
+ }
2468
+ .cratis-number-input__step:hover:not(:disabled) {
2469
+ background: var(--cratis-number-input-step-background-hover);
2470
+ }
2471
+ .cratis-number-input__step:focus-visible {
2472
+ outline: 2px solid var(--cratis-primary-color);
2473
+ outline-offset: -3px;
2474
+ }
2475
+ .cratis-number-input__step:disabled {
2476
+ cursor: not-allowed;
2477
+ opacity: var(--cratis-disabled-opacity);
2478
+ }
2479
+ .cratis-number-input__description,
2480
+ .cratis-number-input__error {
2481
+ display: block;
2482
+ margin-block-start: 0.35rem;
2483
+ line-height: 1.4;
2484
+ }
2485
+ .cratis-number-input__description {
2486
+ color: var(--cratis-text-color-secondary);
2487
+ }
2488
+ .cratis-number-input__error {
2489
+ color: var(--cratis-red-500);
2490
+ }
2491
+
2492
+ @media (max-width: 24rem) {
2493
+ .cratis-number-input__group {
2494
+ flex-wrap: wrap;
2495
+ }
2496
+ .cratis-number-input__control,
2497
+ .cratis-number-input__steps {
2498
+ width: 100%;
2499
+ flex-basis: 100%;
2500
+ border-block-start: 1px solid var(--cratis-control-border);
2501
+ }
2502
+ .cratis-number-input__step {
2503
+ width: 50%;
2504
+ min-width: 0;
2505
+ flex: 1 1 50%;
2506
+ }
2507
+ .cratis-number-input__step:first-child {
2508
+ border-inline-start: 0;
2509
+ }
2510
+ }
2511
+
2512
+ @media (forced-colors: active) {
2513
+ .cratis-number-input__group,
2514
+ .cratis-number-input__step {
2515
+ border-color: CanvasText;
2516
+ }
2517
+ .cratis-number-input__group:focus-within,
2518
+ .cratis-number-input__step:focus-visible {
2519
+ outline: 2px solid Highlight;
2520
+ outline-offset: 2px;
2521
+ box-shadow: none;
2522
+ }
2523
+ }
2524
+
2525
+ /* ── ./Common/Primitives.css ─────────────────────────────────────── */
2526
+ /* Copyright (c) Cratis. All rights reserved. */
2527
+ /* Licensed under the MIT license. See LICENSE file in the project root for full license information. */
2528
+
2529
+ .cratis-text-input,
2530
+ .cratis-text-area {
2531
+ box-sizing: border-box;
2532
+ min-height: var(--cratis-control-height);
2533
+ padding: 0.625rem 0.75rem;
2534
+ border: 1px solid var(--cratis-control-border);
2535
+ border-radius: var(--cratis-border-radius);
2536
+ background: var(--cratis-control-background);
2537
+ color: var(--cratis-text-color);
2538
+ font: inherit;
2539
+ box-shadow: var(--cratis-shadow-subtle);
2540
+ }
2541
+ .cratis-text-area {
2542
+ resize: vertical;
2543
+ }
2544
+ .cratis-text-input::placeholder,
2545
+ .cratis-text-area::placeholder {
2546
+ color: var(--cratis-text-color-secondary);
2547
+ }
2548
+ .cratis-text-input:focus-visible,
2549
+ .cratis-text-area:focus-visible {
2550
+ outline: none;
2551
+ border-color: var(--cratis-primary-color);
2552
+ box-shadow: var(--cratis-focus-ring);
2553
+ }
2554
+ .cratis-text-input:is(:disabled, [data-readonly='true']),
2555
+ .cratis-text-area:is(:disabled, [data-readonly='true']) {
2556
+ background: var(--cratis-surface-100);
2557
+ }
2558
+ .cratis-text-input:disabled,
2559
+ .cratis-text-area:disabled {
2560
+ cursor: not-allowed;
2561
+ opacity: var(--cratis-disabled-opacity);
2562
+ }
2563
+ .cratis-text-input[data-invalid='true'],
2564
+ .cratis-text-area[data-invalid='true'] {
2565
+ border-color: var(--cratis-red-500);
2566
+ }
2567
+
2568
+ .cratis-choice {
2569
+ position: relative;
2570
+ display: inline-flex;
2571
+ align-items: center;
2572
+ gap: 0.5rem;
2573
+ color: var(--cratis-text-color);
2574
+ cursor: pointer;
2575
+ }
2576
+ .cratis-choice[data-disabled='true'] {
2577
+ cursor: not-allowed;
2578
+ opacity: var(--cratis-disabled-opacity);
2579
+ }
2580
+ .cratis-choice[data-readonly='true']:not([data-disabled='true']) {
2581
+ cursor: default;
2582
+ }
2583
+ .cratis-choice__input {
2584
+ position: absolute;
2585
+ width: 1px;
2586
+ height: 1px;
2587
+ margin: -1px;
2588
+ overflow: hidden;
2589
+ clip: rect(0 0 0 0);
2590
+ clip-path: inset(50%);
2591
+ white-space: nowrap;
2592
+ }
2593
+ .cratis-checkbox__box,
2594
+ .cratis-radio__box,
2595
+ .cratis-switch__control {
2596
+ display: inline-flex;
2597
+ box-sizing: border-box;
2598
+ flex: 0 0 auto;
2599
+ align-items: center;
2600
+ border: 1px solid var(--cratis-control-border);
2601
+ background: var(--cratis-control-background);
2602
+ }
2603
+ .cratis-checkbox__box,
2604
+ .cratis-radio__box {
2605
+ width: 1.25rem;
2606
+ height: 1.25rem;
2607
+ justify-content: center;
2608
+ }
2609
+ .cratis-checkbox__box {
2610
+ border-radius: var(--cratis-border-radius);
2611
+ }
2612
+ .cratis-checkbox__indicator {
2613
+ color: var(--cratis-primary-color-text);
2614
+ font-size: 0.85rem;
2615
+ line-height: 1;
2616
+ opacity: 0;
2617
+ }
2618
+ .cratis-radio__box {
2619
+ border-radius: 50%;
2620
+ }
2621
+ .cratis-radio__indicator {
2622
+ width: 0.65rem;
2623
+ height: 0.65rem;
2624
+ border-radius: 50%;
2625
+ background: var(--cratis-primary-color);
2626
+ opacity: 0;
2627
+ }
2628
+ .cratis-switch__control {
2629
+ width: 2.5rem;
2630
+ height: 1.4rem;
2631
+ padding: 0.15rem;
2632
+ border-radius: 999px;
2633
+ background: var(--cratis-surface-100);
2634
+ transition: background-color 140ms ease;
2635
+ }
2636
+ .cratis-switch__handle {
2637
+ width: 1rem;
2638
+ height: 1rem;
2639
+ border-radius: 50%;
2640
+ background: var(--cratis-control-background);
2641
+ box-shadow: var(--cratis-shadow-subtle);
2642
+ transition: transform 140ms ease;
2643
+ }
2644
+ .cratis-choice__input:focus-visible
2645
+ + :is(.cratis-checkbox__box, .cratis-radio__box, .cratis-switch__control) {
2646
+ box-shadow: var(--cratis-focus-ring);
2647
+ }
2648
+ .cratis-choice__input:checked + .cratis-checkbox__box,
2649
+ .cratis-choice__input:checked + .cratis-switch__control {
2650
+ border-color: var(--cratis-primary-color);
2651
+ background: var(--cratis-primary-color);
2652
+ }
2653
+ .cratis-choice__input:checked + .cratis-checkbox__box .cratis-checkbox__indicator,
2654
+ .cratis-choice__input:checked + .cratis-radio__box .cratis-radio__indicator {
2655
+ opacity: 1;
2656
+ }
2657
+ .cratis-choice__input:checked + .cratis-radio__box {
2658
+ border-color: var(--cratis-primary-color);
2659
+ }
2660
+ .cratis-choice__input:checked + .cratis-switch__control .cratis-switch__handle {
2661
+ transform: translateX(1.05rem);
2662
+ }
2663
+ .cratis-choice:dir(rtl)
2664
+ .cratis-choice__input:checked
2665
+ + .cratis-switch__control
2666
+ .cratis-switch__handle {
2667
+ transform: translateX(-1.05rem);
2668
+ }
2669
+ .cratis-choice[data-invalid='true']
2670
+ :is(.cratis-checkbox__box, .cratis-radio__box, .cratis-switch__control) {
2671
+ border-color: var(--cratis-red-500);
2672
+ }
2673
+
2674
+ .cratis-surface {
2675
+ box-sizing: border-box;
2676
+ border: 1px solid var(--cratis-surface-border);
2677
+ border-radius: var(--cratis-border-radius);
2678
+ background: var(--cratis-surface-card);
2679
+ color: var(--cratis-text-color);
2680
+ box-shadow: var(--cratis-shadow-subtle);
2681
+ }
2682
+
2683
+ @media (prefers-reduced-motion: reduce) {
2684
+ .cratis-switch__control,
2685
+ .cratis-switch__handle {
2686
+ transition: none;
2687
+ }
2688
+ }
2689
+
2690
+ @media (forced-colors: active) {
2691
+ .cratis-text-input,
2692
+ .cratis-text-area,
2693
+ .cratis-checkbox__box,
2694
+ .cratis-radio__box,
2695
+ .cratis-switch__control,
2696
+ .cratis-surface {
2697
+ border-color: CanvasText;
2698
+ }
2699
+ .cratis-choice__input:checked
2700
+ + :is(.cratis-checkbox__box, .cratis-switch__control) {
2701
+ background: Highlight;
2702
+ }
2703
+ .cratis-choice__input:checked + .cratis-radio__box .cratis-radio__indicator {
2704
+ background: Highlight;
2705
+ }
2706
+ .cratis-choice__input:focus-visible
2707
+ + :is(.cratis-checkbox__box, .cratis-radio__box, .cratis-switch__control),
2708
+ .cratis-text-input:focus-visible,
2709
+ .cratis-text-area:focus-visible {
2710
+ outline: 2px solid Highlight;
2711
+ outline-offset: 2px;
2712
+ box-shadow: none;
2713
+ }
2714
+ }
2715
+
2716
+ /* ── ./Common/Tooltip.css ─────────────────────────────────────── */
2717
+ /* Copyright (c) Cratis. All rights reserved. */
2718
+ /* Licensed under the MIT license. See LICENSE file in the project root for full license information. */
2719
+
2720
+ /* ── Tooltip bubble ──────────────────────────────────────────────────────── */
2721
+ .cratis-tooltip-popup {
2722
+ background: var(--cratis-surface-100);
2723
+ color: var(--cratis-text-color);
2724
+ border: 1px solid var(--cratis-surface-border);
2725
+ box-shadow: var(--cratis-shadow-overlay);
2726
+ border-radius: var(--cratis-border-radius);
2727
+ padding: 0.25rem 0.5rem;
2728
+ font-size: 0.75rem;
2729
+ font-family: inherit;
2730
+ max-width: 24rem;
2731
+ z-index: var(--cratis-z-index-tooltip);
2732
+ }
2733
+
2734
+ /* ── ./Dialogs/Dialog.css ─────────────────────────────────────── */
2735
+ /* Copyright (c) Cratis. All rights reserved. */
2736
+ /* Licensed under the MIT license. See LICENSE file in the project root for full license information. */
2737
+
2738
+ .cratis-dialog__backdrop {
2739
+ position: fixed;
2740
+ inset: 0;
2741
+ z-index: var(--cratis-z-index-dialog);
2742
+ display: flex;
2743
+ align-items: center;
2744
+ justify-content: center;
2745
+ padding: 2rem;
2746
+ background: var(--cratis-maskbg);
2747
+ }
2748
+ .cratis-dialog__backdrop[data-entering] {
2749
+ animation: cratis-dialog-fade 160ms ease-out;
2750
+ }
2751
+ .cratis-dialog__backdrop[data-exiting] {
2752
+ animation: cratis-dialog-fade 120ms reverse ease-in;
2753
+ }
2754
+ .cratis-dialog__backdrop[data-placement='start'],
2755
+ .cratis-dialog__backdrop[data-placement='end'] {
2756
+ padding: 0;
2757
+ align-items: stretch;
2758
+ }
2759
+ .cratis-dialog__backdrop[data-placement='start'] {
2760
+ justify-content: flex-start;
2761
+ }
2762
+ .cratis-dialog__backdrop[data-placement='end'] {
2763
+ justify-content: flex-end;
2764
+ }
2765
+ .cratis-dialog__positioner {
2766
+ display: flex;
2767
+ max-width: 100%;
2768
+ max-height: 100%;
2769
+ align-items: center;
2770
+ justify-content: center;
2771
+ }
2772
+ .cratis-dialog {
2773
+ display: flex;
2774
+ max-width: min(100%, calc(100vw - 2rem));
2775
+ max-height: min(90vh, calc(100vh - 2rem));
2776
+ overflow: hidden;
2777
+ flex-direction: column;
2778
+ border: 1px solid var(--cratis-surface-border);
2779
+ border-radius: var(--cratis-border-radius);
2780
+ background: var(--cratis-surface-overlay);
2781
+ color: var(--cratis-text-color);
2782
+ box-shadow: var(--cratis-shadow-dialog);
2783
+ outline: none;
2784
+ }
2785
+ .cratis-dialog[data-placement='start'],
2786
+ .cratis-dialog[data-placement='end'] {
2787
+ height: 100%;
2788
+ max-width: 100%;
2789
+ max-height: 100%;
2790
+ border-radius: 0;
2791
+ border-block-width: 0;
2792
+ }
2793
+ .cratis-dialog[data-placement='start'] {
2794
+ --cratis-dialog-slide-from: -100%;
2795
+ border-inline-start-width: 0;
2796
+ }
2797
+ .cratis-dialog[data-placement='end'] {
2798
+ --cratis-dialog-slide-from: 100%;
2799
+ border-inline-end-width: 0;
2800
+ }
2801
+ .cratis-dialog[data-placement='start']:dir(rtl) {
2802
+ --cratis-dialog-slide-from: 100%;
2803
+ }
2804
+ .cratis-dialog[data-placement='end']:dir(rtl) {
2805
+ --cratis-dialog-slide-from: -100%;
2806
+ }
2807
+ .cratis-dialog[data-placement='start'][data-entering],
2808
+ .cratis-dialog[data-placement='end'][data-entering] {
2809
+ animation: cratis-dialog-slide 200ms ease-out;
2810
+ }
2811
+ .cratis-dialog[data-placement='start'][data-exiting],
2812
+ .cratis-dialog[data-placement='end'][data-exiting] {
2813
+ animation: cratis-dialog-slide 150ms reverse ease-in;
2814
+ }
2815
+ .cratis-dialog__document {
2816
+ display: flex;
2817
+ min-height: 0;
2818
+ flex: 1;
2819
+ flex-direction: column;
2820
+ outline: none;
2821
+ }
2822
+ .cratis-dialog__header {
2823
+ display: grid;
2824
+ grid-template-columns: minmax(0, 1fr) auto;
2825
+ padding: 1rem 1.25rem;
2826
+ align-items: center;
2827
+ column-gap: 1rem;
2828
+ flex: 0 0 auto;
2829
+ border-bottom: 1px solid var(--cratis-surface-border);
2830
+ }
2831
+ .cratis-dialog__title {
2832
+ grid-column: 1;
2833
+ justify-self: start;
2834
+ margin: 0;
2835
+ font-size: 1.1rem;
2836
+ font-weight: 700;
2837
+ outline: none;
2838
+ }
2839
+ .cratis-dialog__subtitle {
2840
+ grid-column: 1;
2841
+ margin: 0.25rem 0 0;
2842
+ color: var(--cratis-text-color-secondary);
2843
+ font-size: 0.9rem;
2844
+ }
2845
+ .cratis-dialog__title:focus-visible {
2846
+ border-radius: var(--cratis-border-radius);
2847
+ }
2848
+ .cratis-dialog__close {
2849
+ display: inline-flex;
2850
+ grid-column: 2;
2851
+ grid-row: 1;
2852
+ width: 2rem;
2853
+ height: 2rem;
2854
+ padding: 0;
2855
+ align-items: center;
2856
+ justify-content: center;
2857
+ border: 0;
2858
+ border-radius: 50%;
2859
+ background: transparent;
2860
+ color: var(--cratis-text-color-secondary);
2861
+ font-size: 1.25rem;
2862
+ cursor: pointer;
2863
+ }
2864
+ .cratis-dialog__close:hover {
2865
+ background: var(--cratis-surface-hover);
2866
+ color: var(--cratis-text-color);
2867
+ }
2868
+ .cratis-dialog__content {
2869
+ min-height: 0;
2870
+ padding: 1.25rem;
2871
+ overflow: auto;
2872
+ flex: 1 1 auto;
2873
+ }
2874
+ .cratis-dialog__busy-scope {
2875
+ display: contents;
2876
+ min-inline-size: 0;
2877
+ padding: 0;
2878
+ margin: 0;
2879
+ border: 0;
2880
+ }
2881
+ .cratis-dialog__footer {
2882
+ display: flex;
2883
+ padding: 0.9rem 1.25rem;
2884
+ justify-content: flex-start;
2885
+ flex-wrap: wrap;
2886
+ gap: 0.75rem;
2887
+ flex: 0 0 auto;
2888
+ border-top: 1px solid var(--cratis-surface-border);
2889
+ }
2890
+ .cratis-dialog__button {
2891
+ display: inline-flex;
2892
+ box-sizing: border-box;
2893
+ min-height: var(--cratis-control-height);
2894
+ padding: 0.75rem 1.25rem;
2895
+ align-items: center;
2896
+ justify-content: center;
2897
+ gap: 0.45rem;
2898
+ border: 1px solid var(--cratis-action-background);
2899
+ border-radius: var(--cratis-border-radius);
2900
+ font: inherit;
2901
+ font-weight: 600;
2902
+ cursor: pointer;
2903
+ }
2904
+ .cratis-dialog__button--primary {
2905
+ background: var(--cratis-action-background);
2906
+ color: var(--cratis-action-text);
2907
+ }
2908
+ .cratis-dialog__button--secondary {
2909
+ background: transparent;
2910
+ color: var(--cratis-action-background);
2911
+ }
2912
+ .cratis-dialog__button--primary:hover:not(:disabled) {
2913
+ border-color: var(--cratis-action-background-hover);
2914
+ background: var(--cratis-action-background-hover);
2915
+ }
2916
+ .cratis-dialog__button--primary:active:not(:disabled) {
2917
+ border-color: var(--cratis-action-background-active);
2918
+ background: var(--cratis-action-background-active);
2919
+ }
2920
+ .cratis-dialog__button--secondary:hover:not(:disabled) {
2921
+ background: color-mix(in srgb, var(--cratis-action-background) 4%, transparent);
2922
+ }
2923
+ .cratis-dialog__button--secondary:active:not(:disabled) {
2924
+ background: color-mix(in srgb, var(--cratis-action-background) 16%, transparent);
2925
+ }
2926
+ .cratis-dialog__title:focus-visible,
2927
+ .cratis-dialog__close:focus-visible,
2928
+ .cratis-dialog__button:focus-visible {
2929
+ outline: none;
2930
+ box-shadow: var(--cratis-focus-ring);
2931
+ }
2932
+ .cratis-dialog__button:disabled {
2933
+ cursor: not-allowed;
2934
+ opacity: var(--cratis-disabled-opacity);
2935
+ }
2936
+ .cratis-dialog__spinner {
2937
+ width: 1em;
2938
+ height: 1em;
2939
+ border: 2px solid currentColor;
2940
+ border-right-color: transparent;
2941
+ border-radius: 50%;
2942
+ animation: cratis-dialog-spin 700ms linear infinite;
2943
+ }
2944
+
2945
+ @keyframes cratis-dialog-fade {
2946
+ from {
2947
+ opacity: 0;
2948
+ }
2949
+ to {
2950
+ opacity: 1;
2951
+ }
2952
+ }
2953
+ @keyframes cratis-dialog-spin {
2954
+ to {
2955
+ transform: rotate(360deg);
2956
+ }
2957
+ }
2958
+ @keyframes cratis-dialog-slide {
2959
+ from {
2960
+ translate: var(--cratis-dialog-slide-from) 0;
2961
+ }
2962
+ }
2963
+
2964
+ @media (prefers-reduced-motion: reduce) {
2965
+ .cratis-dialog__backdrop,
2966
+ .cratis-dialog[data-placement],
2967
+ .cratis-dialog__spinner {
2968
+ animation: none;
2969
+ }
2970
+ }
2971
+
2972
+ @media (forced-colors: active) {
2973
+ .cratis-dialog,
2974
+ .cratis-dialog__button {
2975
+ border-color: CanvasText;
2976
+ }
2977
+ .cratis-dialog__close:focus-visible,
2978
+ .cratis-dialog__button:focus-visible {
2979
+ outline: 2px solid Highlight;
2980
+ outline-offset: 2px;
2981
+ box-shadow: none;
2982
+ }
2983
+ }
2984
+
2985
+ /* ── ./Notifications/Toaster.css ─────────────────────────────────────── */
2986
+ /* Copyright (c) Cratis. All rights reserved. */
2987
+ /* Licensed under the MIT license. See LICENSE file in the project root for full license information. */
2988
+
2989
+ .cratis-toaster {
2990
+ position: fixed;
2991
+ z-index: var(--cratis-z-index-toast);
2992
+ display: flex;
2993
+ width: min(26rem, calc(100vw - 2rem));
2994
+ flex-direction: column;
2995
+ gap: 0.75rem;
2996
+ pointer-events: none;
2997
+ }
2998
+ .cratis-toaster[data-position^='top'] {
2999
+ top: 1rem;
3000
+ }
3001
+ .cratis-toaster[data-position^='bottom'] {
3002
+ bottom: 1rem;
3003
+ }
3004
+ .cratis-toaster[data-position$='left'] {
3005
+ left: 1rem;
3006
+ }
3007
+ .cratis-toaster[data-position$='right'] {
3008
+ right: 1rem;
3009
+ }
3010
+ .cratis-toaster[data-position$='center'] {
3011
+ left: 50%;
3012
+ transform: translateX(-50%);
3013
+ }
3014
+
3015
+ .cratis-toast {
3016
+ display: flex;
3017
+ min-width: 18rem;
3018
+ max-width: 26rem;
3019
+ padding: 0.75rem 1rem;
3020
+ align-items: flex-start;
3021
+ gap: 0.625rem;
3022
+ border: 1px solid var(--cratis-surface-border);
3023
+ border-inline-start: 4px solid var(--cratis-surface-border);
3024
+ border-radius: var(--cratis-border-radius);
3025
+ background: var(--cratis-surface-overlay);
3026
+ color: var(--cratis-text-color);
3027
+ box-shadow: var(--cratis-shadow-toast);
3028
+ pointer-events: auto;
3029
+ }
3030
+ .cratis-toast__content {
3031
+ display: flex;
3032
+ min-width: 0;
3033
+ flex: 1;
3034
+ flex-direction: column;
3035
+ gap: 0.125rem;
3036
+ }
3037
+ .cratis-toast__title {
3038
+ font-weight: 600;
3039
+ }
3040
+ .cratis-toast__description {
3041
+ color: var(--cratis-text-color-secondary);
3042
+ font-size: 0.875rem;
3043
+ white-space: pre-line;
3044
+ }
3045
+ .cratis-toast__icon {
3046
+ flex: 0 0 auto;
3047
+ }
3048
+ .cratis-toast__close {
3049
+ display: inline-flex;
3050
+ width: 1.75rem;
3051
+ height: 1.75rem;
3052
+ padding: 0;
3053
+ align-items: center;
3054
+ justify-content: center;
3055
+ border: 0;
3056
+ border-radius: 50%;
3057
+ background: transparent;
3058
+ color: var(--cratis-text-color-secondary);
3059
+ cursor: pointer;
3060
+ }
3061
+ .cratis-toast__close:hover {
3062
+ background: var(--cratis-surface-hover);
3063
+ color: var(--cratis-text-color);
3064
+ }
3065
+ .cratis-toast__close:focus-visible {
3066
+ outline: none;
3067
+ box-shadow: var(--cratis-focus-ring);
3068
+ }
3069
+ .cratis-toast__action {
3070
+ align-self: flex-start;
3071
+ margin-top: 0.4rem;
3072
+ }
3073
+ .cratis-toast[data-loading='true'] .cratis-toast__icon {
3074
+ animation: cratis-toast-spin 800ms linear infinite;
3075
+ }
3076
+ .cratis-toast[data-severity='success'] {
3077
+ border-inline-start-color: var(--cratis-green-500);
3078
+ }
3079
+ .cratis-toast[data-severity='info'] {
3080
+ border-inline-start-color: var(--cratis-primary-color);
3081
+ }
3082
+ .cratis-toast[data-severity='warn'] {
3083
+ border-inline-start-color: var(--cratis-orange-500);
3084
+ }
3085
+ .cratis-toast[data-severity='error'] {
3086
+ border-inline-start-color: var(--cratis-red-500);
3087
+ }
3088
+ .cratis-toast[data-severity='success'] .cratis-toast__icon {
3089
+ color: var(--cratis-green-500);
3090
+ }
3091
+ .cratis-toast[data-severity='info'] .cratis-toast__icon {
3092
+ color: var(--cratis-primary-color);
3093
+ }
3094
+ .cratis-toast[data-severity='warn'] .cratis-toast__icon {
3095
+ color: var(--cratis-orange-500);
3096
+ }
3097
+ .cratis-toast[data-severity='error'] .cratis-toast__icon {
3098
+ color: var(--cratis-red-500);
3099
+ }
3100
+
3101
+ @keyframes cratis-toast-spin {
3102
+ to {
3103
+ transform: rotate(360deg);
3104
+ }
3105
+ }
3106
+ @media (prefers-reduced-motion: reduce) {
3107
+ .cratis-toast__icon {
3108
+ animation: none !important;
3109
+ }
3110
+ }
3111
+ @media (forced-colors: active) {
3112
+ .cratis-toast {
3113
+ border-color: CanvasText;
3114
+ }
3115
+ .cratis-toast__close:focus-visible {
3116
+ outline: 2px solid Highlight;
3117
+ outline-offset: 2px;
3118
+ box-shadow: none;
3119
+ }
3120
+ }
3121
+
3122
+ }