Skip to content
860 linesCodeBlameRaw
1@import "tailwindcss" source(".");
2@import "@g1t/theme/tokens.css";
3@import "@g1t/theme/fonts.css";
4
5/* Tailwind's names for the shared g1t tokens. */
6@theme {
7 --font-sans: var(--g1t-font-sans);
8 --font-display: var(--g1t-font-display);
9 --font-mono: var(--g1t-font-mono);
10
11 /* Hanken and Bricolage want less tightening than a text face set close. */
12 --tracking-tight: -0.012em;
13
14 --color-bg: var(--g1t-bg);
15 /* Behind the page panel, where the sidebar sits flat (components/shell.tsx). */
16 --color-shell: #0b0b0d;
17 /* The dock's own surface: a floating bar a step above the shell. */
18 --color-dock: #15151a;
19 --color-surface: var(--g1t-surface);
20 --color-raised: var(--g1t-raised);
21 --color-line: var(--g1t-line);
22 --color-line-strong: var(--g1t-line-strong);
23
24 --color-fg: var(--g1t-fg);
25 --color-fg-soft: var(--g1t-fg-soft);
26 --color-muted: var(--g1t-muted);
27 --color-faint: var(--g1t-faint);
28
29 --color-accent: var(--g1t-accent);
30 --color-accent-hover: var(--g1t-accent-hover);
31 --color-accent-dim: var(--g1t-accent-dim);
32
33 --color-success: var(--g1t-success);
34 --color-success-dim: var(--g1t-success-dim);
35
36 --color-info: var(--g1t-info);
37 --color-merged: var(--g1t-merged);
38 --color-warn: var(--g1t-warn);
39 --color-danger: var(--g1t-danger);
40
41 --animate-fade-up: fade-up 0.6s ease-out both;
42 --animate-dash: dash 1.2s linear infinite;
43 /* An agent's face, on hover: a small, quick wiggle (components/agent-avatar.tsx). */
44 --animate-wiggle: wiggle 0.45s ease-in-out;
45
46 /* Menus, popovers and dialogs (components/ui): in quickly, out quicker. */
47 --animate-pop-in: pop-in 0.16s cubic-bezier(0.16, 1, 0.3, 1);
48 --animate-pop-out: pop-out 0.1s ease-in forwards;
49 --animate-fade-in: fade-in 0.16s ease-out;
50 --animate-fade-out: fade-out 0.12s ease-in forwards;
51 --animate-dialog-in: dialog-in 0.2s cubic-bezier(0.16, 1, 0.3, 1);
52 --animate-dialog-out: dialog-out 0.12s ease-in forwards;
53 /* Sheets (components/ui/sheet.tsx) slide in from their edge. */
54 --animate-sheet-in-right: sheet-in-right 0.24s cubic-bezier(0.16, 1, 0.3, 1);
55 --animate-sheet-out-right: sheet-out-right 0.16s ease-in forwards;
56 --animate-sheet-in-left: sheet-in-left 0.24s cubic-bezier(0.16, 1, 0.3, 1);
57 --animate-sheet-out-left: sheet-out-left 0.16s ease-in forwards;
58}
59
60@keyframes sheet-in-bottom {
61 from {
62 transform: translateY(100%);
63 }
64}
65
66@keyframes sheet-in-right {
67 from {
68 transform: translateX(100%);
69 }
70}
71
72@keyframes sheet-out-right {
73 to {
74 transform: translateX(100%);
75 }
76}
77
78@keyframes sheet-in-left {
79 from {
80 transform: translateX(-100%);
81 }
82}
83
84@keyframes sheet-out-left {
85 to {
86 transform: translateX(-100%);
87 }
88}
89
90@keyframes pop-in {
91 from {
92 opacity: 0;
93 transform: scale(0.96) translateY(-2px);
94 }
95}
96
97@keyframes pop-out {
98 to {
99 opacity: 0;
100 transform: scale(0.96);
101 }
102}
103
104@keyframes fade-in {
105 from {
106 opacity: 0;
107 }
108}
109
110@keyframes fade-out {
111 to {
112 opacity: 0;
113 }
114}
115
116@keyframes dialog-in {
117 from {
118 opacity: 0;
119 transform: translateY(6px) scale(0.97);
120 }
121}
122
123@keyframes dialog-out {
124 to {
125 opacity: 0;
126 transform: translateY(6px) scale(0.97);
127 }
128}
129
130/* Never from nothing: the first paint already shows the words. */
131@keyframes fade-up {
132 from {
133 opacity: 0.4;
134 transform: translateY(8px);
135 }
136}
137
138@keyframes wiggle {
139 25% {
140 transform: rotate(-8deg);
141 }
142 60% {
143 transform: rotate(6deg);
144 }
145}
146
147@keyframes dash {
148 to {
149 stroke-dashoffset: -16;
150 }
151}
152
153html {
154 color-scheme: dark;
155 scroll-behavior: smooth;
156 /* A tap shows the control's own active state, not the browser's grey flash. */
157 -webkit-tap-highlight-color: transparent;
158 /* Phones: no text inflation on rotation. */
159 -webkit-text-size-adjust: 100%;
160 text-size-adjust: 100%;
161}
162
163/*
164 * The app's frame (components/shell.tsx), set on <body data-frame="app">:
165 * the page is a rounded panel inset from the window, with the dock beside
166 * it and, on a phone, the bottom bar under it. What sticks to the top or
167 * the bottom of a page reads these:
168 * --frame-top the gap above the panel
169 * --topbar-h the panel's top edge plus its header: where sticky things sit
170 * --tabbar-h what the bottom takes: the gap under the panel, and the phone's bar
171 * --page-h the height left for a page that fills the panel (Chat)
172 * The public frame keeps a plain header 3.5rem tall and nothing at the bottom.
173 */
174:root {
175 --frame-top: 0px;
176 --tabbar-h: 0px;
177 --topbar-h: 3.5rem;
178 --page-h: calc(100dvh - var(--topbar-h) - var(--tabbar-h));
179}
180body[data-frame="app"] {
181 --frame-top: 0.5rem;
182 --topbar-h: calc(var(--frame-top) + 3.5rem);
183 --tabbar-h: 0.5rem;
184 --page-h: calc(100dvh - var(--topbar-h) - var(--tabbar-h));
185}
186@media (max-width: 767px) {
187 body[data-frame="app"] {
188 --frame-top: max(0.375rem, env(safe-area-inset-top));
189 /* The bottom bar: 64px tall, 8px off the edge, 6px under the panel. */
190 --tabbar-h: calc(4.875rem + env(safe-area-inset-bottom));
191 }
192}
193
194/*
195 * Phones: a field under 16px makes iOS zoom the page when it is focused, so
196 * every field is at least that; and nothing scrolls the page sideways.
197 */
198@media (max-width: 767px) {
199 input:not([type="checkbox"]):not([type="radio"]):not([type="range"]),
200 textarea,
201 select {
202 font-size: max(16px, 1em) !important;
203 }
204 body {
205 overflow-x: clip;
206 }
207}
208
209body {
210 @apply bg-bg font-sans text-fg antialiased;
211}
212body[data-frame="app"] {
213 @apply bg-shell;
214}
215
216::selection {
217 background: color-mix(in srgb, var(--color-accent) 35%, transparent);
218}
219
220/* In the base layer, so a component can opt out with a utility. */
221@layer base {
222 :focus-visible {
223 outline: 2px solid var(--color-accent);
224 outline-offset: 2px;
225 }
226
227 /*
228 * A grid's items may shrink below their content. Without this, one long
229 * line that does not wrap (a command, a commit hash, an address) widens
230 * the whole column, and on a phone the page scrolls sideways.
231 */
232 .grid > * {
233 min-width: 0;
234 }
235
236 /*
237 * Headlines are set in Bricolage Grotesque, its optical size following
238 * the font size; small section labels that happen to be headings stay in
239 * the text face. A utility on the element still wins.
240 */
241 :where(h1, h2),
242 :where(h3):is(.text-xl, .text-2xl, .text-3xl, .text-4xl) {
243 font-family: var(--font-display);
244 font-optical-sizing: auto;
245 }
246 :where(h2):is(.text-xs, .text-sm, .text-base) {
247 font-family: var(--font-sans);
248 }
249
250 /* Checkboxes, radios and ranges in g1t's own colour, not the browser's. */
251 :root {
252 accent-color: var(--color-accent);
253 }
254}
255
256@media (prefers-reduced-motion: reduce) {
257 *,
258 *::before,
259 *::after {
260 animation: none !important;
261 scroll-behavior: auto !important;
262 }
263}
264
265/* Rendered markdown: READMEs, briefs and docs. */
266.prose > :first-child {
267 margin-top: 0;
268 padding-top: 0;
269}
270.prose {
271 @apply leading-7 text-fg;
272}
273.prose > * + * {
274 @apply mt-4;
275}
276.prose h1 {
277 @apply mt-0 text-3xl font-semibold tracking-tight;
278}
279.prose h2 {
280 @apply mt-10 border-b border-line pb-2 text-xl font-semibold tracking-tight;
281}
282.prose h3 {
283 @apply mt-8 text-base font-semibold;
284}
285.prose p,
286.prose li {
287 @apply text-[0.9375rem] text-fg/90;
288}
289.prose a {
290 @apply text-accent underline decoration-accent/40 underline-offset-4 hover:decoration-accent;
291}
292.prose ul {
293 @apply list-disc space-y-1.5 pl-6;
294}
295.prose ol {
296 @apply list-decimal space-y-1.5 pl-6;
297}
298.prose code {
299 @apply rounded bg-raised px-1.5 py-0.5 font-mono text-[0.85em];
300}
301.prose pre {
302 @apply overflow-x-auto rounded-lg border border-line bg-surface p-4 font-mono text-sm leading-6;
303}
304.prose pre code {
305 @apply bg-transparent p-0;
306}
307.prose blockquote {
308 @apply border-l-2 border-line-strong pl-4 text-muted;
309}
310.prose table {
311 @apply w-full border-collapse text-sm;
312}
313.prose th {
314 @apply border-b border-line-strong px-3 py-2 text-left font-medium text-muted;
315}
316.prose td {
317 @apply border-b border-line px-3 py-2 align-top;
318}
319.prose hr {
320 @apply border-line;
321}
322.prose h4 {
323 @apply mt-6 text-sm font-semibold;
324}
325.prose li.task-list-item {
326 @apply list-none;
327}
328.prose ul.contains-task-list {
329 @apply pl-1;
330}
331.prose kbd {
332 @apply rounded border border-line-strong bg-raised px-1.5 py-px font-mono text-[0.8em] shadow-[inset_0_-1px_0_var(--color-line-strong)];
333}
334.prose del {
335 @apply text-muted;
336}
337.prose details {
338 @apply rounded-lg border border-line px-4 py-2;
339}
340.prose summary {
341 @apply cursor-pointer font-medium;
342}
343.prose img {
344 @apply inline max-w-full;
345}
346.prose section[data-footnotes] {
347 @apply mt-10 border-t border-line pt-4 text-sm text-muted;
348}
349.prose a[data-footnote-ref] {
350 @apply no-underline;
351}
352
353/* Syntax-highlighted code from Shiki. */
354.shiki {
355 @apply bg-transparent! font-mono text-sm leading-6;
356}
357.shiki code {
358 counter-reset: line;
359 display: block;
360}
361.shiki .line::before {
362 counter-increment: line;
363 content: counter(line);
364 @apply mr-5 inline-block w-10 text-right text-faint select-none;
365}
366
367/*
368 * Artwork (components/art.tsx). Every piece loops on its drawing's
369 * `--dur` and starts at its own `--delay`, so the parts of one drawing
370 * stay in step. Only transforms, opacity and stroke offsets move. The
371 * resting style of each class is the finished picture: that is what
372 * prefers-reduced-motion shows, since the rule above stops every
373 * animation.
374 */
375.art-flow {
376 animation: dash 1.6s linear infinite;
377}
378
379/* Something travelling along a track (pathLength 100). Unseen at rest. */
380.art-packet {
381 opacity: 0;
382 stroke-dasharray: 4 200;
383 stroke-dashoffset: 4;
384 animation: art-travel var(--dur, 3s) cubic-bezier(0.45, 0, 0.35, 1) infinite;
385 animation-delay: var(--delay, 0s);
386}
387@keyframes art-travel {
388 0% {
389 stroke-dashoffset: 4;
390 opacity: 0;
391 }
392 3% {
393 opacity: 1;
394 }
395 32% {
396 opacity: 1;
397 }
398 36%,
399 100% {
400 stroke-dashoffset: -100;
401 opacity: 0;
402 }
403}
404
405/* A face that lights up for most of the loop. Lit at rest. */
406.art-lit {
407 opacity: 0.85;
408 animation: art-lit var(--dur, 8s) ease-in-out infinite both;
409 animation-delay: var(--delay, 0s);
410}
411@keyframes art-lit {
412 0%,
413 4% {
414 opacity: 0;
415 }
416 12%,
417 70% {
418 opacity: 0.9;
419 }
420 88%,
421 100% {
422 opacity: 0;
423 }
424}
425
426/* A short flash, as a check runs over a layer. Unseen at rest. */
427.art-scan {
428 opacity: 0;
429 animation: art-scan var(--dur, 8s) ease-out infinite both;
430 animation-delay: var(--delay, 0s);
431}
432@keyframes art-scan {
433 0% {
434 opacity: 0;
435 }
436 4% {
437 opacity: 1;
438 }
439 16%,
440 100% {
441 opacity: 0;
442 }
443}
444
445/* A line that draws itself, then fades. Drawn at rest. */
446.art-draw {
447 stroke-dasharray: 100;
448 stroke-dashoffset: 0;
449 animation: art-draw var(--dur, 8s) ease-in-out infinite both;
450 animation-delay: var(--delay, 0s);
451}
452@keyframes art-draw {
453 0% {
454 stroke-dashoffset: 100;
455 opacity: 1;
456 }
457 14%,
458 70% {
459 stroke-dashoffset: 0;
460 opacity: 1;
461 }
462 88%,
463 100% {
464 stroke-dashoffset: 0;
465 opacity: 0;
466 }
467}
468
469/* Something that appears, stays, and goes. Shown at rest. */
470.art-pop {
471 animation: art-pop var(--dur, 8s) ease-out infinite both;
472 animation-delay: var(--delay, 0s);
473}
474@keyframes art-pop {
475 0% {
476 opacity: 0;
477 transform: translateY(4px);
478 }
479 6%,
480 72% {
481 opacity: 1;
482 transform: none;
483 }
484 86%,
485 100% {
486 opacity: 0;
487 transform: none;
488 }
489}
490
491/* A layer dropping onto a stack. In place at rest. */
492.art-drop {
493 animation: art-drop var(--dur, 7s) cubic-bezier(0.2, 0.9, 0.3, 1.2) infinite both;
494 animation-delay: var(--delay, 0s);
495}
496@keyframes art-drop {
497 0% {
498 opacity: 0;
499 transform: translateY(-26px);
500 }
501 8%,
502 74% {
503 opacity: 1;
504 transform: none;
505 }
506 88%,
507 100% {
508 opacity: 0;
509 transform: none;
510 }
511}
512
513/* The layer that fails: drops, shakes, and is sent back. */
514.art-eject {
515 animation: art-eject var(--dur, 7s) ease-in-out infinite both;
516 animation-delay: var(--delay, 0s);
517}
518@keyframes art-eject {
519 0% {
520 opacity: 0;
521 transform: translateY(-26px);
522 }
523 8%,
524 30% {
525 opacity: 1;
526 transform: none;
527 }
528 33% {
529 transform: translateX(-3px);
530 }
531 36% {
532 transform: translateX(3px);
533 }
534 39% {
535 transform: translateX(-2px);
536 }
537 42% {
538 opacity: 1;
539 transform: none;
540 }
541 56%,
542 100% {
543 opacity: 0;
544 transform: translate(-40px, -22px);
545 }
546}
547
548/* A layer rising out of the one below it. In place at rest. */
549.art-rise {
550 animation: art-rise var(--dur, 7s) cubic-bezier(0.2, 0.8, 0.3, 1) infinite both;
551 animation-delay: var(--delay, 0s);
552}
553@keyframes art-rise {
554 0% {
555 opacity: 0;
556 transform: translateY(14px);
557 }
558 10%,
559 70% {
560 opacity: 1;
561 transform: none;
562 }
563 86%,
564 100% {
565 opacity: 0;
566 transform: none;
567 }
568}
569
570/* A soft glow breathing around a point. */
571.art-pulse {
572 transform-box: fill-box;
573 transform-origin: center;
574 animation: art-pulse var(--dur, 4s) ease-in-out infinite;
575 animation-delay: var(--delay, 0s);
576}
577@keyframes art-pulse {
578 0%,
579 100% {
580 transform: scale(0.8);
581 opacity: 1;
582 }
583 50% {
584 transform: scale(1.5);
585 opacity: 0.35;
586 }
587}
588
589/* Drawings off-screen hold still (components/art.tsx, Live). */
590[data-paused] * {
591 animation-play-state: paused !important;
592}
593/*
594 * Each drawing on its own layer, so a frame of its motion repaints only the
595 * drawing, not the page around it or the blurred header passing over it.
596 */
597[data-live] {
598 will-change: transform;
599}
600
601/* The landing page's steps, lit in turn along their line. */
602/* The ring is drawn once and only fades and grows, so the compositor runs it. */
603.flow-step::after {
604 content: "";
605 position: absolute;
606 inset: 0;
607 border-radius: inherit;
608 box-shadow: 0 0 0 6px color-mix(in srgb, var(--color-accent) 18%, transparent);
609 opacity: 0;
610 pointer-events: none;
611 animation: flow-step 10s ease-in-out infinite both;
612 animation-delay: var(--delay, 0s);
613}
614@keyframes flow-step {
615 0%,
616 100% {
617 opacity: 0;
618 transform: scale(0.82);
619 }
620 6% {
621 opacity: 1;
622 transform: none;
623 }
624 16% {
625 opacity: 0;
626 transform: none;
627 }
628}
629.flow-runner {
630 animation: flow-runner 10s cubic-bezier(0.45, 0, 0.35, 1) infinite both;
631}
632@keyframes flow-runner {
633 0% {
634 transform: translateX(0);
635 opacity: 0;
636 }
637 4%,
638 76% {
639 opacity: 1;
640 }
641 80%,
642 100% {
643 transform: translateX(100%);
644 opacity: 0;
645 }
646}
647@media (prefers-reduced-motion: reduce) {
648 .flow-runner {
649 display: none;
650 }
651}
652
653/* The sidebar trading one far-side menu for another, in place. */
654@keyframes g1t-swap {
655 from {
656 opacity: 0;
657 transform: translateX(10px);
658 }
659 to {
660 opacity: 1;
661 transform: none;
662 }
663}
664
665/*
666 * Behind the landing page's opening: the same dark gray, with a faint
667 * lavender and peach glow low on the horizon and a few stars.
668 */
669.dusk {
670 background:
671 radial-gradient(1px 1px at 12% 18%, rgb(255 255 255 / 0.35), transparent),
672 radial-gradient(1px 1px at 27% 8%, rgb(255 255 255 / 0.25), transparent),
673 radial-gradient(1px 1px at 63% 12%, rgb(255 255 255 / 0.3), transparent),
674 radial-gradient(1.5px 1.5px at 90% 9%, rgb(255 255 255 / 0.35), transparent),
675 radial-gradient(60% 35% at 50% 100%, rgb(255 189 140 / 0.08), transparent 70%),
676 radial-gradient(80% 45% at 50% 100%, rgb(182 168 255 / 0.1), transparent 75%),
677 var(--color-bg);
678}
679/* A planet rising behind the horizon, its lit edge catching the glow. */
680.dusk-planet {
681 background: radial-gradient(circle at 70% 30%, rgb(182 168 255 / 0.12), rgb(255 189 140 / 0.04) 45%, transparent 70%);
682 box-shadow: inset 14px -8px 50px rgb(255 214 186 / 0.07);
683}
684
685/*
686 * On a phone, every field's text is at least 16px: iOS zooms the page into
687 * a field set smaller as soon as it is focused. Unlayered, so it wins over
688 * a field's text-sm utility.
689 */
690@media (max-width: 639px) {
691 input:not([type="checkbox"], [type="radio"], [type="range"], [type="color"], [type="file"]),
692 textarea,
693 select {
694 font-size: max(16px, 1em);
695 }
696}
697
698/* A card's Markdown preview in chat (components/chat/card.tsx): the same Markdown, smaller and tighter. */
699.card-md .prose {
700 @apply text-[0.8125rem] leading-relaxed;
701}
702.card-md .prose > * + * {
703 @apply mt-2;
704}
705.card-md .prose p,
706.card-md .prose li {
707 @apply text-[0.8125rem] text-fg/85;
708}
709.card-md .prose h1,
710.card-md .prose h2,
711.card-md .prose h3,
712.card-md .prose h4 {
713 @apply mt-3 border-0 pb-0 text-sm font-semibold;
714}
715.card-md .prose ul,
716.card-md .prose ol {
717 @apply space-y-0.5 pl-5;
718}
719.card-md .prose pre {
720 @apply p-2.5 text-xs leading-5;
721}
722
723/*
724 * The Docs page editor (components/docs/editor.tsx) is BlockNote with its
725 * shadcn components. Their classes are generated from the package, and
726 * shadcn's token names map onto g1t's. `accent` and `muted` mean
727 * something else in shadcn (a hover fill, a quiet fill), so inside the
728 * editor they are redefined; g1t's own blocks there use the g1t tokens
729 * directly.
730 */
731@source "../../../node_modules/@blocknote/shadcn/dist";
732
733@theme {
734 --color-background: var(--g1t-bg);
735 --color-foreground: var(--g1t-fg);
736 --color-popover: var(--g1t-surface);
737 --color-popover-foreground: var(--g1t-fg);
738 --color-card: var(--g1t-surface);
739 --color-card-foreground: var(--g1t-fg);
740 --color-primary: var(--g1t-accent);
741 --color-primary-foreground: var(--g1t-bg);
742 --color-secondary: var(--g1t-raised);
743 --color-secondary-foreground: var(--g1t-fg);
744 --color-muted-foreground: var(--g1t-muted);
745 --color-accent-foreground: var(--g1t-fg);
746 --color-destructive: var(--g1t-danger);
747 --color-destructive-foreground: var(--g1t-bg);
748 --color-border: var(--g1t-line);
749 --color-input: var(--g1t-line);
750 --color-ring: var(--g1t-accent);
751}
752
753.bn-shadcn {
754 --color-accent: var(--g1t-raised);
755 --color-muted: var(--g1t-raised);
756 --bn-colors-editor-background: transparent;
757 --bn-colors-editor-text: var(--g1t-fg);
758 --bn-colors-menu-background: var(--g1t-surface);
759 --bn-colors-menu-text: var(--g1t-fg);
760 --bn-colors-hovered-background: var(--g1t-raised);
761 --bn-colors-side-menu: var(--g1t-faint);
762 --bn-colors-border: var(--g1t-line);
763 --bn-border-radius: 8px;
764}
765
766.g1t-editor .bn-editor {
767 font-family: var(--g1t-font-sans);
768 font-size: 1rem;
769 line-height: 1.65;
770 background: transparent;
771 color: var(--g1t-fg);
772}
773
774/* The drag handle and + sit in the margin, so the text lines up with the title. */
775@media (min-width: 768px) {
776 .g1t-editor .bn-container {
777 margin-inline: -54px;
778 }
779}
780
781.g1t-editor .bn-block-content[data-content-type="heading"] h1,
782.g1t-editor .bn-block-content[data-content-type="heading"] h2,
783.g1t-editor .bn-block-content[data-content-type="heading"] h3 {
784 font-family: var(--g1t-font-display);
785 letter-spacing: -0.012em;
786}
787
788.g1t-editor .bn-inline-content code {
789 font-family: var(--g1t-font-mono);
790 font-size: 0.875em;
791 padding: 0.1em 0.35em;
792 border-radius: 4px;
793 background: var(--g1t-raised);
794}
795
796.g1t-editor .bn-block-content[data-content-type="codeBlock"] {
797 border-radius: 10px;
798 background: var(--g1t-surface);
799 border: 1px solid var(--g1t-line);
800}
801
802.g1t-editor .bn-thread-mark {
803 background: color-mix(in srgb, var(--g1t-warn) 22%, transparent);
804 border-bottom: 2px solid color-mix(in srgb, var(--g1t-warn) 60%, transparent);
805}
806
807.g1t-editor a {
808 color: var(--g1t-accent);
809}
810
811/* A page read before the editor loads: the same measure as the editor. */
812.docs-read .prose {
813 font-size: 1rem;
814 line-height: 1.65;
815}
816
817/* The chat composer (components/chat/editor.tsx): formatting as messages show it. */
818.chat-editor {
819 white-space: pre-wrap;
820 word-break: break-word;
821 min-height: 1.5rem;
822}
823.chat-editor p.is-editor-empty:first-child::before {
824 content: attr(data-placeholder);
825 float: left;
826 height: 0;
827 pointer-events: none;
828 color: var(--color-faint);
829}
830.chat-editor strong {
831 @apply font-semibold text-fg;
832}
833.chat-editor s {
834 @apply text-muted;
835}
836.chat-editor a {
837 @apply text-accent underline underline-offset-2;
838}
839.chat-editor code {
840 @apply rounded-[4px] border border-line bg-raised px-1 py-px font-mono text-[0.84em] text-fg-soft;
841}
842.chat-editor pre {
843 @apply my-1 rounded-md border border-line bg-bg px-3 py-2 font-mono text-[0.8125rem] leading-relaxed text-fg-soft;
844}
845.chat-editor pre code {
846 @apply border-0 bg-transparent p-0 text-[1em];
847}
848.chat-editor ul {
849 @apply list-disc pl-5 marker:text-faint;
850}
851.chat-editor ol {
852 @apply list-decimal pl-5 marker:text-faint;
853}
854.chat-editor li > p {
855 @apply pl-0.5;
856}
857.chat-editor blockquote {
858 @apply my-0.5 border-l-2 border-line-strong pl-3 text-muted;
859}
860