1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
|
@import "tailwindcss";
/* Syntax-highlight theme for fenced code blocks in rendered Markdown. */
@import "highlight.js/styles/atom-one-dark.min.css";
/* Custom monokai theme — must be imported BEFORE the @plugin block. */
@import "./themes/monokai.css";
/* DaisyUI v5 — enable the plugin AND bundle the monokai theme (set as default,
applied via <html data-theme="monokai">). Themes not listed here are NOT
bundled, so monokai must be named explicitly, not merely referenced. */
@plugin "daisyui" {
themes: monokai --default;
}
/* Inset "well" — the Artix wiki's code blocks (darkstrap `pre`): a panel that
reads as pressed INTO the page — the page colour darkened, a dark inner
shadow under the top edge, a faint light catch-line under the bottom edge.
Shared by the chat's thinking/tool blocks (LazyCollapse) and the sidebar
views (TilePanels). darkstrap: background rgba(0,0,0,.3); box-shadow
rgba(255,255,255,.1) 0 1px 0, rgba(0,0,0,.8) 0 1px 7px 0 inset; radius 4px.
Ported onto the active daisyUI theme so it always matches, and toned down a
step: the fill is the theme's page colour (base-100 — both the chat and the
sidebar sit on it) darkened 18% (darkstrap: 30%), computed OPAQUE and exposed
as --inset-well-bg so sticky children can paint the same colour; the inner
shadow is 0.6 (darkstrap: 0.8); the catch-line is the theme's text colour at
10% instead of plain white. In @layer components so utilities can override. */
@layer components {
.inset-well {
--inset-well-bg: color-mix(in srgb, var(--color-base-100) 82%, black);
background-color: var(--inset-well-bg);
border: 0;
border-radius: var(--radius-box);
box-shadow:
0 1px 0 color-mix(in srgb, var(--color-base-content) 10%, transparent),
inset 0 1px 7px 0 rgb(0 0 0 / 0.6);
}
}
/* App navbar — the Artix wiki's navbar (darkstrap `.navbar-inner` on a
fixed-top bar): a top-to-bottom gradient (#444 → #222), a soft drop shadow
under the bar (0 1px 10px), and engraved text (1px black text-shadow).
Shared by the workspace top bar (App) and the home page header. Ported onto
the active daisyUI theme so it always matches. darkstrap's bar is LIGHTER
than its near-black page (that's what makes the shadow read), so the
gradient is shifted to keep that relationship on our lighter page: it runs
from the page colour (base-100) lifted 20% toward the text colour down to
base-100 lifted 3%. No bottom border (darkstrap has a 1px #030303 one —
removed by user request; the gradient + shadow carry the edge). The shadow
is 0.5 (darkstrap: 0.1 — invisible on our lighter page). Positioned +
z-indexed so the shadow paints over the content below. */
@layer components {
.app-navbar {
position: relative;
z-index: 10;
flex-shrink: 0;
background-image: linear-gradient(
to bottom,
color-mix(in srgb, var(--color-base-100) 80%, var(--color-base-content)),
color-mix(in srgb, var(--color-base-100) 97%, var(--color-base-content))
);
box-shadow: 0 1px 10px rgb(0 0 0 / 0.5);
}
/* Engraved navbar text — titles only; on tiny faint labels it muddies. */
.app-navbar-engraved {
text-shadow: 0 1px 0 black;
}
}
/* Navbar buttons — the navbar's (`.app-navbar`) raised button, in the
same depth language as the rest of the UI: at rest they're RAISED (a
gradient a step lighter than the bar, a light highlight inside the top edge,
a thin dark edge + small drop below, light primary-blue text), and when
PRESSED they flip to the inset well (dark inner shadow) — pushed into the
bar. `.navbar-btn-primary` is the main action: the same button, tinted with
the theme's primary colour. `.navbar-btn-icon` is the square icon-only form.
Self-contained (not DaisyUI .btn) so nothing fights the gradients; colours
all come from the theme. */
@layer components {
.navbar-btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.375rem;
height: 2rem;
padding-inline: 0.75rem;
border-radius: var(--radius-field);
font-size: 0.875rem;
font-weight: 600;
line-height: 1;
white-space: nowrap;
cursor: pointer;
user-select: none;
/* The theme's primary, lifted toward white: crisp on the grey gradient
(plain primary read dim, and an engraved text-shadow muddied it at this
size — the shadow stays on the big titles only). */
color: color-mix(in srgb, var(--color-primary) 75%, white);
border: 1px solid rgb(0 0 0 / 0.55);
background-image: linear-gradient(
to bottom,
color-mix(in srgb, var(--color-base-100) 72%, var(--color-base-content)),
color-mix(in srgb, var(--color-base-100) 90%, var(--color-base-content))
);
box-shadow:
inset 0 1px 0 rgb(255 255 255 / 0.12),
0 1px 2px rgb(0 0 0 / 0.45);
transition:
filter 120ms ease-out,
box-shadow 120ms ease-out;
}
.navbar-btn:hover {
filter: brightness(1.12);
}
.navbar-btn:active,
.navbar-btn[aria-expanded="true"] {
/* Pressed → the inset well: pushed into the bar. */
filter: none;
background-image: none;
background-color: color-mix(in srgb, var(--color-base-100) 82%, black);
box-shadow:
inset 0 1px 5px rgb(0 0 0 / 0.7),
0 1px 0 color-mix(in srgb, var(--color-base-content) 10%, transparent);
}
.navbar-btn:focus-visible {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
}
.navbar-btn-icon {
width: 2rem;
padding-inline: 0;
}
/* Secondary navbar actions: visually just text — no background, border or
shadow. The light primary-blue colour (same as the buttons' text) and the
spacing mark them as clickable; hover brightens + underlines, press dims. */
.navbar-link {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.375rem;
height: 2rem;
min-width: 2rem; /* icon-only (compact) links keep a comfortable tap target */
padding-inline: 0.375rem;
border-radius: var(--radius-field);
font-size: 0.875rem;
font-weight: 600;
line-height: 1;
white-space: nowrap;
cursor: pointer;
user-select: none;
background: none;
border: 0;
color: color-mix(in srgb, var(--color-primary) 75%, white);
text-underline-offset: 4px;
transition: color 120ms ease-out;
}
.navbar-link:hover {
color: color-mix(in srgb, var(--color-primary) 45%, white);
text-decoration: underline;
}
.navbar-link:active {
color: var(--color-primary);
}
.navbar-link:focus-visible {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
}
.navbar-btn-primary {
color: var(--color-primary-content);
background-image: linear-gradient(
to bottom,
color-mix(in srgb, var(--color-primary) 82%, white),
color-mix(in srgb, var(--color-primary) 88%, black)
);
box-shadow:
inset 0 1px 0 rgb(255 255 255 / 0.35),
0 1px 2px rgb(0 0 0 / 0.45);
}
.navbar-btn-primary:active {
background-color: color-mix(in srgb, var(--color-primary) 80%, black);
}
}
/* Raised — the INVERSE of `.inset-well`, for things that pop OUT of the page
(the home page's workspace tiles). Where the well has a dark shadow INSIDE
under its top edge and a light catch-line OUTSIDE below it, a raised surface
has a light highlight INSIDE along its top edge, a soft shadow OUTSIDE below
it, and a faint top-lit gradient over its own colour (lighter top → darker
bottom — convex, where the well is concave). Built on Tailwind's shadow
variables (not a bare box-shadow) so it COMPOSES with the ring / shadow
utilities: the inner highlight rides --tw-inset-shadow (kept by
`hover:shadow-xl`, rings, drag), the drop rides --tw-shadow (which
`hover:shadow-xl` / `shadow-2xl` swap for a bigger lift). Works on any
background colour (the tiles' tone colours), so it always matches the theme. */
@layer components {
.raised {
--tw-inset-shadow: inset 0 1px 0 rgb(255 255 255 / 0.45), inset 0 -1px 0 rgb(0 0 0 / 0.25);
--tw-shadow: 0 2px 9px 0 rgb(0 0 0 / 0.7);
box-shadow:
var(--tw-inset-shadow), var(--tw-inset-ring-shadow, 0 0 #0000),
var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
background-image: linear-gradient(to bottom, rgb(255 255 255 / 0.14), rgb(0 0 0 / 0.14));
}
}
/* Rendered-Markdown (assistant messages) typography — scoped to .markdown-body
so it never leaks into the rest of the app. */
.markdown-body {
& p {
margin-block: 0.5em;
&:first-child {
margin-block-start: 0;
}
&:last-child {
margin-block-end: 0;
}
}
& h1,
& h2,
& h3,
& h4,
& h5,
& h6 {
font-weight: 600;
line-height: 1.25;
margin-block: 0.75em 0.25em;
&:first-child {
margin-block-start: 0;
}
}
& h1 {
font-size: 1.4em;
}
& h2 {
font-size: 1.2em;
}
& h3 {
font-size: 1.1em;
}
& ul,
& ol {
padding-inline-start: 1.5em;
margin-block: 0.5em;
}
& ul {
list-style-type: disc;
}
& ol {
list-style-type: decimal;
}
& li {
margin-block: 0.15em;
}
& pre {
overflow-x: auto;
border-radius: var(--radius-box);
margin-block: 0.5em;
}
& pre code {
display: block;
padding: 0.75em 1em;
font-size: 0.8125em;
line-height: 1.5;
}
& :not(pre) > code {
font-size: 0.875em;
padding: 0.15em 0.4em;
border-radius: var(--radius-selector);
background-color: oklch(var(--color-base-content) / 0.1);
}
& blockquote {
border-inline-start: 3px solid oklch(var(--color-base-content) / 0.2);
padding-inline-start: 0.75em;
margin-block: 0.5em;
opacity: 0.8;
}
& a {
color: oklch(var(--color-primary));
text-decoration: underline;
&:hover {
opacity: 0.8;
}
}
& strong {
font-weight: 600;
}
& table {
width: 100%;
border-collapse: collapse;
margin-block: 0.5em;
font-size: 0.875em;
}
& th,
& td {
border: 1px solid oklch(var(--color-base-content) / 0.15);
padding: 0.4em 0.75em;
text-align: start;
}
& th {
font-weight: 600;
background-color: oklch(var(--color-base-200));
}
& hr {
border: none;
border-top: 1px solid oklch(var(--color-base-content) / 0.2);
margin-block: 0.75em;
}
}
/* App shell fills the viewport and never scrolls/overflows at the page level —
the inner regions (tab strip, chat transcript) own their own scrolling. */
html,
body,
#app {
height: 100%;
}
body {
overflow: hidden;
}
/* Transcript rows: skip layout + paint for rows scrolled out of view. A long
turn has hundreds of rows; without this every one is laid out on first
paint (and on every resize). `contain-intrinsic-size: auto <h>` makes the
browser remember each row's last rendered height, so scrolling back up
doesn't jump; the 64px is only the estimate for never-rendered rows. */
.chat-row {
content-visibility: auto;
contain-intrinsic-size: auto 64px;
}
|