1@import "tailwindcss";
2
3@font-face {
4 font-family: "AT Name Sans";
5 src: url("/fonts/private/ATNameSans-Regular.woff2") format("woff2");
6 font-weight: 1 1000;
7 font-style: normal;
8 font-display: swap;
9}
10
11@font-face {
12 font-family: "AT Name Sans";
13 src: url("/fonts/private/ATNameSans-Italic.woff2") format("woff2");
14 font-weight: 1 1000;
15 font-style: italic;
16 font-display: swap;
17}
18
19@font-face {
20 font-family: "AT Name Mono";
21 src: url("/fonts/private/ATNameMono-Regular.woff2") format("woff2");
22 font-weight: 400 900;
23 font-style: normal;
24 font-display: swap;
25}
26
27@font-face {
28 font-family: "AT Name Mono";
29 src: url("/fonts/private/ATNameMono-Italic.woff2") format("woff2");
30 font-weight: 400 900;
31 font-style: italic;
32 font-display: swap;
33}
34
35@font-face {
36 font-family: "Recursive";
37 src: url("./assets/fonts/Recursive_VF_1.085.woff2") format("woff2");
38 font-weight: 300 1000;
39 font-display: swap;
40}
41
42/* Box-drawing glyphs share the 0.6em cell width of the code font. */
43@font-face {
44 font-family: "T3 Markdown Symbols";
45 src: url("@clo/terminal/fonts/__clo_terminal_primary_34_8_1-Regular.woff2") format("woff2");
46 unicode-range: U+2500-259F;
47 font-display: swap;
48}
49
50@custom-variant dark (&:is(.dark, .dark *));
51@custom-variant light (&:not(.dark, .dark *));
52/* Window Controls Overlay: active when Electron exposes native titlebar control geometry. */
53@custom-variant wco (&:is(.wco, .wco *));
54
55/* Secondary hovering pointers reveal sidebar actions on touch-primary devices. */
56@custom-variant any-hover {
57 @media (any-hover: hover) {
58 &:hover {
59 @slot;
60 }
61 }
62}
63
64/* On mobile, morph the expanded hero composer into the compact docked
65 composer while it moves; the rest of the app remains visually stationary. */
66html[data-mobile-composer-route-transition="true"]::view-transition-old(root),
67html[data-mobile-composer-route-transition="true"]::view-transition-new(root) {
68 animation: none;
69}
70
71html[data-mobile-composer-route-transition="true"]::view-transition-group(t3-mobile-composer) {
72 animation-duration: var(--mobile-composer-transition-duration);
73 animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
74}
75
76html[data-mobile-composer-route-transition="true"]::view-transition-image-pair(t3-mobile-composer) {
77 isolation: isolate;
78}
79
80html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobile-composer) {
81 animation: t3-mobile-composer-old var(--mobile-composer-transition-duration) linear both;
82 mix-blend-mode: normal;
83}
84
85html[data-mobile-composer-route-transition="true"]::view-transition-new(t3-mobile-composer) {
86 animation: t3-mobile-composer-new var(--mobile-composer-transition-duration) linear both;
87 mix-blend-mode: normal;
88}
89
90/* The expanded and compact composers have different internal layouts. Use a
91 brief crossfade while their shared wrapper moves so the layout change does
92 not read as a single-frame cut. */
93@keyframes t3-mobile-composer-old {
94 0%,
95 35% {
96 opacity: 1;
97 }
98 65%,
99 100% {
100 opacity: 0;
101 }
102}
103
104@keyframes t3-mobile-composer-new {
105 0%,
106 35% {
107 opacity: 0;
108 }
109 65%,
110 100% {
111 opacity: 1;
112 }
113}
114
115html[data-mobile-composer-route-transition="true"]::view-transition-group(t3-mobile-draft-headline) {
116 animation-duration: var(--mobile-composer-transition-duration);
117}
118
119html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobile-draft-headline) {
120 animation: t3-mobile-draft-headline-exit var(--mobile-composer-transition-duration)
121 cubic-bezier(0.4, 0, 1, 1) both;
122 mix-blend-mode: normal;
123}
124
125@keyframes t3-mobile-draft-headline-exit {
126 from {
127 opacity: 1;
128 transform: translateY(0);
129 }
130 to {
131 opacity: 0;
132 transform: translateY(-6px);
133 }
134}
135
136:root {
137 --clo-term-font: "AT Name Mono";
138 --app-scrollbar-width: 6px;
139 --appearance-contrast-base: 100%;
140 --appearance-contrast-boost: 0%;
141 --appearance-contrast-border-boost: 0%;
142 --appearance-contrast-target: black;
143 --app-scrollbar-thumb: rgb(217 217 217);
144 --app-scrollbar-thumb-hover: rgb(191 191 191);
145 /*
146 * Compact UI geometry. Keep these values semantic so sidebar, palette,
147 * tooltip, and toolbar controls cannot quietly drift apart.
148 */
149 --control-radius: 0.5rem;
150 --sidebar-content-inset: 0.5rem;
151 --sidebar-control-gap: 0.5rem;
152 --sidebar-icon-color: color-mix(
153 in srgb,
154 var(--contrast-sidebar-muted-foreground) 60%,
155 var(--sidebar)
156 );
157 --sidebar-row-content-inset: 0.625rem;
158 --command-shell-inset: 0.5rem;
159 --command-content-inset: 1rem;
160 --floating-content-inset: 0.75rem;
161 /* App layer order: docked sheets, then the floating preview player (47–49, see
162 previewMiniPlayerLayout.ts), then dialogs and popovers (50 and up). */
163 --z-sheet: 46;
164 --glass-blur: 12px;
165 --glass-opacity: 80%;
166 --glass-saturation: 1.14;
167 --desktop-window-right-resize-inset: 0px;
168 --workspace-topbar-height: 52px;
169 --workspace-controls-top: 0px;
170 --workspace-controls-left: calc(env(safe-area-inset-left) + 0.75rem);
171 --workspace-controls-right: calc(env(safe-area-inset-right) + 0.75rem);
172 --workspace-native-controls-inset: 0px;
173 --workspace-titlebar-control-size: 1.75rem;
174 --workspace-titlebar-control-gap: 0.75rem;
175 --workspace-titlebar-scroll-fade-height: 1.5rem;
176 --chat-content-max-width: 46rem;
177 --thread-details-panel-width: 17.5rem;
178 /* The prompt on phones: iOS zooms into any input under 16px on focus. */
179 --font-size-prompt-touch: max(var(--font-size-prompt, 1rem), 16px);
180 /* Horizontal gutter shared by the page header and the chat column, clear of
181 the device's safe area. */
182 --workspace-gutter: 0.75rem;
183 --workspace-gutter-start: calc(env(safe-area-inset-left) + var(--workspace-gutter));
184 --workspace-gutter-end: calc(env(safe-area-inset-right) + var(--workspace-gutter));
185
186 @variant sm {
187 --workspace-gutter: 1.25rem;
188 }
189
190 @variant dark {
191 --appearance-contrast-target: white;
192 --app-scrollbar-thumb: rgb(255 255 255 / 8%);
193 --app-scrollbar-thumb-hover: rgb(255 255 255 / 12%);
194 --glass-blur: 16px;
195 --glass-saturation: 1.08;
196 }
197}
198
199.wco {
200 --workspace-topbar-height: max(40px, env(titlebar-area-height, 52px));
201 --workspace-controls-top: env(titlebar-area-y, 0px);
202 --workspace-controls-left: calc(env(titlebar-area-x, 0px) + 0.75rem);
203 --workspace-controls-right: calc(
204 100vw - env(titlebar-area-width, 100vw) - env(titlebar-area-x, 0px) + 0.75rem
205 );
206 --workspace-native-controls-inset: calc(
207 100vw - env(titlebar-area-width, 100vw) - env(titlebar-area-x, 0px) + 0.75rem
208 );
209}
210
211/* The font tokens are declared outside the inline theme so utilities reference
212 the variables and Settings -> Appearance can override them at runtime. The
213 default stacks are mirrored in `appearanceFonts.ts`. */
214@theme {
215 --font-sans: "AT Name Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
216 --font-mono:
217 "AT Name Mono", "SF Mono", "SFMono-Regular", Menlo, Consolas, "Liberation Mono", monospace;
218}
219
220@theme inline {
221 /* Two steps below Tailwind's text-xs (12px) for dense UI: metadata rows,
222 badges, counters, keyboard hints and diagnostics tables. 2xs is 11px,
223 3xs is 10px. Provider icon overlays use 4xs (8px) and 5xs (7px) to fit
224 their smaller badges. Registered with tailwind-merge in
225 lib/utils.ts so cn() treats them as font sizes, not colours. */
226 --text-2xs: 0.6875rem;
227 --text-2xs--line-height: calc(1 / 0.6875);
228 --text-3xs: 0.625rem;
229 --text-3xs--line-height: calc(0.875 / 0.625);
230 --text-4xs: 0.5rem;
231 --text-4xs--line-height: 1;
232 --text-5xs: 0.4375rem;
233 --text-5xs--line-height: 1;
234 /* Motion. drawer is the decelerating curve for surfaces that slide or pop in
235 (composer context, pills). */
236 --inset-shadow-control-highlight: 0 1px rgb(255 255 255 / 16%);
237 --inset-shadow-control-pressed: 0 1px rgb(0 0 0 / 8%);
238 --ease-drawer: cubic-bezier(0.32, 0.72, 0, 1);
239 /* The composer's elevation: a soft drop pulled in under the surface, shared
240 by the composer, its context strip and attached banners. */
241 --shadow-composer: 0 12px 28px -18px rgb(0 0 0 / 40%);
242 --shadow-composer-dark: 0 14px 32px -18px rgb(0 0 0 / 75%);
243 /* Dark-mode shade where an attached surface meets the composer: 10px of
244 shadow fading away from the seam. Inline, so the offsets resolve on the
245 element. below: the context strip under the composer (1rem seam, faint
246 white fill). above: an attached banner over it (its tint on top). */
247 --background-image-composer-seam-below:
248 linear-gradient(
249 to bottom,
250 transparent 0 1rem,
251 rgb(0 0 0 / 18%) 1rem,
252 transparent calc(1rem + 10px)
253 ),
254 linear-gradient(rgb(255 255 255 / 1%), rgb(255 255 255 / 1%));
255 --background-image-composer-seam-above:
256 linear-gradient(var(--chat-composer-attached-tint), var(--chat-composer-attached-tint)),
257 linear-gradient(
258 to top,
259 transparent 0 var(--chat-composer-attachment-overlap),
260 rgb(0 0 0 / 18%) var(--chat-composer-attachment-overlap),
261 transparent calc(var(--chat-composer-attachment-overlap) + 10px)
262 );
263 --color-zinc-25: oklch(99.2% 0 0);
264 --animate-skeleton: skeleton 2.4s infinite;
265 /* Duty-cycled indicator animations: long holds with stepped ramps, so the
266 compositor updates discrete frames instead of every vsync. */
267 --animate-status-pulse: status-pulse 2s infinite;
268 --animate-status-ping: status-ping 2s infinite;
269 --color-warning-foreground: var(--warning-foreground);
270 --color-warning: var(--warning);
271 --color-warning-surface: var(--warning-surface);
272 --color-error-foreground: var(--error-foreground);
273 --color-tool-error-icon: var(--tool-error-icon);
274 --color-error: var(--error);
275 --color-error-surface: var(--error-surface);
276 --color-update-foreground: var(--update-foreground);
277 --color-update: var(--update);
278 --color-update-surface: var(--update-surface);
279 --color-success-foreground: var(--success-foreground);
280 --color-success: var(--success);
281 --color-diff-addition: var(--diff-addition);
282 --color-diff-deletion: var(--diff-deletion);
283 --color-diff-addition-foreground: var(--diff-addition-foreground);
284 --color-diff-deletion-foreground: var(--diff-deletion-foreground);
285 --color-info-foreground: var(--info-foreground);
286 --color-info: var(--info);
287 --color-destructive-foreground: var(--destructive-foreground);
288 --color-ring: var(--ring);
289 --color-input: var(--contrast-input);
290 --color-border: var(--contrast-border);
291 --color-destructive: var(--destructive);
292 --color-accent-foreground: var(--contrast-accent-foreground);
293 --color-accent: var(--accent);
294 --color-muted-foreground: var(--contrast-muted-foreground);
295 --color-muted: var(--muted);
296 --color-placeholder: var(--contrast-placeholder);
297 --color-secondary-label: var(--contrast-secondary-label);
298 --color-icon-muted: var(--contrast-icon-muted);
299 --color-secondary-foreground: var(--contrast-secondary-foreground);
300 --color-secondary: var(--secondary);
301 --color-primary-foreground: var(--primary-foreground);
302 --color-primary: var(--primary);
303 --color-popover-foreground: var(--contrast-popover-foreground);
304 --color-popover: var(--popover);
305 --color-card-foreground: var(--contrast-card-foreground);
306 --color-card: var(--card);
307 --color-code: var(--code-background);
308 --color-code-foreground: var(--code-foreground);
309 --color-foreground: var(--contrast-foreground);
310 --color-background: var(--background);
311 --color-surface-raised: var(--surface-raised);
312 --color-message: var(--message-surface);
313 --color-message-foreground: var(--contrast-message-foreground);
314 --color-message-action: var(--message-action);
315 --color-message-action-foreground: var(--message-action-foreground);
316 --color-message-action-hover: var(--message-action-hover);
317 --color-sidebar: var(--sidebar);
318 --color-sidebar-foreground: var(--contrast-sidebar-foreground);
319 --color-sidebar-muted-foreground: var(--contrast-sidebar-muted-foreground);
320 --color-sidebar-control-surface: var(--sidebar-control-surface);
321 --color-sidebar-row-hover: var(--sidebar-row-hover);
322 --color-sidebar-row-active: var(--sidebar-row-active);
323 --color-sidebar-row-selected: var(--sidebar-row-selected);
324 --color-sidebar-border: var(--contrast-sidebar-border);
325 --radius-sm: calc(var(--radius) - 4px);
326 --radius-md: calc(var(--radius) - 2px);
327 --radius-lg: var(--radius);
328 --radius-xl: calc(var(--radius) + 4px);
329 --radius-2xl: calc(var(--radius) + 8px);
330 --radius-3xl: calc(var(--radius) + 12px);
331 @keyframes skeleton {
332 /* The single loading-bar breath used by every skeleton: one opacity pulse
333 per container, stepped so however many bars sit under it, the compositor
334 draws a handful of discrete frames per cycle rather than one per vsync —
335 which on a 120Hz display is the difference between ~14 and ~288 updates. */
336 0%,
337 42% {
338 opacity: 1;
339 animation-timing-function: steps(4);
340 }
341 50%,
342 92% {
343 opacity: 0.55;
344 animation-timing-function: steps(4);
345 }
346 100% {
347 opacity: 1;
348 }
349 }
350 @keyframes status-pulse {
351 0%,
352 40% {
353 opacity: 1;
354 animation-timing-function: steps(6);
355 }
356 50%,
357 90% {
358 opacity: 0.5;
359 animation-timing-function: steps(6);
360 }
361 100% {
362 opacity: 1;
363 }
364 }
365 @keyframes status-ping {
366 /* Burst first (immediate feedback for click ripples), then hold
367 invisible for the rest of the cycle. Mirrors animate-ping's
368 75%-scale start. */
369 0% {
370 opacity: 0.9;
371 scale: 0.75;
372 animation-timing-function: steps(8);
373 }
374 40%,
375 100% {
376 opacity: 0;
377 scale: 2;
378 }
379 }
380}
381
382@utility surface-glass {
383 background: color-mix(in srgb, var(--background) var(--glass-opacity), transparent);
384 -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));
385 backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));
386
387 @supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
388 background: var(--background) !important;
389 }
390}
391
392@utility alert-glass {
393 --alert-glass-tint: transparent;
394 background:
395 linear-gradient(
396 color-mix(in srgb, var(--alert-glass-tint) 4%, transparent),
397 color-mix(in srgb, var(--alert-glass-tint) 4%, transparent)
398 ),
399 color-mix(in srgb, var(--background) var(--glass-opacity), transparent) !important;
400 -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));
401 backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));
402
403 &[data-variant="error"] {
404 --alert-glass-tint: var(--destructive);
405 }
406
407 &[data-variant="info"] {
408 --alert-glass-tint: var(--info);
409 }
410
411 &[data-variant="success"] {
412 --alert-glass-tint: var(--success);
413 }
414
415 &[data-variant="warning"] {
416 --alert-glass-tint: var(--warning);
417 }
418
419 @supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
420 background: var(--background) !important;
421 }
422}
423
424/* Paint find results without changing rendered markdown or copied text. */
425::highlight(t3-thread-find) {
426 color: var(--app-theme-search-match-foreground, oklch(0.216164 0.028283 77.423));
427 background-color: var(--app-theme-search-match-background, oklch(0.915 0.11 85));
428
429 @variant dark {
430 color: var(--app-theme-search-match-foreground, oklch(0.970151 0 0));
431 background-color: var(--app-theme-search-match-background, oklch(0.55 0.1 85));
432 }
433}
434
435::highlight(t3-thread-find-active) {
436 color: var(--app-theme-search-match-active-foreground, oklch(0.216164 0.028283 77.423));
437 background-color: var(
438 --app-theme-search-match-active-background,
439 oklch(0.774202 0.151946 74.331)
440 );
441}
442
443@utility dialog-glass {
444 background: color-mix(in srgb, var(--background) var(--glass-opacity), transparent);
445 -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));
446 backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));
447 border-color: color-mix(in srgb, var(--contrast-foreground) 10%, transparent);
448 box-shadow: 0 24px 64px -24px rgb(0 0 0 / 65%);
449
450 @variant dark {
451 border-color: color-mix(in srgb, var(--color-white) 8%, transparent);
452 box-shadow:
453 inset 0 1px rgb(255 255 255 / 4%),
454 0 24px 72px -20px rgb(0 0 0 / 90%);
455 }
456
457 @supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
458 background: var(--popover) !important;
459 }
460}
461
462@utility dialog-backdrop {
463 background: color-mix(in srgb, var(--background) 60%, transparent);
464 -webkit-backdrop-filter: blur(4px);
465 backdrop-filter: blur(4px);
466
467 @variant dark {
468 background: color-mix(in srgb, var(--background) 64%, transparent);
469 }
470}
471
472@utility dropdown-glass {
473 background: color-mix(
474 in srgb,
475 var(--popover) 18%,
476 color-mix(in srgb, var(--popover) var(--glass-opacity), transparent)
477 );
478 -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));
479 backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));
480 border: 1px solid color-mix(in srgb, var(--contrast-foreground) 10%, transparent);
481
482 @supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
483 background: var(--popover) !important;
484 }
485}
486
487@utility topbar-scroll-fade {
488 -webkit-mask-image:
489 linear-gradient(
490 to bottom,
491 transparent 0%,
492 rgb(0 0 0 / 10%) 10%,
493 rgb(0 0 0 / 30%) 24%,
494 rgb(0 0 0 / 58%) 42%,
495 rgb(0 0 0 / 82%) 62%,
496 rgb(0 0 0 / 96%) 82%,
497 black 100%
498 ),
499 linear-gradient(black, black), linear-gradient(black, black);
500 -webkit-mask-position: top, bottom, right;
501 -webkit-mask-repeat: no-repeat;
502 -webkit-mask-size:
503 100% calc(var(--workspace-titlebar-scroll-fade-height) + 1px),
504 100% calc(100% - var(--workspace-titlebar-scroll-fade-height)),
505 var(--app-scrollbar-width) 100%;
506 mask-image:
507 linear-gradient(
508 to bottom,
509 transparent 0%,
510 rgb(0 0 0 / 10%) 10%,
511 rgb(0 0 0 / 30%) 24%,
512 rgb(0 0 0 / 58%) 42%,
513 rgb(0 0 0 / 82%) 62%,
514 rgb(0 0 0 / 96%) 82%,
515 black 100%
516 ),
517 linear-gradient(black, black), linear-gradient(black, black);
518 mask-position: top, bottom, right;
519 mask-repeat: no-repeat;
520 mask-size:
521 100% calc(var(--workspace-titlebar-scroll-fade-height) + 1px),
522 100% calc(100% - var(--workspace-titlebar-scroll-fade-height)),
523 var(--app-scrollbar-width) 100%;
524}
525
526/* Virtualizers own their native scroll element, so they cannot use ScrollArea's
527 viewport fade. Keep the scrollbar lane opaque while sharing the same fade
528 contract across those lists. */
529@utility virtualized-scroll-fade {
530 -webkit-mask-image: var(--virtualized-scroll-fade-mask), linear-gradient(black, black);
531 mask-image: var(--virtualized-scroll-fade-mask), linear-gradient(black, black);
532 -webkit-mask-position: left, right;
533 mask-position: left, right;
534 -webkit-mask-repeat: no-repeat;
535 mask-repeat: no-repeat;
536 -webkit-mask-size:
537 calc(100% - var(--app-scrollbar-width)) 100%,
538 var(--app-scrollbar-width) 100%;
539 mask-size:
540 calc(100% - var(--app-scrollbar-width)) 100%,
541 var(--app-scrollbar-width) 100%;
542}
543
544@keyframes live-tool-shine {
545 from {
546 background-position:
547 -4.5rem 0,
548 0 0;
549 }
550 to {
551 background-position:
552 calc(100% + 4.5rem) 0,
553 0 0;
554 }
555}
556
557@utility live-tool-shine {
558 @media (prefers-reduced-motion: no-preference) and (forced-colors: none) {
559 color: transparent;
560 background-image:
561 linear-gradient(to right, transparent, var(--contrast-foreground), transparent),
562 linear-gradient(var(--contrast-secondary-label), var(--contrast-secondary-label));
563 background-size:
564 4.5rem 100%,
565 100% 100%;
566 background-repeat: no-repeat;
567 background-clip: text;
568 animation: live-tool-shine 2.2s steps(30) infinite;
569 animation-play-state: var(--visible-animation-state, paused);
570 }
571}
572
573@utility visible-animate-spin {
574 animation: var(--animate-spin);
575 animation-play-state: var(--visible-animation-state, paused);
576 will-change: var(--visible-animation-will-change, auto);
577}
578
579@keyframes live-activity-focus {
580 from {
581 transform: translateX(0);
582 }
583 to {
584 transform: translateX(100%);
585 }
586}
587
588@keyframes live-activity-focus-counter {
589 from {
590 transform: translateX(0);
591 }
592 to {
593 transform: translateX(-100%);
594 }
595}
596
597@utility live-activity-focus {
598 --live-activity-focus-width: 4.5rem;
599
600 right: auto;
601 left: calc(-1 * var(--live-activity-focus-width));
602 width: calc(100% + var(--live-activity-focus-width) + var(--live-activity-focus-width));
603 -webkit-mask-image: linear-gradient(
604 to right,
605 transparent 0,
606 rgb(0 0 0 / 12%) 0.675rem,
607 rgb(0 0 0 / 55%) 1.575rem,
608 black 2.25rem,
609 rgb(0 0 0 / 55%) 2.925rem,
610 rgb(0 0 0 / 12%) 3.825rem,
611 transparent var(--live-activity-focus-width),
612 transparent 100%
613 );
614 -webkit-mask-repeat: no-repeat;
615 mask-image: linear-gradient(
616 to right,
617 transparent 0,
618 rgb(0 0 0 / 12%) 0.675rem,
619 rgb(0 0 0 / 55%) 1.575rem,
620 black 2.25rem,
621 rgb(0 0 0 / 55%) 2.925rem,
622 rgb(0 0 0 / 12%) 3.825rem,
623 transparent var(--live-activity-focus-width),
624 transparent 100%
625 );
626 mask-repeat: no-repeat;
627 animation: live-activity-focus 2.2s steps(30) infinite;
628 animation-play-state: var(--visible-animation-state, paused);
629 will-change: var(--visible-animation-will-change, auto);
630
631 @media (prefers-reduced-motion: reduce), (forced-colors: active) {
632 animation: none;
633 opacity: 0;
634 will-change: auto;
635 }
636}
637
638@utility live-activity-focus-counter {
639 width: 100%;
640 animation: live-activity-focus-counter 2.2s steps(30) infinite;
641 animation-play-state: var(--visible-animation-state, paused);
642 will-change: var(--visible-animation-will-change, auto);
643
644 @media (prefers-reduced-motion: reduce), (forced-colors: active) {
645 animation: none;
646 will-change: auto;
647 }
648}
649
650@utility live-activity-focus-aligned {
651 width: calc(100% - var(--live-activity-focus-width) - var(--live-activity-focus-width));
652 margin-left: var(--live-activity-focus-width);
653}
654
655@property --assistant-citation-highlight-opacity {
656 syntax: "<number>";
657 inherits: true;
658 initial-value: 0;
659}
660
661@layer base {
662 ::highlight(t3-assistant-citation) {
663 background-color: color-mix(
664 in oklab,
665 var(--primary) calc(var(--assistant-citation-highlight-opacity) * 100%),
666 transparent
667 );
668 }
669 ::highlight(t3-assistant-citation-comment) {
670 background-color: color-mix(in oklab, var(--primary) 45%, transparent);
671 }
672 :root {
673 /* Keep the original T3 Code artwork palettes as the defaults. Built-in
674 themes override them with their own pigments in the components layer. */
675 --stage-art-top: oklch(0.782169 0.123386 240.226);
676 --stage-art-mid: oklch(0.616111 0.195824 259.735);
677 --stage-art-bottom: oklch(0.441553 0.232394 265.474);
678 --stage-art-highlight: oklch(0.951597 0.037289 215.482);
679 --stage-art-secondary: oklch(0.794668 0.12136 235.46);
680 --stage-art-tertiary: oklch(0.678991 0.170261 275.365);
681 --stage-art-line: oklch(0.959666 0.029238 218.179);
682 --stage-art-celeste-highlight: oklch(0.968763 0.045822 196.42);
683 --stage-art-celeste-secondary: oklch(0.827395 0.126071 211.26);
684 --stage-art-violet-highlight: oklch(0.895381 0.053248 286.447);
685 --stage-art-grid-line: oklch(0.966822 0.01757 239.99);
686 --stage-night-base-top: oklch(0.283792 0.117327 297.201);
687 --stage-night-base-mid: oklch(0.227147 0.086086 277.99);
688 --stage-night-base-bottom: oklch(0.200528 0.055699 261.216);
689 --stage-night-top: var(--stage-night-base-top);
690 --stage-night-mid: var(--stage-night-base-mid);
691 --stage-night-bottom: var(--stage-night-base-bottom);
692 --stage-night-highlight: oklch(0.707246 0.157418 252.091);
693 --stage-night-secondary: oklch(0.600473 0.182225 277.296);
694 --stage-night-tertiary: oklch(0.62583 0.210886 305.994);
695 --stage-night-line: oklch(0.938794 0.029114 273.103);
696 --stage-night-glow-highlight: oklch(0.553749 0.176543 271.958);
697 --stage-night-glow-secondary: oklch(0.345571 0.117466 273.568);
698 --stage-night-sparkle: oklch(0.880867 0.057747 269.011);
699
700 @variant dark {
701 --stage-art-top: oklch(0.581473 0.149124 256.9);
702 --stage-art-mid: oklch(0.456509 0.159377 261.945);
703 --stage-art-bottom: oklch(0.291327 0.136578 267.649);
704 }
705 }
706
707 * {
708 @apply border-border outline-ring/50;
709 }
710 /* The app's 2px outlines stay inside controls at clipped and scrollport edges. */
711 *,
712 ::before,
713 ::after {
714 outline-offset: -2px;
715 }
716 :where([data-slot="menu-popup"], [data-slot="select-popup"], [data-slot="popover-popup"]):focus,
717 :where(
718 [data-slot="menu-popup"],
719 [data-slot="select-popup"],
720 [data-slot="popover-popup"]
721 ):focus-visible {
722 @apply outline-none ring-0;
723 }
724 html,
725 body {
726 background-color: var(--app-chrome-background);
727 }
728 body {
729 @apply text-foreground relative;
730 }
731}
732
733@layer components {
734 /* AnimatedHeight owns resizing for its contents. Nested collapsibles must
735 expose their final layout immediately, otherwise each animation frame
736 restarts the parent's transition and the dialog trails behind its content. */
737 [data-slot="animated-height"] [data-slot="collapsible-panel"] {
738 height: auto !important;
739 transition: none !important;
740 }
741
742 [data-slot="animated-height"] [data-slot="collapsible-panel"][data-closed] {
743 display: none;
744 }
745
746 /* Each maintainer palette gives the same line art its own material: rose
747 vellum, forest drafting paper, marine cyanotype, copper, and violet ink.
748 These colors stay deliberately deep at the top edge so the white stage
749 wordmark and controls retain contrast in both appearance modes. */
750 html[data-theme-id="t3-chat"] {
751 --stage-art-top: oklch(0.760675 0.141361 348.019);
752 --stage-art-mid: oklch(0.588093 0.188832 353.138);
753 --stage-art-bottom: oklch(0.383959 0.135096 348.656);
754 --stage-art-highlight: oklch(0.949689 0.034241 339.171);
755 --stage-art-secondary: oklch(0.763402 0.163836 352.525);
756 --stage-art-tertiary: oklch(0.70819 0.180285 311.949);
757 --stage-art-line: oklch(0.952158 0.034194 336.179);
758
759 @variant dark {
760 --stage-art-top: oklch(0.540689 0.143665 347.587);
761 --stage-art-mid: oklch(0.396586 0.126592 347.6);
762 --stage-art-bottom: oklch(0.249959 0.079694 340.523);
763 --stage-art-highlight: oklch(0.921297 0.051708 343.229);
764 --stage-art-secondary: oklch(0.667398 0.165674 352.549);
765 --stage-art-tertiary: oklch(0.609315 0.163722 306.315);
766 --stage-art-line: oklch(0.945349 0.036045 341.433);
767 }
768 }
769
770 html[data-theme-id="grove"] {
771 --stage-art-top: oklch(0.751465 0.112731 157.313);
772 --stage-art-mid: oklch(0.567105 0.11099 158.859);
773 --stage-art-bottom: oklch(0.367918 0.067635 160.558);
774 --stage-art-highlight: oklch(0.976387 0.034353 161.456);
775 --stage-art-secondary: oklch(0.80968 0.117071 158.817);
776 --stage-art-tertiary: oklch(0.784102 0.108047 88.516);
777 --stage-art-line: oklch(0.981098 0.027812 158.718);
778 --stage-night-top: oklch(0.45805 0.079616 160.092);
779 --stage-night-mid: oklch(0.368483 0.065631 162.719);
780 --stage-night-bottom: oklch(0.264797 0.044397 168.088);
781 --stage-night-highlight: oklch(0.912563 0.055173 158.974);
782 --stage-night-secondary: oklch(0.665652 0.109731 156.599);
783 --stage-night-tertiary: oklch(0.698651 0.103024 89.828);
784 --stage-night-line: oklch(0.945336 0.041923 157.222);
785
786 @variant dark {
787 --stage-art-top: oklch(0.58719 0.09869 157.426);
788 --stage-art-mid: oklch(0.454979 0.079031 159.756);
789 --stage-art-bottom: oklch(0.297856 0.050355 161.167);
790 --stage-art-highlight: oklch(0.952407 0.053872 158.44);
791 --stage-art-secondary: oklch(0.732591 0.120606 155.853);
792 --stage-art-tertiary: oklch(0.716282 0.116547 80.563);
793 --stage-art-line: oklch(0.961577 0.035285 157.03);
794 --stage-night-top: oklch(0.398632 0.065534 158.601);
795 --stage-night-mid: oklch(0.290561 0.049694 160.456);
796 --stage-night-bottom: oklch(0.210147 0.03173 169.818);
797 --stage-night-highlight: oklch(0.866303 0.057526 156.796);
798 --stage-night-secondary: oklch(0.586553 0.093722 157.365);
799 --stage-night-tertiary: oklch(0.6364 0.101769 82.985);
800 --stage-night-line: oklch(0.913292 0.035718 156.976);
801 }
802 }
803
804 html[data-theme-id="ocean"] {
805 --stage-art-top: oklch(0.792993 0.088072 220.421);
806 --stage-art-mid: oklch(0.590296 0.116841 243.184);
807 --stage-art-bottom: oklch(0.409123 0.093718 247.98);
808 --stage-art-highlight: oklch(0.971217 0.023008 213.615);
809 --stage-art-secondary: oklch(0.788391 0.090856 215.684);
810 --stage-art-tertiary: oklch(0.76441 0.099607 187.893);
811 --stage-art-line: oklch(0.976025 0.019647 212.543);
812
813 @variant dark {
814 --stage-art-top: oklch(0.59663 0.089167 233.427);
815 --stage-art-mid: oklch(0.461094 0.084904 243.478);
816 --stage-art-bottom: oklch(0.294818 0.05947 250.526);
817 --stage-art-highlight: oklch(0.952907 0.032224 221.27);
818 --stage-art-secondary: oklch(0.732079 0.09296 224.414);
819 --stage-art-tertiary: oklch(0.720885 0.095495 190.903);
820 --stage-art-line: oklch(0.961039 0.027355 219.756);
821 }
822 }
823
824 html[data-theme-id="ember"] {
825 --stage-art-top: oklch(0.782439 0.118806 52.569);
826 --stage-art-mid: oklch(0.62863 0.15922 39.401);
827 --stage-art-bottom: oklch(0.404251 0.117664 30.424);
828 --stage-art-highlight: oklch(0.962469 0.02774 70.95);
829 --stage-art-secondary: oklch(0.815735 0.11904 53.659);
830 --stage-art-tertiary: oklch(0.69361 0.159346 24.787);
831 --stage-art-line: oklch(0.970313 0.02052 67.583);
832 --stage-night-top: oklch(0.470236 0.102322 39.355);
833 --stage-night-mid: oklch(0.345367 0.080697 33.217);
834 --stage-night-bottom: oklch(0.227386 0.056077 27.437);
835 --stage-night-highlight: oklch(0.903891 0.059281 56.181);
836 --stage-night-secondary: oklch(0.641705 0.126508 44.376);
837 --stage-night-tertiary: oklch(0.538694 0.129931 25.865);
838 --stage-night-line: oklch(0.926348 0.046029 58.73);
839
840 @variant dark {
841 --stage-art-top: oklch(0.597533 0.120694 43.455);
842 --stage-art-mid: oklch(0.437763 0.101287 34.86);
843 --stage-art-bottom: oklch(0.264269 0.055858 26.548);
844 --stage-art-highlight: oklch(0.929214 0.042638 55.801);
845 --stage-art-secondary: oklch(0.705592 0.137369 43.176);
846 --stage-art-tertiary: oklch(0.629583 0.158322 24.088);
847 --stage-art-line: oklch(0.945058 0.033906 58.824);
848 --stage-night-top: oklch(0.392352 0.081287 36.444);
849 --stage-night-mid: oklch(0.271305 0.056352 31.135);
850 --stage-night-bottom: oklch(0.182126 0.028154 27.774);
851 --stage-night-highlight: oklch(0.851007 0.061294 53.805);
852 --stage-night-secondary: oklch(0.560789 0.10645 42.953);
853 --stage-night-tertiary: oklch(0.476228 0.106656 24.165);
854 --stage-night-line: oklch(0.884931 0.046607 56.556);
855 }
856 }
857
858 html[data-theme-id="iris"] {
859 --stage-art-top: oklch(0.738305 0.113454 298.926);
860 --stage-art-mid: oklch(0.551826 0.159189 294.531);
861 --stage-art-bottom: oklch(0.37147 0.133317 289.999);
862 --stage-art-highlight: oklch(0.949472 0.029204 303.081);
863 --stage-art-secondary: oklch(0.745085 0.125892 298.647);
864 --stage-art-tertiary: oklch(0.73066 0.167815 340.964);
865 --stage-art-line: oklch(0.960278 0.024064 306.969);
866
867 @variant dark {
868 --stage-art-top: oklch(0.57297 0.145973 295.185);
869 --stage-art-mid: oklch(0.419499 0.13752 292.131);
870 --stage-art-bottom: oklch(0.274235 0.095798 286.608);
871 --stage-art-highlight: oklch(0.916698 0.047206 300.224);
872 --stage-art-secondary: oklch(0.670994 0.13095 296.689);
873 --stage-art-tertiary: oklch(0.679357 0.165376 340.439);
874 --stage-art-line: oklch(0.940582 0.032921 299.076);
875 }
876 }
877
878 :is(html[data-theme-id="t3-chat"], html[data-theme-id="ocean"], html[data-theme-id="iris"]) {
879 --stage-night-top: color-mix(in oklch, var(--stage-art-top) 38%, var(--stage-night-base-top));
880 --stage-night-mid: color-mix(in oklch, var(--stage-art-mid) 38%, var(--stage-night-base-mid));
881 --stage-night-bottom: color-mix(
882 in oklch,
883 var(--stage-art-bottom) 38%,
884 var(--stage-night-base-bottom)
885 );
886 --stage-night-highlight: var(--stage-art-highlight);
887 --stage-night-secondary: color-mix(
888 in oklch,
889 var(--stage-art-secondary) 55%,
890 var(--stage-night-mid)
891 );
892 --stage-night-tertiary: color-mix(
893 in oklch,
894 var(--stage-art-tertiary) 60%,
895 var(--stage-night-top)
896 );
897 --stage-night-line: color-mix(in oklch, var(--stage-art-line) 82%, oklch(1 0 0));
898 }
899
900 :is(
901 html[data-theme-id="t3-chat"],
902 html[data-theme-id="grove"],
903 html[data-theme-id="ocean"],
904 html[data-theme-id="ember"],
905 html[data-theme-id="iris"]
906 ) {
907 --stage-art-celeste-highlight: var(--stage-art-highlight);
908 --stage-art-celeste-secondary: var(--stage-art-secondary);
909 --stage-art-violet-highlight: var(--stage-art-highlight);
910 --stage-art-grid-line: var(--stage-art-line);
911 --stage-night-glow-highlight: var(--stage-night-highlight);
912 --stage-night-glow-secondary: var(--stage-night-secondary);
913 --stage-night-sparkle: var(--stage-night-line);
914 }
915
916 @keyframes settings-search-target-pulse {
917 0%,
918 100% {
919 box-shadow: inset 0 0 0 0 transparent;
920 }
921 50% {
922 box-shadow: inset 0 0 0 2px color-mix(in oklab, var(--color-primary) 70%, transparent);
923 }
924 }
925
926 [data-settings-page-scroll] div.settings-search-target-pulse,
927 [data-settings-page-scroll] section.settings-search-target-pulse > div:first-child {
928 animation: settings-search-target-pulse 650ms ease-in-out 2;
929 border-radius: 0.75rem;
930 }
931
932 /* The pulse is the destination indicator; without it (reduced motion), the
933 focus outline takes over, so exactly one indicator shows at a time. */
934 [data-settings-page-scroll] .settings-search-target-pulse:focus {
935 outline: none;
936 }
937
938 .surface-subheader {
939 @apply flex h-10 min-h-10 shrink-0 items-center border-b border-border/60 bg-background;
940 }
941
942 [data-preview-panel-mode="inline"] [data-right-panel-surface-content] [data-surface-subheader] {
943 height: calc(var(--spacing) * 7);
944 min-height: calc(var(--spacing) * 7);
945 margin-bottom: calc(var(--spacing) * 3);
946 border-bottom-color: transparent;
947 }
948
949 .chat-content-lane {
950 margin-inline: auto;
951 width: 100%;
952 min-width: 0;
953 max-width: var(--chat-content-max-width);
954 }
955
956 .messages-timeline-row-frame {
957 padding-inline: 0;
958 }
959
960 .settings-slider {
961 --settings-slider-progress: 0%;
962 --settings-slider-fill-offset: 0.5rem;
963 --settings-slider-fill-position: calc(
964 var(--settings-slider-progress) + var(--settings-slider-fill-offset)
965 );
966 appearance: none;
967 height: 1.5rem;
968 background: transparent;
969 cursor: pointer;
970 }
971
972 .settings-slider::-webkit-slider-runnable-track {
973 height: 0.375rem;
974 border-radius: 9999px;
975 background: linear-gradient(
976 to right,
977 var(--primary) 0 var(--settings-slider-fill-position),
978 color-mix(in srgb, var(--contrast-muted-foreground) 22%, transparent)
979 var(--settings-slider-fill-position) 100%
980 );
981 box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--contrast-border) 55%, transparent);
982 }
983
984 .settings-slider::-moz-range-track {
985 height: 0.375rem;
986 border-radius: 9999px;
987 background: color-mix(in srgb, var(--contrast-muted-foreground) 22%, transparent);
988 box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--contrast-border) 55%, transparent);
989 }
990
991 .settings-slider::-moz-range-progress {
992 height: 0.375rem;
993 border-radius: 9999px;
994 background: var(--primary);
995 }
996
997 .settings-slider::-webkit-slider-thumb {
998 appearance: none;
999 width: 1rem;
1000 height: 1rem;
1001 margin-top: -0.3125rem;
1002 border: 2px solid var(--primary);
1003 border-radius: 9999px;
1004 background: var(--background);
1005 box-shadow: 0 1px 2px color-mix(in srgb, var(--contrast-foreground) 12%, transparent);
1006 transition:
1007 transform 120ms ease,
1008 box-shadow 120ms ease;
1009 }
1010
1011 .settings-slider::-moz-range-thumb {
1012 width: 1rem;
1013 height: 1rem;
1014 border: 2px solid var(--primary);
1015 border-radius: 9999px;
1016 background: var(--background);
1017 box-shadow: 0 1px 2px color-mix(in srgb, var(--contrast-foreground) 12%, transparent);
1018 transition:
1019 transform 120ms ease,
1020 box-shadow 120ms ease;
1021 }
1022
1023 .settings-slider:hover::-webkit-slider-thumb {
1024 transform: scale(1.08);
1025 box-shadow: 0 1px 3px color-mix(in srgb, var(--contrast-foreground) 20%, transparent);
1026 }
1027
1028 .settings-slider:hover::-moz-range-thumb {
1029 transform: scale(1.08);
1030 box-shadow: 0 1px 3px color-mix(in srgb, var(--contrast-foreground) 20%, transparent);
1031 }
1032
1033 .settings-slider:active::-webkit-slider-thumb {
1034 transform: scale(0.94);
1035 }
1036
1037 .settings-slider:active::-moz-range-thumb {
1038 transform: scale(0.94);
1039 }
1040
1041 .settings-slider:focus-visible {
1042 outline: none;
1043 }
1044
1045 .settings-slider:focus-visible::-webkit-slider-thumb {
1046 box-shadow:
1047 inset 0 0 0 1px var(--background),
1048 inset 0 0 0 3px var(--ring);
1049 }
1050
1051 .settings-slider:focus-visible::-moz-range-thumb {
1052 box-shadow:
1053 inset 0 0 0 1px var(--background),
1054 inset 0 0 0 3px var(--ring);
1055 }
1056
1057 @media (forced-colors: active) {
1058 .settings-slider {
1059 appearance: auto;
1060 accent-color: Highlight;
1061 }
1062
1063 .settings-slider::-webkit-slider-runnable-track,
1064 .settings-slider::-webkit-slider-thumb {
1065 all: revert;
1066 }
1067
1068 .settings-slider::-moz-range-track,
1069 .settings-slider::-moz-range-progress,
1070 .settings-slider::-moz-range-thumb {
1071 all: revert;
1072 }
1073 }
1074}
1075
1076/* Safe-area inset utilities for surfaces that opt into edge-to-edge rendering.
1077 Insets resolve to 0 when the browser already constrains the layout viewport
1078 to the safe area. */
1079@utility pt-safe {
1080 padding-top: max(env(safe-area-inset-top), 0px);
1081}
1082@utility pb-safe {
1083 padding-bottom: max(env(safe-area-inset-bottom), 0px);
1084}
1085@utility pl-safe {
1086 padding-left: max(env(safe-area-inset-left), 0px);
1087}
1088@utility pr-safe {
1089 padding-right: max(env(safe-area-inset-right), 0px);
1090}
1091
1092/* Grain texture for chrome surfaces that float above the body (see the
1093 --surface-grain note). Layered over the element's background-color, so it
1094 composes with bg-* utilities. */
1095@utility surface-grain {
1096 background-image: var(--surface-grain);
1097 background-repeat: repeat;
1098 background-size: var(--surface-grain-size);
1099}
1100
1101/* Suppress all transitions during theme changes */
1102.no-transitions,
1103.no-transitions *,
1104.no-transitions *::before,
1105.no-transitions *::after {
1106 transition-duration: 0s !important;
1107 animation-duration: 0s !important;
1108}
1109
1110:root {
1111 color-scheme: light;
1112 --radius: 0.625rem;
1113 --background: var(--color-zinc-25);
1114 --app-chrome-background: var(--background);
1115 --toolbar-background: var(--app-chrome-background);
1116 --toolbar-foreground: var(--foreground);
1117 --toolbar-border: var(--border);
1118 --toolbar-control: var(--popover);
1119 --toolbar-control-foreground: var(--foreground);
1120 --toolbar-control-hover: var(--accent);
1121 --surface-raised: color-mix(in srgb, var(--card) 20%, transparent);
1122 --foreground: var(--color-zinc-800);
1123 --card: var(--color-white);
1124 --card-foreground: var(--color-zinc-800);
1125 --popover: var(--color-white);
1126 --popover-foreground: var(--color-zinc-800);
1127 --primary: oklch(0.488 0.217 264);
1128 --primary-foreground: var(--color-white);
1129 --secondary: var(--color-zinc-50);
1130 --secondary-foreground: var(--color-zinc-800);
1131 --muted: var(--color-zinc-50);
1132 --muted-foreground: var(--color-zinc-500);
1133 --placeholder: var(--muted-foreground);
1134 --secondary-label: var(--muted-foreground);
1135 --icon-muted: var(--muted-foreground);
1136 --message-surface: var(--accent);
1137 --message-foreground: var(--foreground);
1138 --message-action: var(--primary);
1139 --message-action-foreground: var(--primary-foreground);
1140 --message-action-hover: color-mix(in srgb, var(--primary) 90%, var(--background));
1141 --accent: var(--color-zinc-100);
1142 --accent-foreground: var(--color-zinc-900);
1143 --error: var(--color-red-500);
1144 --error-foreground: var(--color-red-700);
1145 --tool-error-icon: var(--error);
1146 --error-surface: color-mix(in srgb, var(--error) 8%, transparent);
1147 --destructive: var(--error);
1148 --border: var(--color-zinc-200);
1149 --input: var(--color-zinc-300);
1150 --ring: var(--primary);
1151 --activity-foreground: light-dark(
1152 oklch(from var(--ring) min(l, 0.55) c h),
1153 oklch(from var(--ring) max(l, 0.65) c h)
1154 );
1155 --destructive-foreground: var(--error-foreground);
1156 --info: var(--color-blue-500);
1157 --info-foreground: var(--color-blue-700);
1158 --success: var(--color-emerald-500);
1159 --success-foreground: var(--color-emerald-700);
1160 --warning: var(--color-amber-500);
1161 --warning-foreground: var(--color-amber-700);
1162 --warning-surface: color-mix(in srgb, var(--warning) 8%, transparent);
1163 --update: var(--primary);
1164 --update-foreground: var(--primary);
1165 --update-surface: color-mix(in srgb, var(--update) 12%, transparent);
1166 /* Keep every sidebar primitive on the same light surface hierarchy, including
1167 portaled mobile sheets and settings navigation outside the app sidebar. */
1168 --sidebar: var(--color-zinc-50);
1169 --sidebar-foreground: var(--foreground);
1170 --sidebar-muted-foreground: var(--muted-foreground);
1171 --sidebar-control-surface: var(--color-zinc-100);
1172 --sidebar-row-hover: var(--color-zinc-25);
1173 --sidebar-row-active: var(--color-white);
1174 --sidebar-row-selected: var(--color-white);
1175 --sidebar-border: var(--border);
1176 --code-background: color-mix(in srgb, var(--card) 90%, var(--background));
1177 --code-foreground: var(--foreground);
1178 --terminal-background: var(--background);
1179 --terminal-foreground: var(--foreground);
1180 --terminal-cursor: rgb(38 56 78);
1181 --terminal-selection-background: rgb(37 63 99 / 20%);
1182
1183 @variant dark {
1184 color-scheme: dark;
1185 /* Keep controls and floating surfaces close to the neutral-black canvas.
1186 Borders and hover states provide separation without milky gray fills. */
1187 --background: var(--color-neutral-950);
1188 --surface-raised: color-mix(in srgb, var(--background) 97%, var(--color-white));
1189 --foreground: var(--color-neutral-100);
1190 --card: color-mix(in srgb, var(--background) 97%, var(--color-white));
1191 --card-foreground: var(--color-neutral-100);
1192 --popover: color-mix(in srgb, var(--background) 97%, var(--color-white));
1193 --popover-foreground: var(--color-neutral-100);
1194 --primary: oklch(0.571 0.21 264);
1195 --secondary: --alpha(var(--color-white) / 3%);
1196 --secondary-foreground: var(--color-neutral-100);
1197 --muted: --alpha(var(--color-white) / 3%);
1198 --muted-foreground: color-mix(in srgb, var(--color-neutral-500) 90%, var(--color-white));
1199 --accent: --alpha(var(--color-white) / 4%);
1200 --accent-foreground: var(--color-neutral-100);
1201 --error: color-mix(in srgb, var(--color-red-500) 90%, var(--color-white));
1202 --error-foreground: var(--color-red-400);
1203 --tool-error-icon: #fca5a5;
1204 --error-surface: color-mix(in srgb, var(--error) 16%, transparent);
1205 --border: --alpha(var(--color-white) / 6%);
1206 --input: --alpha(var(--color-white) / 8%);
1207 --info-foreground: var(--color-blue-400);
1208 --success-foreground: var(--color-emerald-400);
1209 --warning-foreground: var(--color-amber-400);
1210 --warning-surface: color-mix(in srgb, var(--warning) 16%, transparent);
1211 --update-foreground: var(--color-blue-400);
1212 --update-surface: color-mix(in srgb, var(--update) 18%, transparent);
1213 --sidebar: var(--card);
1214 --sidebar-control-surface: var(--muted);
1215 --sidebar-row-hover: var(--accent);
1216 --sidebar-row-active: var(--accent);
1217 --sidebar-row-selected: var(--muted);
1218 --terminal-cursor: rgb(180 203 255);
1219 --terminal-selection-background: rgb(180 203 255 / 25%);
1220 }
1221}
1222
1223/* Keep both sidebar implementations (default and legacy) on the same quiet
1224 zinc hierarchy: zinc-50 navigation, zinc-25 hover, and white selected/raised
1225 surfaces. */
1226[data-app-sidebar] {
1227 --background: var(--color-zinc-25);
1228 --foreground: var(--color-zinc-800);
1229 --card: var(--color-white);
1230 --card-foreground: var(--color-zinc-800);
1231 --accent: var(--color-zinc-100);
1232 --accent-foreground: var(--color-zinc-900);
1233 --muted: var(--color-zinc-50);
1234 --muted-foreground: var(--color-zinc-500);
1235 --border: var(--color-zinc-200);
1236 --input: var(--color-zinc-300);
1237 --sidebar: var(--color-zinc-50);
1238 --sidebar-foreground: var(--color-zinc-800);
1239 --sidebar-muted-foreground: var(--color-zinc-500);
1240 --sidebar-control-surface: var(--color-zinc-100);
1241 --sidebar-row-hover: var(--color-zinc-25);
1242 --sidebar-row-active: var(--color-white);
1243 --sidebar-row-selected: var(--color-white);
1244 --sidebar-border: var(--color-zinc-200);
1245
1246 @variant dark {
1247 --background: #000;
1248 --foreground: #f1f3f7;
1249 --card: #000;
1250 --card-foreground: var(--foreground);
1251 --accent: #191a1d;
1252 --accent-foreground: #f7f9ff;
1253 --muted: #0a0a0a;
1254 --muted-foreground: #a3a3a3;
1255 --border: rgb(255 255 255 / 8%);
1256 --input: rgb(255 255 255 / 18%);
1257 --sidebar: var(--card);
1258 --sidebar-foreground: var(--foreground);
1259 --sidebar-muted-foreground: var(--muted-foreground);
1260 --sidebar-control-surface: var(--muted);
1261 --sidebar-row-hover: color-mix(in srgb, var(--contrast-foreground) 8%, transparent);
1262 --sidebar-row-active: color-mix(in srgb, var(--contrast-foreground) 11%, transparent);
1263 --sidebar-row-selected: color-mix(in srgb, var(--contrast-foreground) 7%, transparent);
1264 --sidebar-border: var(--border);
1265 }
1266}
1267
1268/* Theme files are expressed in app color roles and mapped to the existing
1269 semantic surface tokens here. Keep this block after the sidebar-version
1270 compatibility overrides so both navigation implementations receive the
1271 same palette. Success, info, provider, and channel identity colors remain
1272 independent; error, warning, and update roles are themeable below. */
1273/* The non-empty marker adds enough specificity to outrank the generated root
1274 dark variant without reintroducing raw `.dark` selectors. */
1275html[data-theme-id]:not([data-theme-id=""]) {
1276 --background: var(--app-theme-canvas);
1277 --app-chrome-background: var(--app-theme-chrome);
1278 --toolbar-background: var(--app-theme-toolbar);
1279 --toolbar-foreground: var(--app-theme-toolbar-foreground);
1280 --toolbar-border: var(--app-theme-toolbar-border);
1281 --toolbar-control: var(--app-theme-toolbar-control);
1282 --toolbar-control-foreground: var(--app-theme-toolbar-control-foreground);
1283 --toolbar-control-hover: var(--app-theme-toolbar-control-hover);
1284 --surface-raised: var(--app-theme-surface-raised);
1285 --foreground: var(--app-theme-text);
1286 --card: var(--app-theme-surface);
1287 --card-foreground: var(--app-theme-text);
1288 --popover: var(--app-theme-surface-overlay);
1289 --popover-foreground: var(--app-theme-text);
1290 /* Solid controls (CTAs, switches, sliders, the send button) all share the
1291 theme's action color so every "act now" affordance reads consistently;
1292 the accent keeps driving surfaces, selection, and focus. Palettes without
1293 a distinct action color make these identical. */
1294 --primary: var(--app-theme-message-action);
1295 --primary-foreground: var(--app-theme-message-action-foreground);
1296 --secondary: var(--app-theme-secondary);
1297 --secondary-foreground: var(--app-theme-secondary-foreground);
1298 --muted: var(--app-theme-muted);
1299 --muted-foreground: var(--app-theme-muted-foreground);
1300 --placeholder: var(--app-theme-placeholder);
1301 --secondary-label: var(--app-theme-secondary-label);
1302 --icon-muted: var(--app-theme-icon-muted);
1303 --accent: var(--app-theme-accent-surface);
1304 --accent-foreground: var(--app-theme-accent-surface-foreground);
1305 --message-surface: var(--app-theme-message-surface);
1306 --message-foreground: var(--app-theme-message-foreground);
1307 --message-action: var(--app-theme-message-action);
1308 --message-action-foreground: var(--app-theme-message-action-foreground);
1309 --message-action-hover: var(--app-theme-message-action-hover);
1310 --error: var(--app-theme-error);
1311 --error-foreground: var(--app-theme-error-foreground);
1312 --tool-error-icon: var(--error-foreground);
1313 --error-surface: var(--app-theme-error-surface);
1314 --destructive: var(--error);
1315 --destructive-foreground: var(--error-foreground);
1316 --warning: var(--app-theme-warning);
1317 --warning-foreground: var(--app-theme-warning-foreground);
1318 --warning-surface: var(--app-theme-warning-surface);
1319 --update: var(--app-theme-update);
1320 --update-foreground: var(--app-theme-update-foreground);
1321 --update-surface: var(--app-theme-update-surface);
1322 --border: var(--app-theme-border);
1323 --input: var(--app-theme-input);
1324 --ring: var(--app-theme-focus);
1325 --sidebar: var(--app-theme-sidebar);
1326 --sidebar-foreground: var(--app-theme-sidebar-foreground);
1327 --sidebar-muted-foreground: var(--app-theme-sidebar-muted-foreground);
1328 --sidebar-control-surface: var(--app-theme-sidebar-control-surface);
1329 --sidebar-row-hover: var(--app-theme-sidebar-row-hover);
1330 --sidebar-row-active: var(--app-theme-sidebar-row-active);
1331 --sidebar-row-selected: var(--app-theme-sidebar-row-selected);
1332 --sidebar-border: var(--app-theme-sidebar-border);
1333 --code-background: var(--app-theme-code-background);
1334 --code-foreground: var(--app-theme-code-foreground);
1335 --terminal-background: var(--app-theme-terminal-background);
1336 --terminal-foreground: var(--app-theme-terminal-foreground);
1337 --terminal-cursor: var(--app-theme-terminal-cursor);
1338 --terminal-selection-background: var(--app-theme-terminal-selection-background);
1339}
1340
1341/* Theme-token dependency probes are restored synchronously, before paint. Keep
1342 transitions from observing the temporary sentinel color in between. */
1343html[data-theme-token-probe],
1344html[data-theme-token-probe] *,
1345html[data-theme-token-probe] *::before,
1346html[data-theme-token-probe] *::after {
1347 transition: none !important;
1348}
1349
1350#theme-inspector-spotlight {
1351 position: fixed;
1352 z-index: 100;
1353 inset: 0;
1354 width: 100vw;
1355 height: 100dvh;
1356 overflow: visible;
1357 pointer-events: none;
1358}
1359
1360[data-slot="popover-positioner"]:has([data-theme-editor-panel]),
1361[data-slot="tooltip-positioner"]:has([data-theme-editor-panel]) {
1362 z-index: 120;
1363}
1364
1365.theme-inspector-spotlight-dimmer {
1366 fill: rgb(2 3 8 / 54%);
1367}
1368
1369.theme-inspector-spotlight-glow {
1370 fill: color-mix(in oklab, var(--ring) 9%, transparent);
1371 stroke: color-mix(in oklab, var(--ring) 72%, white);
1372 stroke-width: 2px;
1373 opacity: 0.92;
1374 vector-effect: non-scaling-stroke;
1375}
1376
1377#theme-inspector-hover {
1378 position: fixed;
1379 z-index: 101;
1380 pointer-events: none;
1381 background: color-mix(in oklab, var(--ring) 7%, transparent);
1382 border: 1px solid color-mix(in oklab, var(--ring) 70%, white);
1383 box-shadow:
1384 0 0 0 3px color-mix(in oklab, var(--ring) 16%, transparent),
1385 0 0 18px color-mix(in oklab, var(--ring) 38%, transparent);
1386}
1387
1388#theme-inspector-hover [data-theme-inspector-hover-label] {
1389 position: absolute;
1390 left: 0;
1391 max-width: 12rem;
1392 overflow: hidden;
1393 padding: 0.2rem 0.45rem;
1394 border: 1px solid color-mix(in oklab, var(--ring) 48%, var(--contrast-border));
1395 border-radius: 0.4rem;
1396 background: var(--popover);
1397 box-shadow: 0 4px 16px rgb(0 0 0 / 24%);
1398 color: var(--contrast-popover-foreground);
1399 font-size: 0.6875rem;
1400 font-weight: 600;
1401 line-height: 1rem;
1402 text-overflow: ellipsis;
1403 white-space: nowrap;
1404}
1405
1406#theme-inspector-hover[data-placement="above"] [data-theme-inspector-hover-label] {
1407 bottom: calc(100% + 0.35rem);
1408}
1409
1410#theme-inspector-hover[data-placement="below"] [data-theme-inspector-hover-label] {
1411 top: calc(100% + 0.35rem);
1412}
1413
1414/* Workspace actions use a small, separate chrome hierarchy. Keeping these
1415 roles explicit prevents the thread toolbar from falling back to the default
1416 dark-mode whites while preserving the same theme file across clients. */
1417html[data-theme-id] [data-chat-header] {
1418 background-color: var(--toolbar-background);
1419 color: var(--contrast-toolbar-foreground);
1420}
1421
1422html[data-theme-id] [data-chat-header] [data-slot="button"],
1423html[data-theme-id] [data-chat-header] [data-slot="menu-trigger"],
1424html[data-theme-id] [data-chat-header] [data-toolbar-control] {
1425 --control-icon-color: var(--contrast-toolbar-control-foreground);
1426 background-color: var(--toolbar-control);
1427 border-color: var(--contrast-toolbar-border);
1428 color: var(--contrast-toolbar-control-foreground);
1429}
1430
1431/* The panel layout toggles stay ghost: they render both inside the header and
1432 in the titlebar strip, so filling them would make them change appearance as
1433 the panel opens. They only take the themed foreground; hover and pressed
1434 keep the base ghost accent. The tooltip trigger's data-slot wins over the
1435 toggle's when the trigger renders the toggle, so match both. */
1436html[data-theme-id] [data-panel-layout-controls] [data-slot="toggle"],
1437html[data-theme-id] [data-panel-layout-controls] [data-slot="tooltip-trigger"],
1438html[data-theme-id] [data-workspace-titlebar-controls] [data-slot="toggle"],
1439html[data-theme-id] [data-workspace-titlebar-controls] [data-slot="tooltip-trigger"] {
1440 --control-icon-color: var(--contrast-toolbar-foreground);
1441 color: var(--contrast-toolbar-foreground);
1442}
1443
1444html[data-theme-id] [data-panel-layout-controls] [data-slot="toggle"]:disabled,
1445html[data-theme-id] [data-panel-layout-controls] [data-slot="tooltip-trigger"]:disabled,
1446html[data-theme-id] [data-workspace-titlebar-controls] [data-slot="toggle"]:disabled,
1447html[data-theme-id] [data-workspace-titlebar-controls] [data-slot="tooltip-trigger"]:disabled {
1448 --control-icon-color: color-mix(
1449 in oklab,
1450 var(--contrast-toolbar-foreground) 55%,
1451 var(--toolbar-background)
1452 );
1453 color: color-mix(in oklab, var(--contrast-toolbar-foreground) 55%, var(--toolbar-background));
1454}
1455
1456html[data-theme-id] [data-chat-header] [data-slot="button"]:hover,
1457html[data-theme-id] [data-chat-header] [data-slot="button"][data-pressed],
1458html[data-theme-id] [data-chat-header] [data-slot="menu-trigger"]:hover,
1459html[data-theme-id] [data-chat-header] [data-slot="menu-trigger"][data-pressed],
1460html[data-theme-id] [data-chat-header] [data-toolbar-control]:hover,
1461html[data-theme-id] [data-chat-header] [data-toolbar-control][data-pressed] {
1462 background-color: var(--toolbar-control-hover);
1463}
1464
1465html[data-theme-id] [data-chat-header] [data-slot="separator"] {
1466 background-color: var(--contrast-toolbar-border);
1467}
1468
1469/* Chat code blocks join the other code surfaces on the code tokens. The
1470 default look keeps its secondary/input tints; themed palettes tint those
1471 surfaces with the accent, which fights syntax highlighting. */
1472html[data-theme-id] .chat-markdown .chat-markdown-codeblock {
1473 background-color: var(--code-background);
1474 border-color: var(--contrast-border);
1475 color: var(--code-foreground);
1476}
1477
1478html[data-theme-id] .chat-markdown pre {
1479 background-color: var(--code-background);
1480 color: var(--code-foreground);
1481}
1482
1483html[data-theme-id] .chat-markdown .chat-markdown-codeblock pre {
1484 background-color: transparent;
1485}
1486
1487html[data-theme-id] .chat-markdown .chat-markdown-codeblock-header {
1488 color: color-mix(in srgb, var(--code-foreground) 72%, transparent);
1489}
1490
1491/* T3 Chat renders inline code and compact chat artifacts with its translucent
1492 secondary surface flattened over the light chat canvas. The raw muted and
1493 secondary tokens are substantially darker than those visible pixels. */
1494html[data-theme-id="t3-chat"] {
1495 @variant light {
1496 & .chat-markdown :not(pre) > code,
1497 & [data-changed-files-state],
1498 & [data-changed-files-header] {
1499 background-color: var(--message-surface);
1500 }
1501
1502 & .chat-markdown :not(pre) > code,
1503 & [data-changed-files-state] {
1504 border-color: transparent;
1505 }
1506
1507 & .chat-markdown :not(pre) > code {
1508 color: inherit;
1509 }
1510 }
1511}
1512
1513html[data-theme-id] [data-app-sidebar] {
1514 --background: var(--app-theme-canvas);
1515 --foreground: var(--app-theme-text);
1516 --card: var(--app-theme-surface);
1517 --card-foreground: var(--app-theme-text);
1518 --accent: var(--app-theme-accent-surface);
1519 --accent-foreground: var(--app-theme-accent-surface-foreground);
1520 --muted: var(--app-theme-muted);
1521 --muted-foreground: var(--app-theme-muted-foreground);
1522 --border: var(--app-theme-border);
1523 --input: var(--app-theme-input);
1524 --sidebar: var(--app-theme-sidebar);
1525 --sidebar-foreground: var(--app-theme-sidebar-foreground);
1526 --sidebar-muted-foreground: var(--app-theme-sidebar-muted-foreground);
1527 --sidebar-control-surface: var(--app-theme-sidebar-control-surface);
1528 --sidebar-row-hover: var(--app-theme-sidebar-row-hover);
1529 --sidebar-row-active: var(--app-theme-sidebar-row-active);
1530 --sidebar-row-selected: var(--app-theme-sidebar-row-selected);
1531 --sidebar-border: var(--app-theme-sidebar-border);
1532 border-color: color-mix(in srgb, var(--contrast-sidebar-foreground) 10%, transparent);
1533
1534 @variant dark {
1535 border-color: color-mix(in srgb, var(--contrast-sidebar-foreground) 8%, transparent);
1536 }
1537}
1538
1539/* T3 Chat's panel divider is deliberately pink, and its resize affordance
1540 keeps that color while hovered. Do not neutralize this branded edge. */
1541html[data-theme-id="t3-chat"] [data-app-sidebar] {
1542 @variant dark {
1543 border-color: var(--contrast-sidebar-border);
1544 }
1545}
1546
1547/* Recompute contrast wherever a subtree owns its own semantic color palette. */
1548:root,
1549[data-app-sidebar] {
1550 --contrast-toolbar-foreground: color-mix(
1551 in oklab,
1552 color-mix(
1553 in oklab,
1554 var(--toolbar-foreground) var(--appearance-contrast-base),
1555 var(--toolbar-background)
1556 ),
1557 var(--appearance-contrast-target) var(--appearance-contrast-boost)
1558 );
1559 --contrast-toolbar-border: color-mix(
1560 in srgb,
1561 color-mix(in srgb, var(--toolbar-border) var(--appearance-contrast-base), transparent),
1562 var(--toolbar-foreground) var(--appearance-contrast-border-boost)
1563 );
1564 --contrast-toolbar-control-foreground: color-mix(
1565 in oklab,
1566 color-mix(
1567 in oklab,
1568 var(--toolbar-control-foreground) var(--appearance-contrast-base),
1569 var(--toolbar-control)
1570 ),
1571 var(--appearance-contrast-target) var(--appearance-contrast-boost)
1572 );
1573 --contrast-input: color-mix(
1574 in srgb,
1575 color-mix(in srgb, var(--input) var(--appearance-contrast-base), transparent),
1576 var(--foreground) var(--appearance-contrast-border-boost)
1577 );
1578 --contrast-border: color-mix(
1579 in srgb,
1580 color-mix(in srgb, var(--border) var(--appearance-contrast-base), transparent),
1581 var(--foreground) var(--appearance-contrast-border-boost)
1582 );
1583 --contrast-foreground: color-mix(
1584 in oklab,
1585 color-mix(in oklab, var(--foreground) var(--appearance-contrast-base), var(--background)),
1586 var(--appearance-contrast-target) var(--appearance-contrast-boost)
1587 );
1588 --contrast-muted-foreground: color-mix(
1589 in oklab,
1590 color-mix(in oklab, var(--muted-foreground) var(--appearance-contrast-base), var(--background)),
1591 var(--appearance-contrast-target) var(--appearance-contrast-boost)
1592 );
1593 --contrast-placeholder: color-mix(
1594 in oklab,
1595 color-mix(in oklab, var(--placeholder) var(--appearance-contrast-base), var(--background)),
1596 var(--appearance-contrast-target) var(--appearance-contrast-boost)
1597 );
1598 --contrast-secondary-label: color-mix(
1599 in oklab,
1600 color-mix(in oklab, var(--secondary-label) var(--appearance-contrast-base), var(--background)),
1601 var(--appearance-contrast-target) var(--appearance-contrast-boost)
1602 );
1603 --contrast-icon-muted: color-mix(
1604 in oklab,
1605 color-mix(in oklab, var(--icon-muted) var(--appearance-contrast-base), var(--background)),
1606 var(--appearance-contrast-target) var(--appearance-contrast-boost)
1607 );
1608 --contrast-card-foreground: color-mix(
1609 in oklab,
1610 color-mix(in oklab, var(--card-foreground) var(--appearance-contrast-base), var(--card)),
1611 var(--appearance-contrast-target) var(--appearance-contrast-boost)
1612 );
1613 --contrast-popover-foreground: color-mix(
1614 in oklab,
1615 color-mix(in oklab, var(--popover-foreground) var(--appearance-contrast-base), var(--popover)),
1616 var(--appearance-contrast-target) var(--appearance-contrast-boost)
1617 );
1618 --contrast-accent-foreground: color-mix(
1619 in oklab,
1620 color-mix(in oklab, var(--accent-foreground) var(--appearance-contrast-base), var(--accent)),
1621 var(--appearance-contrast-target) var(--appearance-contrast-boost)
1622 );
1623 --contrast-secondary-foreground: color-mix(
1624 in oklab,
1625 color-mix(
1626 in oklab,
1627 var(--secondary-foreground) var(--appearance-contrast-base),
1628 var(--secondary)
1629 ),
1630 var(--appearance-contrast-target) var(--appearance-contrast-boost)
1631 );
1632 --contrast-message-foreground: color-mix(
1633 in oklab,
1634 color-mix(
1635 in oklab,
1636 var(--message-foreground) var(--appearance-contrast-base),
1637 var(--message-surface)
1638 ),
1639 var(--appearance-contrast-target) var(--appearance-contrast-boost)
1640 );
1641 --contrast-sidebar-foreground: color-mix(
1642 in oklab,
1643 color-mix(in oklab, var(--sidebar-foreground) var(--appearance-contrast-base), var(--sidebar)),
1644 var(--appearance-contrast-target) var(--appearance-contrast-boost)
1645 );
1646 --contrast-sidebar-muted-foreground: color-mix(
1647 in oklab,
1648 color-mix(
1649 in oklab,
1650 var(--sidebar-muted-foreground) var(--appearance-contrast-base),
1651 var(--sidebar)
1652 ),
1653 var(--appearance-contrast-target) var(--appearance-contrast-boost)
1654 );
1655 --contrast-sidebar-border: color-mix(
1656 in srgb,
1657 color-mix(in srgb, var(--sidebar-border) var(--appearance-contrast-base), transparent),
1658 var(--sidebar-foreground) var(--appearance-contrast-border-boost)
1659 );
1660}
1661
1662body {
1663 /* Reference the theme token (not a literal stack) so the Settings ->
1664 Appearance runtime override of --font-sans reaches all interface text. */
1665 font-family: var(--font-sans);
1666 margin: 0;
1667 padding: 0;
1668}
1669
1670html,
1671body {
1672 min-height: calc(100svh + env(safe-area-inset-top));
1673 overscroll-behavior: none;
1674}
1675
1676body {
1677 height: 100%;
1678 overflow: hidden;
1679}
1680
1681#root {
1682 height: 100%;
1683 width: 100%;
1684 max-width: 100%;
1685 overflow-x: hidden;
1686 overflow-x: clip;
1687 overflow-y: hidden;
1688 overscroll-behavior-y: none;
1689 padding-top: max(env(safe-area-inset-top), 0px);
1690 padding-right: var(--desktop-window-right-resize-inset);
1691}
1692
1693.electron-windows {
1694 --desktop-window-right-resize-inset: 6px;
1695}
1696
1697/* App-chrome grain. Baked into each surface's own background (behind
1698 content) rather than a fixed overlay on top: a full-viewport overlay
1699 forces the compositor to re-blend every frame any animation produces,
1700 which multiplied idle GPU cost. The overlay's 0.035 opacity lives in the
1701 SVG rect instead. Surfaces that float over the body (e.g. the inset main
1702 card) must opt in via the surface-grain utility, or they lose the
1703 grain's subtle brightening and stand out against the body. */
1704:root {
1705 --surface-grain: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.035'/%3E%3C/svg%3E");
1706 --surface-grain-size: 256px 256px;
1707}
1708
1709/* The live T3 Chat texture is a 128px grayscale tile with a constant 7/255
1710 alpha. Keep our procedural texture cheap while matching that scale and
1711 strength for the branded preset. */
1712html[data-theme-id="t3-chat"] {
1713 --surface-grain: url("data:image/svg+xml,%3Csvg viewBox='0 0 128 128' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.02745'/%3E%3C/svg%3E");
1714 --surface-grain-size: 128px 128px;
1715}
1716
1717body {
1718 background-image: var(--surface-grain);
1719 background-repeat: repeat;
1720 background-size: var(--surface-grain-size);
1721}
1722
1723pre,
1724code {
1725 font-family: var(--font-mono);
1726}
1727
1728.chat-markdown pre,
1729.chat-markdown code {
1730 font-family: var(--chat-code-font, "AT Name Mono"), "T3 Markdown Symbols", var(--font-mono);
1731 font-variation-settings:
1732 "CASL" 0.15,
1733 "MONO" 1;
1734}
1735
1736/* Code blocks in chat carry the size preference. Scoped to chat markdown
1737 rather than every pre: a global rule would beat text-size utilities and
1738 inherited sizes on unrelated pre surfaces (terminal previews, approvals).
1739 Inline code stays relative to its sentence so it never towers over prose;
1740 diffs and file previews take the size through --diffs-font-size. */
1741.chat-markdown .chat-markdown-shiki .shiki,
1742.chat-markdown pre,
1743.chat-markdown pre code {
1744 font-size: var(--font-size-code, inherit);
1745}
1746
1747/* @pierre/diffs surfaces (diffs, file previews, annotatable code, search
1748 lines) render inside shadow roots but consult these hooks with their own
1749 literal stacks as fallback. Custom properties inherit across the shadow
1750 boundary, so defining them once here routes every code surface through the
1751 appearance font tokens. */
1752:root {
1753 --diffs-font-family: var(--font-mono);
1754 --diffs-header-font-family: var(--font-sans);
1755}
1756
1757/* Window drag region (frameless titlebar) */
1758.drag-region {
1759 -webkit-app-region: drag;
1760}
1761
1762/* Electron 44 inherits app-region. Reset children to the initial value so they
1763 do not add drag rectangles over fixed controls. Explicit `none` is treated
1764 as `no-drag` and breaks titlebar dragging; `initial` emits no rectangle.
1765 The base layer lets explicit drag/no-drag utilities take precedence. */
1766@layer base {
1767 .drag-region > * {
1768 -webkit-app-region: initial;
1769 }
1770}
1771
1772.drag-region button,
1773.drag-region input,
1774.drag-region textarea,
1775.drag-region select,
1776.drag-region a {
1777 -webkit-app-region: no-drag;
1778}
1779
1780/* Scrollbar styling */
1781::-webkit-scrollbar {
1782 width: var(--app-scrollbar-width);
1783}
1784
1785::-webkit-scrollbar-track {
1786 background: transparent;
1787}
1788
1789::-webkit-scrollbar-thumb {
1790 background: var(--app-scrollbar-thumb);
1791 border-radius: 3px;
1792}
1793
1794::-webkit-scrollbar-thumb:hover {
1795 background: var(--app-scrollbar-thumb-hover);
1796}
1797
1798/* Chat markdown rendering */
1799
1800.chat-markdown > :first-child {
1801 margin-top: 0;
1802}
1803
1804.chat-markdown > :last-child {
1805 margin-bottom: 0;
1806}
1807
1808.chat-transcript-blocks,
1809.chat-transcript-figure {
1810 container-type: inline-size;
1811}
1812
1813.chat-transcript-blocks
1814 .chat-markdown
1815 > :is(
1816 .chat-markdown-codeblock,
1817 .chat-markdown-table-container,
1818 .chat-markdown-video-row,
1819 [data-language],
1820 figure
1821 ),
1822.chat-transcript-figure > * {
1823 position: relative;
1824 left: 50%;
1825 width: max-content;
1826 min-width: 100cqw;
1827 max-width: calc(
1828 100cqw + 2 * min(var(--chat-block-outset-start, 0px), var(--chat-block-outset-end, 0px))
1829 );
1830 translate: -50%;
1831}
1832
1833.chat-markdown .chat-markdown-video-row {
1834 --chat-video-width: 20rem;
1835 --chat-video-gap: 0.75rem;
1836 display: grid;
1837 grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--chat-video-width)), 1fr));
1838 align-items: start;
1839 gap: var(--chat-video-gap);
1840 width: 100%;
1841 margin: 1.25rem 0 0.5rem;
1842}
1843
1844.chat-transcript-blocks .chat-markdown > .chat-markdown-video-row {
1845 width: calc(
1846 var(--chat-video-count) * var(--chat-video-width) + (var(--chat-video-count) - 1) *
1847 var(--chat-video-gap)
1848 );
1849}
1850
1851.chat-transcript-blocks .chat-markdown > .chat-markdown-video-row[data-video-count="1"] {
1852 width: 100%;
1853}
1854
1855.chat-markdown-video-row > div {
1856 min-width: 0;
1857}
1858
1859.chat-markdown .chat-markdown-video-bare[data-video-count="1"] {
1860 margin: 0;
1861}
1862
1863.chat-markdown strong {
1864 color: light-dark(
1865 oklch(from var(--message-action) min(l, 0.55) c h),
1866 oklch(from var(--message-action) max(l, 0.65) c h)
1867 );
1868}
1869
1870.chat-markdown em {
1871 color: var(--info-foreground);
1872}
1873
1874.chat-markdown :is(strong > em, em > strong) {
1875 color: var(--error-foreground);
1876 background: color-mix(in srgb, var(--error) 10%, transparent);
1877 border-radius: 0.2em;
1878 box-decoration-break: clone;
1879 -webkit-box-decoration-break: clone;
1880}
1881
1882.live-activity-running {
1883 --secondary-label: var(--activity-foreground);
1884 --contrast-secondary-label: var(--activity-foreground);
1885 --icon-muted: var(--activity-foreground);
1886}
1887
1888.chat-markdown p,
1889.chat-markdown ul,
1890.chat-markdown ol,
1891.chat-markdown blockquote,
1892.chat-markdown pre,
1893.chat-markdown .chat-markdown-table-container,
1894.chat-markdown h1,
1895.chat-markdown h2,
1896.chat-markdown h3,
1897.chat-markdown h4,
1898.chat-markdown h5,
1899.chat-markdown h6 {
1900 margin: 1.25rem 0 0.5rem;
1901}
1902
1903.chat-markdown h1,
1904.chat-markdown h2,
1905.chat-markdown h3,
1906.chat-markdown h4,
1907.chat-markdown h5,
1908.chat-markdown h6 {
1909 font-weight: 600;
1910 line-height: 1.3;
1911 color: var(--contrast-foreground);
1912}
1913
1914.chat-markdown h1 {
1915 font-size: 1.25rem;
1916}
1917
1918.chat-markdown h2 {
1919 font-size: 1.125rem;
1920}
1921
1922.chat-markdown h3 {
1923 font-size: 1rem;
1924}
1925
1926.chat-markdown h4,
1927.chat-markdown h5,
1928.chat-markdown h6 {
1929 font-size: 0.875rem;
1930}
1931
1932.chat-markdown h6 {
1933 color: var(--contrast-muted-foreground);
1934}
1935
1936.chat-markdown :is(ul, ol) {
1937 /* Nested lists must not inherit a wider ordered marker's gutter. */
1938 --list-gutter: 1.25rem;
1939 padding-inline-start: var(--list-gutter);
1940}
1941
1942.chat-markdown ul {
1943 list-style-type: disc;
1944}
1945
1946.chat-markdown ol {
1947 list-style-type: decimal;
1948}
1949
1950.chat-markdown ol > li::marker {
1951 font-variant-numeric: tabular-nums;
1952}
1953
1954.chat-markdown ul ul {
1955 list-style-type: circle;
1956}
1957
1958.chat-markdown ul ul ul {
1959 list-style-type: square;
1960}
1961
1962.chat-markdown ol ol {
1963 list-style-type: lower-alpha;
1964}
1965
1966.chat-markdown ol ol ol {
1967 list-style-type: lower-roman;
1968}
1969
1970.chat-markdown li + li {
1971 margin-top: 0.25rem;
1972}
1973
1974.chat-markdown li > :is(ul, ol) {
1975 margin-block: 0.25rem 0;
1976}
1977
1978.chat-markdown li > :first-child {
1979 margin-top: 0;
1980}
1981
1982.chat-markdown li > :last-child {
1983 margin-bottom: 0;
1984}
1985
1986/* GitHub-style task lists: the checkbox replaces the list marker. */
1987.chat-markdown li.task-list-item {
1988 list-style-type: none;
1989}
1990
1991.chat-markdown li.task-list-item input[type="checkbox"] {
1992 margin-block: 0 0.15em;
1993 margin-inline: calc(-1 * var(--list-gutter, 1.25rem)) 0.35em;
1994 vertical-align: middle;
1995}
1996
1997.chat-markdown kbd {
1998 border: 1px solid var(--contrast-border);
1999 border-radius: 0.25rem;
2000 background: var(--muted);
2001 padding: 0.1rem 0.3rem;
2002 color: var(--contrast-foreground);
2003 font-size: 0.85em;
2004}
2005
2006.chat-markdown mark {
2007 background: var(--warning-surface);
2008 color: var(--warning-foreground);
2009}
2010
2011.chat-markdown u {
2012 text-decoration: underline;
2013 text-underline-offset: 0.15em;
2014}
2015
2016/* Markdown assumes inline images — GitHub comments lay button images out in a row — and the
2017 Tailwind preflight's block-level img would stack them one per line. */
2018.chat-markdown img {
2019 display: inline-block;
2020}
2021
2022.chat-markdown a {
2023 text-decoration: none;
2024}
2025
2026/* File links are Chips and own their color and focus ring. */
2027.chat-markdown a:not(.chat-markdown-file-link) {
2028 color: var(--info-foreground);
2029}
2030
2031.chat-markdown a:not(.chat-markdown-file-link, [data-markdown-image-link]):hover,
2032.chat-markdown a:not(.chat-markdown-file-link, [data-markdown-image-link]):focus-visible {
2033 background-image: radial-gradient(circle, currentcolor 0.75px, transparent 1px);
2034 background-position: left bottom;
2035 background-repeat: repeat-x;
2036 background-size: 4px 2px;
2037}
2038
2039.chat-markdown blockquote {
2040 border-left: 2px solid var(--contrast-border);
2041 padding-left: 0.8rem;
2042 color: var(--contrast-muted-foreground);
2043}
2044
2045.chat-markdown section[data-footnotes] {
2046 margin-top: 1.25rem;
2047 border-top: 1px solid var(--contrast-border);
2048 padding-top: 0.75rem;
2049 color: var(--contrast-muted-foreground);
2050 font-size: 0.75rem;
2051}
2052
2053.chat-markdown section[data-footnotes] ol {
2054 margin: 0;
2055}
2056
2057.chat-markdown section[data-footnotes] li + li {
2058 margin-top: 0.35rem;
2059}
2060
2061.chat-markdown a[data-footnote-ref],
2062.chat-markdown section[data-footnotes] li[id] {
2063 scroll-margin-block-start: 1rlh;
2064}
2065
2066.chat-markdown a[data-footnote-ref],
2067.chat-markdown a[data-footnote-backref] {
2068 display: inline-flex;
2069 min-width: 1rem;
2070 align-items: center;
2071 justify-content: center;
2072 border-radius: 0.25rem;
2073 text-decoration: none;
2074 font-size: 0.6875rem;
2075 font-weight: 600;
2076}
2077
2078.chat-markdown :not(pre) > code {
2079 border: 1px solid var(--contrast-border);
2080 border-radius: 0.375rem;
2081 box-decoration-break: clone;
2082 -webkit-box-decoration-break: clone;
2083 background: var(--muted);
2084 padding: 0.1rem 0.35rem;
2085 color: inherit;
2086 font-size: 0.75rem;
2087}
2088
2089.chat-markdown .chat-markdown-file-link code {
2090 border: none;
2091 background: transparent;
2092 padding: 0;
2093 color: inherit;
2094 font-size: inherit;
2095 line-height: inherit;
2096}
2097
2098.chat-markdown pre {
2099 max-width: 100%;
2100 overflow-x: auto;
2101 border: 1px solid var(--contrast-border);
2102 border-radius: 0.75rem;
2103 background: var(--muted);
2104 padding: 0.8rem 0.9rem;
2105 /* The fallback's vertical box strokes span 1.25em and meet on adjacent lines. */
2106 line-height: 1.25;
2107 scrollbar-width: thin;
2108 scrollbar-color: color-mix(in srgb, var(--contrast-border) 78%, transparent) transparent;
2109}
2110
2111.chat-markdown pre code {
2112 border: none;
2113 background: transparent;
2114 padding: 0;
2115}
2116
2117.chat-markdown pre::-webkit-scrollbar {
2118 height: 7px;
2119}
2120
2121.chat-markdown pre::-webkit-scrollbar-thumb {
2122 border-radius: 999px;
2123 background: color-mix(in srgb, var(--contrast-border) 78%, transparent);
2124}
2125
2126.chat-markdown .chat-markdown-codeblock-header {
2127 color: color-mix(in srgb, var(--contrast-foreground) 72%, transparent);
2128}
2129
2130.chat-markdown .chat-markdown-codeblock pre {
2131 margin: 0;
2132 border: none;
2133 border-radius: 0;
2134 background: transparent;
2135}
2136
2137.chat-markdown .chat-markdown-codeblock[data-wrap="true"] pre {
2138 white-space: pre-wrap;
2139 overflow-wrap: anywhere;
2140}
2141
2142.chat-markdown .chat-markdown-shiki .shiki {
2143 background: transparent !important;
2144}
2145
2146/* Paragraphs and code blocks arrive in chunks while a response streams. Fade
2147 each new block in so the chunk does not pop. @starting-style only applies
2148 when an element is first inserted, and the rule is gated on data-streaming,
2149 so opening a finished thread never replays the fade. Opacity only, one
2150 shot, no layout change. */
2151@media (prefers-reduced-motion: no-preference) {
2152 .chat-markdown[data-streaming] > *,
2153 .chat-markdown[data-streaming] .chat-markdown-shiki {
2154 transition: opacity 600ms ease-out;
2155
2156 @starting-style {
2157 opacity: 0;
2158 }
2159 }
2160}
2161
2162/* Diagnostics-style tables: row separators only, uppercase headers, and a
2163 scroll-fade container for horizontal overflow. The root chat-markdown
2164 wrapping rules (overflow-wrap: anywhere) would let columns shrink to single
2165 characters and defeat the overflow — restore word-boundary wrapping so the
2166 min column width is the longest word. */
2167.chat-markdown table {
2168 width: 100%;
2169 min-width: max-content;
2170 border-collapse: collapse;
2171 overflow-wrap: normal;
2172 word-break: normal;
2173 font-size: 0.75rem;
2174}
2175
2176.chat-markdown th,
2177.chat-markdown td {
2178 padding: 0.45rem 0.75rem;
2179 text-align: left;
2180}
2181
2182.chat-markdown thead th {
2183 border-bottom: 1px solid color-mix(in srgb, var(--contrast-border) 60%, transparent);
2184 padding-block: 0.55rem;
2185 font-weight: 600;
2186 white-space: nowrap;
2187}
2188
2189.chat-markdown tbody td {
2190 border-bottom: 1px solid color-mix(in srgb, var(--contrast-border) 60%, transparent);
2191}
2192
2193/* Collapsed (default): single-line cells truncate so arbitrary chat content
2194 cannot blow the column out; the footer toggle expands cells to wrap. */
2195.chat-markdown .chat-markdown-table-container[data-expanded="false"] th,
2196.chat-markdown .chat-markdown-table-container[data-expanded="false"] td {
2197 overflow: hidden;
2198 max-width: 24rem;
2199 text-overflow: ellipsis;
2200 white-space: nowrap;
2201}
2202
2203.chat-markdown .chat-markdown-table-container[data-expanded="true"] td {
2204 max-width: 24rem;
2205 overflow-wrap: anywhere;
2206}
2207
2208:root {
2209 --diff-addition: var(--success);
2210 --diff-deletion: var(--destructive);
2211 --diff-addition-foreground: light-dark(var(--color-emerald-600), var(--color-emerald-400));
2212 --diff-deletion-foreground: light-dark(var(--color-red-600), var(--color-red-400));
2213}
2214
2215/* Chat width setting. Comfortable keeps the default --chat-content-max-width,
2216 which sizes the timeline lane, the composer, and ChatCanvas's width probe. */
2217:root[data-chat-width="wide"] {
2218 --chat-content-max-width: 72rem;
2219}
2220
2221:root[data-chat-width="full"] {
2222 --chat-content-max-width: 100%;
2223}
2224
2225:root[data-diff-color-scheme="blue-orange"] {
2226 --diff-addition: light-dark(var(--color-blue-600), var(--color-blue-400));
2227 --diff-deletion: light-dark(var(--color-orange-600), var(--color-orange-400));
2228 --diff-addition-foreground: var(--diff-addition);
2229 --diff-deletion-foreground: var(--diff-deletion);
2230 --diffs-addition-color-override: var(--diff-addition);
2231 --diffs-deletion-color-override: var(--diff-deletion);
2232 --trees-status-added-override: var(--diff-addition);
2233 --trees-status-deleted-override: var(--diff-deletion);
2234 --trees-status-untracked-override: var(--diff-addition);
2235 --trees-git-added-color-override: var(--diff-addition);
2236 --trees-git-deleted-color-override: var(--diff-deletion);
2237 --trees-git-untracked-color-override: var(--diff-addition);
2238}
2239
2240.diff-render-surface diffs-container {
2241 border: 0;
2242 border-radius: 0;
2243 overflow: clip;
2244 background: var(--code-background);
2245 color: var(--code-foreground);
2246}
2247
2248/* Pierre's find panel and match highlights read these. */
2249.diff-render-surface {
2250 --diffs-search-panel-bg: var(--popover);
2251 --diffs-search-panel-fg: var(--popover-foreground);
2252 --diffs-search-panel-accent: var(--primary);
2253 --diffs-search-panel-font: var(--font-sans);
2254}
2255
2256/* Pierre's file renderer owns its own surface tokens. Keep the root of a file
2257 preview on the same themeable code surface as the diff panel. */
2258.diff-render-surface diffs-container,
2259.file-preview-virtualizer diffs-container {
2260 --diffs-bg: var(--code-background) !important;
2261 --diffs-light-bg: var(--code-background) !important;
2262 --diffs-dark-bg: var(--code-background) !important;
2263 background-color: var(--code-background) !important;
2264 color: var(--code-foreground) !important;
2265}
2266
2267.ultrathink-frame {
2268 --ultrathink-spectrum: linear-gradient(
2269 120deg,
2270 #ff6b6b 0%,
2271 #f59e0b 18%,
2272 #22c55e 36%,
2273 #14b8a6 54%,
2274 #3b82f6 72%,
2275 #ec4899 90%,
2276 #ff6b6b 100%
2277 );
2278 position: relative;
2279}
2280
2281.ultrathink-frame::before {
2282 content: "";
2283 position: absolute;
2284 inset: 0;
2285 border-radius: inherit;
2286 padding: 2px;
2287 background-image: var(--ultrathink-spectrum);
2288 background-size: 220% 220%;
2289 background-position: 0% 50%;
2290 pointer-events: none;
2291 filter: saturate(0.82) brightness(0.92);
2292 -webkit-mask:
2293 linear-gradient(#000 0 0) content-box,
2294 linear-gradient(#000 0 0);
2295 -webkit-mask-composite: xor;
2296 mask:
2297 linear-gradient(#000 0 0) content-box,
2298 linear-gradient(#000 0 0);
2299 mask-composite: exclude;
2300}
2301
2302.ultrathink-frame > * {
2303 position: relative;
2304}
2305
2306.ultrathink-chroma {
2307 filter: saturate(1.2);
2308}
2309
2310@keyframes preview-loading-progress {
2311 from {
2312 transform: scaleX(0.04);
2313 }
2314
2315 to {
2316 transform: scaleX(0.9);
2317 }
2318}
2319
2320.preview-loading-progress {
2321 opacity: 0;
2322 transform: scaleX(1);
2323 transition:
2324 transform 150ms ease-out,
2325 opacity 150ms ease-out 220ms;
2326}
2327
2328.preview-loading-progress[data-loading="true"] {
2329 opacity: 1;
2330 animation: preview-loading-progress 5.3s cubic-bezier(0.1, 0.5, 0.2, 1) forwards;
2331}
2332
2333@media (prefers-reduced-motion: reduce) {
2334 .preview-loading-progress,
2335 .preview-loading-progress[data-loading="true"] {
2336 transition: none;
2337 animation: none;
2338 }
2339
2340 .preview-loading-progress[data-loading="true"] {
2341 transform: scaleX(0.9);
2342 }
2343}
2344
2345/* Let the sortable rows open label clearance before painting the dividers.
2346 This runs once per pickup; rows retain dnd-kit's normal 200ms transitions. */
2347.sidebar-drag-boundary-label {
2348 animation: sidebar-drag-label-reveal 200ms step-end;
2349}
2350
2351@keyframes sidebar-drag-label-reveal {
2352 from {
2353 visibility: hidden;
2354 }
2355 to {
2356 visibility: visible;
2357 }
2358}
2359
2360@layer components {
2361 .chat-composer-lane {
2362 padding-inline-start: calc(
2363 env(safe-area-inset-left) + 0.75rem + var(--chat-lane-inset-start, 0px)
2364 );
2365 padding-inline-end: calc(
2366 env(safe-area-inset-right) + 0.75rem + var(--chat-lane-inset-end, 0px)
2367 );
2368 }
2369 [data-chat-canvas] .messages-timeline-scroll {
2370 padding-inline-start: calc(
2371 0.75rem + var(--chat-lane-inset-start, 0px) - var(--chat-timeline-gutter, 0px)
2372 );
2373 padding-inline-end: calc(
2374 0.75rem + var(--chat-lane-inset-end, 0px) - var(--chat-timeline-gutter, 0px)
2375 );
2376 }
2377 .chat-scroll-to-bottom,
2378 .chat-banner-lane {
2379 inset-inline-start: var(--chat-lane-inset-start, 0px);
2380 inset-inline-end: var(--chat-lane-inset-end, 0px);
2381 }
2382 @media (min-width: 640px) {
2383 .chat-composer-lane {
2384 padding-inline-start: calc(
2385 env(safe-area-inset-left) + 3rem + var(--chat-lane-inset-start, 0px)
2386 );
2387 padding-inline-end: calc(env(safe-area-inset-right) + 3rem + var(--chat-lane-inset-end, 0px));
2388 }
2389 [data-chat-canvas] .messages-timeline-scroll {
2390 padding-inline-start: calc(
2391 3rem + var(--chat-lane-inset-start, 0px) - var(--chat-timeline-gutter, 0px)
2392 );
2393 padding-inline-end: calc(
2394 3rem + var(--chat-lane-inset-end, 0px) - var(--chat-timeline-gutter, 0px)
2395 );
2396 }
2397 }
2398}