@terminus-ai/cli 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1055 -0
  3. package/bin/agent-discovery.mjs +71 -0
  4. package/bin/agent-icon.mjs +77 -0
  5. package/bin/agent-models.mjs +77 -0
  6. package/bin/agent-type.mjs +51 -0
  7. package/bin/agentdev.mjs +657 -0
  8. package/bin/app-route-script.mjs +59 -0
  9. package/bin/app-runtime-contract.mjs +2 -0
  10. package/bin/appdev-remote.mjs +346 -0
  11. package/bin/appdev.mjs +4446 -0
  12. package/bin/apps.mjs +5512 -0
  13. package/bin/capability-calls.mjs +437 -0
  14. package/bin/capsule-data.mjs +260 -0
  15. package/bin/client.mjs +189 -0
  16. package/bin/commands.mjs +1194 -0
  17. package/bin/dev-capsules.mjs +1599 -0
  18. package/bin/dev-contract.mjs +262 -0
  19. package/bin/dev-data.mjs +287 -0
  20. package/bin/dev-members.mjs +18 -0
  21. package/bin/dev-net.mjs +316 -0
  22. package/bin/dev-notification-popup.mjs +628 -0
  23. package/bin/dev-ports.mjs +567 -0
  24. package/bin/dev-server-binding.mjs +35 -0
  25. package/bin/dev-server-ops.mjs +1086 -0
  26. package/bin/dev-ui/IoskeleyMono-400.woff2 +0 -0
  27. package/bin/dev-ui/IoskeleyMono-600.woff2 +0 -0
  28. package/bin/dev-ui/OFL.txt +92 -0
  29. package/bin/dev-ui/agent-robot.webp +0 -0
  30. package/bin/dev-ui/app.js +5217 -0
  31. package/bin/dev-ui/highlight.js +195 -0
  32. package/bin/dev-ui/index.html +34 -0
  33. package/bin/dev-ui/style.css +3640 -0
  34. package/bin/devlint.mjs +112 -0
  35. package/bin/devserver.mjs +2127 -0
  36. package/bin/devtriggers.mjs +367 -0
  37. package/bin/endpoints.mjs +156 -0
  38. package/bin/errors.mjs +61 -0
  39. package/bin/files.mjs +169 -0
  40. package/bin/horizontal-capabilities/v1/contract.json +280 -0
  41. package/bin/http.mjs +500 -0
  42. package/bin/lint-manifests/justbash-commands.json +88 -0
  43. package/bin/lint-manifests/python-stdlib.json +295 -0
  44. package/bin/login-page.mjs +488 -0
  45. package/bin/schedules.mjs +664 -0
  46. package/bin/server-sandbox.mjs +204 -0
  47. package/bin/servicedev.mjs +425 -0
  48. package/bin/sync.mjs +357 -0
  49. package/bin/terminus.js +3666 -0
  50. package/bin/toolchain.mjs +125 -0
  51. package/bin/vendor/app-runtime-v1/app-host.json +124 -0
  52. package/bin/vendor/app-runtime-v1/capability-calls.json +412 -0
  53. package/bin/vendor/app-runtime-v1/doors.json +2867 -0
  54. package/bin/vendor/appd/node-harness.mjs +209 -0
  55. package/bin/vendor/appd/python-harness.py +12 -0
  56. package/bin/vendor/appd/server-protocol.json +84 -0
  57. package/bin/vendor/where.mjs +541 -0
  58. package/bin/versioning.mjs +72 -0
  59. package/bin/write-rules.mjs +398 -0
  60. package/package.json +41 -0
@@ -0,0 +1,3640 @@
1
+ /**
2
+ * terminus dev — the product's own materials, in a browser tab.
3
+ *
4
+ * Round five, second pass. The first pass floated everything — three wells
5
+ * on the water, a lifted composer, glass capsules in the top bar — and a page
6
+ * of objects hovering over a page was too much. So the panes are CONNECTED:
7
+ * one chrome surface (the top bar and both sidebars, a light veil over the
8
+ * landing's water, held still) frames the paper the work happens on, and a
9
+ * hairline — never a gap, a shadow or a rounded panel — tells them apart.
10
+ * What still comes from the product is everything you touch and read:
11
+ *
12
+ * - The CONTROLS: gel candy for what you press (`.aqua-gel`), the Aqua
13
+ * segmented switch, the Switch's double bounce, status orbs.
14
+ * - The TYPE: EB Garamond where something is NAMED (the agent, the sections,
15
+ * a reply's headings), Inter at working size, IoskeleyMono for code and ids.
16
+ * - The settings ROW GRAMMAR: one line per setting, hairlines, nothing under.
17
+ *
18
+ * Only what genuinely sits above the page floats: menus, the toast, and the
19
+ * chip that jumps to the latest message. There is no window chrome either —
20
+ * the page owns its tab, and a titlebar would draw a window inside a real one.
21
+ *
22
+ * COLOUR IS STILL ONE INK. Text, fills and lines are `color-mix` of `--ink` at
23
+ * fixed alphas, and the materials are tokens beside them, so dark mode swaps
24
+ * tokens and the water and touches nothing else.
25
+ */
26
+
27
+ @font-face {
28
+ font-family: "IoskeleyMono";
29
+ src: url("/assets/IoskeleyMono-400.woff2") format("woff2");
30
+ font-weight: 400;
31
+ font-display: swap;
32
+ }
33
+ @font-face {
34
+ font-family: "IoskeleyMono";
35
+ src: url("/assets/IoskeleyMono-600.woff2") format("woff2");
36
+ font-weight: 600;
37
+ font-display: swap;
38
+ }
39
+
40
+ /* ------------------------------------------------------------------------- */
41
+ /* Tokens */
42
+ /* ------------------------------------------------------------------------- */
43
+
44
+ :root {
45
+ color-scheme: light;
46
+
47
+ /* The one ink, the house accent, and the glass tint (--glass-tint's raw
48
+ channels, so alpha can vary per state). */
49
+ --ink: #15171c;
50
+ --accent: #2d68d8;
51
+ --glass-tint: 29 78 168;
52
+ --danger: #cf3626;
53
+ --success: #2f9e3c;
54
+ --warn: #c4700f;
55
+
56
+ --text-1: color-mix(in srgb, var(--ink) 100%, transparent);
57
+ --text-2: color-mix(in srgb, var(--ink) 74%, transparent);
58
+ /* Values and annotations sit on glass as well as paper, so the quiet tiers
59
+ are held where 13px still clears 4.5:1 (text-3) and 3:1 (text-4). */
60
+ --text-3: color-mix(in srgb, var(--ink) 61%, transparent);
61
+ --text-4: color-mix(in srgb, var(--ink) 45%, transparent);
62
+
63
+ --bg-1: color-mix(in srgb, var(--ink) 12%, transparent);
64
+ --bg-2: color-mix(in srgb, var(--ink) 8%, transparent);
65
+ --bg-3: color-mix(in srgb, var(--ink) 5%, transparent);
66
+ --bg-4: color-mix(in srgb, var(--ink) 3%, transparent);
67
+
68
+ --line-1: color-mix(in srgb, var(--ink) 14%, transparent);
69
+ --line-2: color-mix(in srgb, var(--ink) 9%, transparent);
70
+ --line-3: color-mix(in srgb, var(--ink) 6%, transparent);
71
+
72
+ --accent-soft: color-mix(in srgb, var(--accent) 12%, transparent);
73
+ --accent-line: color-mix(in srgb, var(--accent) 45%, transparent);
74
+ --accent-text: color-mix(in srgb, var(--accent) 86%, var(--ink));
75
+ --on-accent: #ffffff;
76
+ /* The spinner glyph (ActivityTrace.tsx geometry) strokes with the
77
+ product's own token names. */
78
+ --brand: var(--accent);
79
+ --border-edge: var(--line-1);
80
+ --ring: 0 0 0 3px color-mix(in srgb, var(--accent) 26%, transparent);
81
+
82
+ /* The selection: menus, the Finder's places, the admin sidebar. */
83
+ --select: linear-gradient(
84
+ 180deg,
85
+ color-mix(in srgb, var(--accent) 72%, #ffffff) 0%,
86
+ var(--accent) 100%
87
+ );
88
+
89
+ /* Materials: two surfaces and a line. CHROME is the top bar and both
90
+ sidebars — one light veil over the water, so the product's ground still
91
+ tints the frame; PAPER is opaque, where the conversation and files are
92
+ read; the RULE between them is a hairline. Objects on paper (cards,
93
+ chips) are drawn by an edge, not lifted by a shadow. */
94
+ --chrome: rgb(255 255 255 / 0.74);
95
+ --chrome-cover: #f4f8fb;
96
+ --paper: #ffffff;
97
+ --rule: var(--line-2);
98
+ --card-fill: #ffffff;
99
+ --card-edge: inset 0 0 0 1px var(--line-2);
100
+ --pop-fill: rgb(252 253 255 / 0.97);
101
+ --pop-throw: 0 0 0 0.5px rgb(8 28 74 / 0.16), 0 12px 32px -14px rgb(8 28 74 / 0.32);
102
+ --field-fill: rgb(8 28 74 / 0.05);
103
+ --field-fill-focus: #ffffff;
104
+ --well-fill: rgb(255 255 255 / 0.6);
105
+ --well-rim: rgb(0 0 0 / 0.18);
106
+ --well-shade: inset 0 1.5px 3px rgb(0 10 40 / 0.14);
107
+ --dock-line: rgb(0 10 40 / 0.1);
108
+ --dock-shine: rgb(255 255 255 / 0.5);
109
+ --you-fill: color-mix(in srgb, var(--accent) 34%, rgb(255 255 255 / 0.5));
110
+ --you-rim: color-mix(in srgb, var(--accent) 45%, rgb(0 10 40 / 0.1));
111
+ --you-shine: inset 0 1px 0 rgb(255 255 255 / 0.45), 0 1px 2px rgb(0 10 40 / 0.1);
112
+ --code-well: rgb(12 30 66 / 0.06);
113
+ --code-well-rim: rgb(0 10 40 / 0.09);
114
+ --glass-card: rgb(255 255 255 / 0.32);
115
+ --glass-card-rim: rgb(0 10 40 / 0.12);
116
+ --row-hover: rgb(0 0 0 / 0.045);
117
+ --chip-fill: color-mix(in srgb, var(--accent) 18%, #ffffff);
118
+ --chip-ink: color-mix(in srgb, var(--accent) 55%, #0a1c40);
119
+ --window-glass: color-mix(in srgb, var(--accent) 8%, rgb(243 246 251 / 0.86));
120
+ --window-rim: rgb(0 0 0 / 0.22);
121
+ --rail-fill: color-mix(in srgb, var(--accent) 9%, rgb(8 20 48 / 0.055));
122
+ /* A window's body is two WELLS recessed into its glass with a seam of pane
123
+ between them (globals.css `.settings-well` / `.settings-sheet`): a
124
+ hairline rim, inset shading, and a light specular under-edge. The rail is
125
+ barer glass — it is chrome; the sheet is near-white, because content
126
+ wants paper under it, not sky. */
127
+ --well-recess: rgb(255 255 255 / 0.4);
128
+ --well-recess-rim: rgb(0 0 0 / 0.25);
129
+ --well-recess-shade: inset 0 1px 2px rgb(0 0 0 / 0.08), 0 1px 0 rgb(255 255 255 / 0.25);
130
+ --sheet-fill: rgb(244 246 249 / 0.85);
131
+ --sheet-rule: rgb(0 0 0 / 0.08);
132
+ --etch: 0 1px 0 rgb(255 255 255 / 0.6);
133
+ --track: linear-gradient(180deg, rgb(8 18 42 / 0.16), rgb(8 18 42 / 0.08));
134
+ --track-shade: inset 0 1px 2px rgb(0 10 40 / 0.2), inset 0 0 0 0.5px rgb(0 0 0 / 0.12),
135
+ 0 1px 0 rgb(255 255 255 / 0.55);
136
+
137
+ /* The gel: `.aqua-gel`'s candy, poured in the accent. The @supports block
138
+ below replaces the fallback stops with the product's own transform. */
139
+ --gel-crown: var(--accent);
140
+ --gel-foot: color-mix(in srgb, var(--accent) 18%, #ffffff);
141
+ --gel-rim-crown: color-mix(in srgb, var(--accent) 62%, #06122c);
142
+ --gel-rim-foot: hsl(0 0% 38%);
143
+ --gel-label: color-mix(in srgb, var(--accent) 34%, #050b18);
144
+
145
+ /* Code is dark on every surface (MarkdownPreview's rule). */
146
+ --code-ground: #0c0e13;
147
+ --code-rim: #1d2129;
148
+ --code-ink: #e4e7ee;
149
+
150
+ /* The register: 14 is read, 13 is worked in, 12 navigates, 11 annotates. */
151
+ --text-read: 14px;
152
+ --text-base: 13px;
153
+ --text-sm: 12px;
154
+ --text-xs: 11px;
155
+
156
+ --radius-well: 16px;
157
+ --radius-card: 12px;
158
+ --radius-row: 8px;
159
+ --radius-small: 6px;
160
+
161
+ --h-xs: 22px;
162
+ --h-sm: 26px;
163
+ --h-base: 30px;
164
+ --h-lg: 34px;
165
+
166
+ --fast: 120ms;
167
+ --normal: 200ms;
168
+ --ease: cubic-bezier(0.2, 0.8, 0.2, 1);
169
+ --ease-out-strong: cubic-bezier(0.23, 1, 0.32, 1);
170
+
171
+ --font-ui: Inter, system-ui, -apple-system, "Segoe UI", sans-serif;
172
+ /* The OS layer's face. Aqua ran on Lucida Grande; the product ships Noto
173
+ Sans as the licence-clean stand-in (lib/fonts.ts measured it against the
174
+ real thing), and every piece of OS chrome over there wears it. The
175
+ workbench keeps Inter — the difference is letterform SHAPE, open humanist
176
+ forms against Inter's mechanical rhythm, and it is most of why a window
177
+ in Inter does not read as an OS window. */
178
+ --font-os: "Noto Sans", Inter, system-ui, sans-serif;
179
+ --font-brand: "EB Garamond", Georgia, "Times New Roman", serif;
180
+ --font-code: "IoskeleyMono", ui-monospace, "SFMono-Regular", "SF Mono", Menlo,
181
+ Consolas, "Liberation Mono", monospace;
182
+
183
+ --topbar-h: 52px;
184
+ /* A file list is names, not paragraphs: the column opens narrow, and both
185
+ seams can be dragged (the widths are remembered in this browser). */
186
+ --sidebar-w: 220px;
187
+ --inspector-w: clamp(328px, 25vw, 372px);
188
+ --measure: 720px;
189
+ /* The conversation's column: Norbert's window is 560px of glass, and its
190
+ line length is what this page's chat should read at too. */
191
+ --chat-measure: 600px;
192
+ --gutter: max(18px, min(32px, 3vw));
193
+ }
194
+
195
+ @supports (color: hsl(from white h s l)) {
196
+ :root {
197
+ --gel-foot: hsl(from var(--gel-crown) calc(h - 32) 95% 92%);
198
+ --gel-rim-crown: hsl(from var(--gel-crown) calc(h + 27) 74% 30%);
199
+ --gel-label: hsl(from var(--gel-crown) h 85% 14%);
200
+ }
201
+ }
202
+
203
+ /* Dark is a CHOICE (the moon in the top bar), never the OS's call: the page
204
+ opens light, and the choice is remembered in this browser. Night water under
205
+ a smoked frame, dark paper, the same ink ladders lifted — light ink reads
206
+ heavier than dark ink at the same alpha, so fills and lines climb. */
207
+ :root[data-theme="dark"] {
208
+ color-scheme: dark;
209
+ --ink: #e7e9ef;
210
+ --accent: #3b76e2;
211
+ --danger: #f0697a;
212
+ --success: #4cc27a;
213
+ --warn: #e9a247;
214
+
215
+ --bg-1: color-mix(in srgb, var(--ink) 16%, transparent);
216
+ --bg-2: color-mix(in srgb, var(--ink) 10%, transparent);
217
+ --bg-3: color-mix(in srgb, var(--ink) 7%, transparent);
218
+ --bg-4: color-mix(in srgb, var(--ink) 4%, transparent);
219
+ --line-1: color-mix(in srgb, var(--ink) 18%, transparent);
220
+ --line-2: color-mix(in srgb, var(--ink) 11%, transparent);
221
+ --line-3: color-mix(in srgb, var(--ink) 7%, transparent);
222
+
223
+ --accent-line: color-mix(in srgb, #7aa9ff 55%, transparent);
224
+ --accent-text: #8cb4ff;
225
+ --ring: 0 0 0 3px color-mix(in srgb, #7aa9ff 30%, transparent);
226
+
227
+ --chrome: rgb(14 18 26 / 0.74);
228
+ --chrome-cover: #141922;
229
+ --paper: #111419;
230
+ --card-fill: rgb(255 255 255 / 0.03);
231
+ --pop-fill: rgb(26 30 39 / 0.98);
232
+ --pop-throw: 0 0 0 0.5px rgb(255 255 255 / 0.12), 0 12px 32px -14px rgb(0 0 0 / 0.75);
233
+ --field-fill: rgb(255 255 255 / 0.06);
234
+ --field-fill-focus: rgb(255 255 255 / 0.1);
235
+ --well-fill: rgb(255 255 255 / 0.06);
236
+ --well-rim: rgb(255 255 255 / 0.12);
237
+ --well-shade: inset 0 1.5px 3px rgb(0 0 0 / 0.35);
238
+ --dock-line: rgb(255 255 255 / 0.08);
239
+ --dock-shine: rgb(255 255 255 / 0.04);
240
+ --you-fill: color-mix(in srgb, var(--accent) 42%, rgb(255 255 255 / 0.04));
241
+ --you-rim: color-mix(in srgb, var(--accent) 60%, rgb(255 255 255 / 0.08));
242
+ --you-shine: inset 0 1px 0 rgb(255 255 255 / 0.1), 0 1px 2px rgb(0 0 0 / 0.35);
243
+ --code-well: rgb(255 255 255 / 0.05);
244
+ --code-well-rim: rgb(255 255 255 / 0.08);
245
+ --glass-card: rgb(255 255 255 / 0.05);
246
+ --glass-card-rim: rgb(255 255 255 / 0.1);
247
+ --row-hover: rgb(255 255 255 / 0.06);
248
+ --chip-fill: color-mix(in srgb, var(--accent) 30%, rgb(255 255 255 / 0.06));
249
+ --chip-ink: color-mix(in srgb, var(--accent) 25%, #ffffff);
250
+ --window-glass: color-mix(in srgb, var(--accent) 10%, rgb(18 22 30 / 0.9));
251
+ --window-rim: rgb(255 255 255 / 0.14);
252
+ --rail-fill: color-mix(in srgb, var(--accent) 12%, rgb(255 255 255 / 0.04));
253
+ --well-recess: rgb(255 255 255 / 0.05);
254
+ --well-recess-rim: rgb(0 0 0 / 0.4);
255
+ --well-recess-shade: inset 0 1px 2px rgb(0 0 0 / 0.35), 0 1px 0 rgb(255 255 255 / 0.05);
256
+ --sheet-fill: rgb(20 24 32 / 0.8);
257
+ --sheet-rule: rgb(255 255 255 / 0.07);
258
+ --etch: none;
259
+ --track: linear-gradient(180deg, rgb(0 0 0 / 0.42), rgb(0 0 0 / 0.28));
260
+ --track-shade: inset 0 1px 2px rgb(0 0 0 / 0.45), inset 0 0 0 0.5px rgb(255 255 255 / 0.08),
261
+ 0 1px 0 rgb(255 255 255 / 0.05);
262
+
263
+ --code-ground: #090b0f;
264
+ --code-rim: #232833;
265
+ }
266
+
267
+ /* ------------------------------------------------------------------------- */
268
+ /* Reset */
269
+ /* ------------------------------------------------------------------------- */
270
+
271
+ * { box-sizing: border-box; }
272
+
273
+ html, body { height: 100%; }
274
+
275
+ body {
276
+ margin: 0;
277
+ background: #edf6fc;
278
+ color: var(--text-1);
279
+ font-family: var(--font-ui);
280
+ font-size: var(--text-base);
281
+ line-height: 1.5;
282
+ -webkit-font-smoothing: antialiased;
283
+ text-rendering: optimizeLegibility;
284
+ }
285
+ :root[data-theme="dark"] body { background: #0b111c; }
286
+
287
+ button, input, textarea, select { font: inherit; color: inherit; }
288
+
289
+ button {
290
+ margin: 0;
291
+ padding: 0;
292
+ border: 0;
293
+ background: none;
294
+ cursor: pointer;
295
+ color: inherit;
296
+ }
297
+
298
+ button:disabled { cursor: default; }
299
+
300
+ a { color: var(--accent-text); }
301
+
302
+ :focus-visible {
303
+ outline: 2px solid var(--accent-line);
304
+ outline-offset: 2px;
305
+ }
306
+
307
+ ::selection { background: color-mix(in srgb, var(--accent) 22%, transparent); }
308
+
309
+ .mono {
310
+ font-family: var(--font-code);
311
+ font-variant-numeric: tabular-nums;
312
+ }
313
+
314
+ .spacer { flex: 1; }
315
+
316
+ /* One scrollbar everywhere: thin, ink-alpha, invisible until the surface is
317
+ hovered. */
318
+ .studio-scroll { overflow: auto; scrollbar-width: thin; scrollbar-color: transparent transparent; }
319
+ .studio-scroll:hover { scrollbar-color: var(--bg-1) transparent; }
320
+ .studio-scroll::-webkit-scrollbar { width: 10px; height: 10px; }
321
+ .studio-scroll::-webkit-scrollbar-thumb {
322
+ background: transparent;
323
+ border: 3px solid transparent;
324
+ border-radius: 999px;
325
+ background-clip: content-box;
326
+ }
327
+ .studio-scroll:hover::-webkit-scrollbar-thumb { background: var(--bg-1); background-clip: content-box; }
328
+ .studio-scroll::-webkit-scrollbar-corner { background: transparent; }
329
+
330
+ /* ------------------------------------------------------------------------- */
331
+ /* The ground: the landing's water, held still */
332
+ /* ------------------------------------------------------------------------- */
333
+
334
+ /* Layer for layer WaterBackground.tsx — basin, glow, two currents, two
335
+ ribbons, grain — with the drift removed: the currents sit where the
336
+ landing's phone breakpoint parks them. */
337
+ .water {
338
+ position: fixed;
339
+ inset: 0;
340
+ z-index: 0;
341
+ overflow: hidden;
342
+ pointer-events: none;
343
+ }
344
+ .water > div { position: absolute; }
345
+
346
+ .water-basin {
347
+ inset: 0;
348
+ background: linear-gradient(180deg, #fbfdff 0%, #edf8ff 38%, #e8f4fb 66%, #fbfdff 100%);
349
+ }
350
+
351
+ .water-glow {
352
+ left: 50%;
353
+ top: 52%;
354
+ width: 84rem;
355
+ height: 46rem;
356
+ translate: -50% -50%;
357
+ border-radius: 50%;
358
+ background: radial-gradient(
359
+ closest-side,
360
+ rgb(147 210 248 / 0.5),
361
+ rgb(186 230 253 / 0.3) 52%,
362
+ transparent 76%
363
+ );
364
+ filter: blur(40px);
365
+ }
366
+
367
+ .water-current {
368
+ top: -24%;
369
+ bottom: -24%;
370
+ left: -50%;
371
+ width: 300%;
372
+ background-repeat: repeat-x;
373
+ background-size: 25% 100%;
374
+ filter: blur(44px);
375
+ -webkit-mask-image: linear-gradient(180deg, transparent 3%, black 24%, black 78%, transparent 97%);
376
+ mask-image: linear-gradient(180deg, transparent 3%, black 24%, black 78%, transparent 97%);
377
+ }
378
+
379
+ .water-current-a {
380
+ background-image:
381
+ radial-gradient(ellipse 38% 8% at 8% 35%, rgb(224 242 254 / 0.82), transparent 74%),
382
+ radial-gradient(ellipse 44% 10% at 43% 54%, rgb(125 211 252 / 0.52), transparent 76%),
383
+ radial-gradient(ellipse 31% 7% at 72% 31%, rgb(255 255 255 / 0.68), transparent 74%),
384
+ radial-gradient(ellipse 48% 11% at 96% 68%, rgb(96 165 250 / 0.3), transparent 78%);
385
+ transform: translate3d(-8%, 1.5%, 0) rotate(-2deg);
386
+ }
387
+
388
+ .water-current-b {
389
+ background-image:
390
+ radial-gradient(ellipse 42% 9% at 16% 70%, rgb(186 230 253 / 0.7), transparent 76%),
391
+ radial-gradient(ellipse 29% 7% at 45% 38%, rgb(147 197 253 / 0.42), transparent 74%),
392
+ radial-gradient(ellipse 46% 9% at 77% 61%, rgb(224 242 254 / 0.84), transparent 78%),
393
+ radial-gradient(ellipse 33% 8% at 98% 43%, rgb(125 211 252 / 0.34), transparent 75%);
394
+ transform: translate3d(-16%, -1.5%, 0) rotate(2deg);
395
+ }
396
+
397
+ .water-ribbon {
398
+ left: -50%;
399
+ width: 300%;
400
+ background-repeat: repeat-x;
401
+ background-size: 25% 100%;
402
+ border-radius: 50%;
403
+ filter: blur(20px);
404
+ opacity: 0.5;
405
+ }
406
+
407
+ .water-ribbon-a {
408
+ top: 25%;
409
+ height: 10rem;
410
+ background-image: linear-gradient(
411
+ 90deg,
412
+ transparent 0%,
413
+ rgb(255 255 255 / 0.04) 8%,
414
+ rgb(255 255 255 / 0.68) 27%,
415
+ rgb(125 211 252 / 0.12) 49%,
416
+ rgb(255 255 255 / 0.56) 73%,
417
+ transparent 100%
418
+ );
419
+ transform: rotate(-4deg) translate3d(-6%, 1rem, 0);
420
+ }
421
+
422
+ .water-ribbon-b {
423
+ top: 64%;
424
+ height: 8rem;
425
+ background-image: linear-gradient(
426
+ 90deg,
427
+ transparent 0%,
428
+ rgb(125 211 252 / 0.06) 12%,
429
+ rgb(255 255 255 / 0.58) 35%,
430
+ rgb(96 165 250 / 0.11) 58%,
431
+ rgb(255 255 255 / 0.5) 79%,
432
+ transparent 100%
433
+ );
434
+ transform: rotate(3deg) translate3d(-12%, -0.75rem, 0);
435
+ }
436
+
437
+ .water-grain {
438
+ inset: 0;
439
+ opacity: 0.1;
440
+ mix-blend-mode: soft-light;
441
+ background-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='200'%20height='200'%3E%3Cfilter%20id='n'%3E%3CfeTurbulence%20type='fractalNoise'%20baseFrequency='0.9'%20numOctaves='2'%20stitchTiles='stitch'/%3E%3C/filter%3E%3Crect%20width='200'%20height='200'%20filter='url(%23n)'%20opacity='0.55'/%3E%3C/svg%3E");
442
+ }
443
+
444
+ /* Night water: the same basin and glow in deep blues; the white currents
445
+ would read as fog on a dark ground, so they dim to a trace. */
446
+ :root[data-theme="dark"] .water-basin {
447
+ background: linear-gradient(180deg, #0a0f18 0%, #0c1524 40%, #0b1320 68%, #090d15 100%);
448
+ }
449
+ :root[data-theme="dark"] .water-glow {
450
+ background: radial-gradient(
451
+ closest-side,
452
+ rgb(38 92 168 / 0.34),
453
+ rgb(28 64 124 / 0.18) 52%,
454
+ transparent 76%
455
+ );
456
+ }
457
+ :root[data-theme="dark"] .water-current { opacity: 0.1; }
458
+ :root[data-theme="dark"] .water-ribbon { opacity: 0.06; }
459
+ :root[data-theme="dark"] .water-grain { opacity: 0.05; }
460
+
461
+ /* ------------------------------------------------------------------------- */
462
+ /* Shared materials */
463
+ /* ------------------------------------------------------------------------- */
464
+
465
+ /* The gel — `.aqua-gel`'s six-layer candy (Kevin Powell's reconstruction, as
466
+ the product ports it): a specular cap, a translucent band with two rounded
467
+ ends, the crown falling to a near-white foot, and a rim that fades dark to
468
+ grey. Worn by everything you PRESS to act: send, stop, the switch's thumb,
469
+ Save, a decision. */
470
+ .gel,
471
+ .send-btn,
472
+ .seg-switch .seg-thumb {
473
+ border: 1px solid transparent;
474
+ background-image:
475
+ linear-gradient(rgb(255 255 255 / 0.85), transparent 10px),
476
+ linear-gradient(rgb(255 255 255 / 0.25) 10px, transparent 10px),
477
+ radial-gradient(circle at 10px 5px, rgb(255 255 255 / 0.25) 5px, transparent 5px),
478
+ radial-gradient(circle at 0px 5px, rgb(255 255 255 / 0.25) 5px, transparent 5px),
479
+ linear-gradient(var(--gel-crown), var(--gel-foot)),
480
+ linear-gradient(var(--gel-rim-crown), var(--gel-rim-foot));
481
+ background-size: 100%, calc(100% - 20px), 10px 10px, 10px 10px, 100%, 100%;
482
+ background-position: top, top center, top left, top right, center, center;
483
+ background-repeat: no-repeat;
484
+ background-origin: padding-box, padding-box, padding-box, padding-box, padding-box, border-box;
485
+ background-clip: padding-box, padding-box, padding-box, padding-box, padding-box, border-box;
486
+ color: var(--gel-label);
487
+ }
488
+
489
+ .gel {
490
+ display: inline-flex;
491
+ align-items: center;
492
+ justify-content: center;
493
+ gap: 6px;
494
+ height: var(--h-sm);
495
+ padding: 0 13px;
496
+ border-radius: 999px;
497
+ font-size: var(--text-sm);
498
+ font-weight: 600;
499
+ white-space: nowrap;
500
+ box-shadow: 0 1px 2px rgb(10 35 90 / 0.28);
501
+ transition: filter 220ms var(--ease), transform 220ms var(--ease);
502
+ }
503
+ .gel:hover:not(:disabled) { filter: brightness(1.05); }
504
+ .gel:active:not(:disabled) { filter: brightness(0.95); transform: scale(0.97); }
505
+ .gel:disabled { filter: saturate(0.25) brightness(1.03); box-shadow: none; opacity: 0.6; }
506
+ .gel:focus-visible { outline: none; box-shadow: 0 1px 2px rgb(10 35 90 / 0.28), var(--ring); }
507
+
508
+ /* A status orb — the house way of saying a state beside the thing it belongs
509
+ to (StatusOrb.tsx). The state's name rides as the label, never as a pill. */
510
+ .orb {
511
+ display: inline-block;
512
+ width: 11px;
513
+ height: 11px;
514
+ flex: none;
515
+ border-radius: 999px;
516
+ background:
517
+ radial-gradient(circle at 35% 28%, rgb(255 255 255 / 0.95), rgb(255 255 255 / 0) 45%),
518
+ linear-gradient(180deg, var(--orb-crown), var(--orb-foot));
519
+ box-shadow: inset 0 0 0 1px var(--orb-rim), inset 0 -2px 3px rgb(255 255 255 / 0.35),
520
+ 0 1px 1px rgb(0 0 0 / 0.2);
521
+ --orb-crown: #e2e2e2;
522
+ --orb-foot: #9c9ca1;
523
+ --orb-rim: rgb(0 0 0 / 0.35);
524
+ }
525
+ .orb.ok { --orb-crown: #8ddc78; --orb-foot: #2f9e3c; --orb-rim: rgb(20 90 30 / 0.55); }
526
+ .orb.warn { --orb-crown: #ffd479; --orb-foot: #e08e18; --orb-rim: rgb(130 80 0 / 0.5); }
527
+ .orb.danger { --orb-crown: #ff9d94; --orb-foot: #d3352b; --orb-rim: rgb(120 15 10 / 0.55); }
528
+
529
+ /* ------------------------------------------------------------------------- */
530
+ /* Shell — one chrome frame around the paper */
531
+ /* ------------------------------------------------------------------------- */
532
+
533
+ .app {
534
+ position: relative;
535
+ z-index: 1;
536
+ display: grid;
537
+ grid-template-rows: var(--topbar-h) minmax(0, 1fr);
538
+ /* One column that cannot be widened by what is inside it: without this the
539
+ implicit track takes the min-content of a long code line and the page
540
+ scrolls sideways on a narrow window. */
541
+ grid-template-columns: minmax(0, 1fr);
542
+ /* Two declarations, deliberately: an unsupported unit invalidates the whole
543
+ declaration, so a lone 100dvh would leave height:auto in an engine
544
+ without dvh. The percentage is the floor; dvh refines it. */
545
+ height: 100%;
546
+ height: 100dvh;
547
+ overflow: hidden;
548
+ /* The top bar and both sidebars are ONE surface: the veil is painted once,
549
+ here, so they cannot disagree about the water behind them. */
550
+ background: var(--chrome);
551
+ }
552
+
553
+ .workbench {
554
+ display: flex;
555
+ min-width: 0;
556
+ min-height: 0;
557
+ }
558
+
559
+ /* A sidebar is the frame itself, attached to the paper by a hairline. It
560
+ collapses by animating WIDTH on this outer box while the body inside stays
561
+ absolutely sized, so contents never reflow on the way out. */
562
+ .lane-col {
563
+ position: relative;
564
+ flex: none;
565
+ overflow: hidden;
566
+ transition: width var(--normal) var(--ease-out-strong), border-color var(--normal) var(--ease);
567
+ }
568
+ .lane-col.left { width: var(--sidebar-w); border-right: 1px solid var(--rule); }
569
+ .lane-col.right { width: var(--inspector-w); border-left: 1px solid var(--rule); }
570
+ .lane-col.left[data-open="false"],
571
+ .lane-col.right[data-open="false"] { width: 0; border-color: transparent; }
572
+
573
+ .lane-body {
574
+ position: absolute;
575
+ inset: 0;
576
+ display: flex;
577
+ flex-direction: column;
578
+ min-height: 0;
579
+ }
580
+ /* The seam between a sidebar and the paper can be dragged. The handle is a
581
+ hit strip centred on the hairline; under the cursor, and while it is held,
582
+ the hairline becomes a 2px accent line. */
583
+ .lane-resize {
584
+ position: relative;
585
+ flex: none;
586
+ width: 0;
587
+ z-index: 25;
588
+ outline: none;
589
+ }
590
+ .lane-resize[hidden] { display: none; }
591
+ .lane-resize::before {
592
+ content: "";
593
+ position: absolute;
594
+ top: 0;
595
+ bottom: 0;
596
+ left: -4px;
597
+ width: 8px;
598
+ cursor: col-resize;
599
+ }
600
+ .lane-resize::after {
601
+ content: "";
602
+ position: absolute;
603
+ top: 0;
604
+ bottom: 0;
605
+ left: -1px;
606
+ width: 2px;
607
+ background: var(--accent);
608
+ opacity: 0;
609
+ pointer-events: none;
610
+ transition: opacity 120ms var(--ease);
611
+ }
612
+ .lane-resize:hover::after { opacity: 0.55; transition-delay: 150ms; }
613
+ .lane-resize[data-dragging]::after,
614
+ .lane-resize:focus-visible::after { opacity: 1; transition-delay: 0ms; }
615
+ /* While a seam is held the whole page resizes with it: no easing on the
616
+ columns, no text selection, and a page preview cannot swallow the pointer. */
617
+ body[data-resizing] { cursor: col-resize; user-select: none; }
618
+ body[data-resizing] .lane-col { transition: none; }
619
+ body[data-resizing] iframe { pointer-events: none; }
620
+
621
+ /* One pixel narrower than the column: the hairline is part of its width. */
622
+ .lane-col.left .lane-body { width: calc(var(--sidebar-w) - 1px); }
623
+ .lane-col.right .lane-body { width: calc(var(--inspector-w) - 1px); right: 0; left: auto; }
624
+
625
+ .lane-main {
626
+ flex: 1;
627
+ min-height: 0;
628
+ display: flex;
629
+ flex-direction: column;
630
+ }
631
+
632
+ /* The composer's model popover hangs off this, not off the scrolling plane. */
633
+ .pop-anchor { position: relative; }
634
+
635
+ /* ------------------------------------------------------------------------- */
636
+ /* Top bar */
637
+ /* ------------------------------------------------------------------------- */
638
+
639
+ .topbar {
640
+ display: flex;
641
+ align-items: center;
642
+ gap: 10px;
643
+ min-width: 0;
644
+ padding: 0 12px;
645
+ }
646
+
647
+ .topbar-lead {
648
+ display: flex;
649
+ align-items: center;
650
+ gap: 6px;
651
+ min-width: 0;
652
+ }
653
+
654
+ /* The agent's mark: artwork, a free-standing object with its own shadow, so
655
+ the button sizes off the robot's body rather than its alpha box. */
656
+ .agent-mark {
657
+ display: inline-flex;
658
+ align-items: center;
659
+ justify-content: center;
660
+ width: 36px;
661
+ height: 36px;
662
+ flex: none;
663
+ border-radius: 10px;
664
+ transition: background-color var(--fast) var(--ease), transform var(--fast) var(--ease);
665
+ }
666
+ .agent-mark:hover { background: var(--bg-3); }
667
+ .agent-mark:active { transform: scale(0.96); }
668
+ .agent-mark img {
669
+ display: block;
670
+ width: 30px;
671
+ height: 30px;
672
+ object-fit: contain;
673
+ }
674
+
675
+ /* The name is signed the way the product signs names: EB Garamond, semibold. */
676
+ .brand {
677
+ display: flex;
678
+ align-items: center;
679
+ gap: 9px;
680
+ min-width: 0;
681
+ padding-right: 4px;
682
+ }
683
+ .brand .name {
684
+ font-family: var(--font-brand);
685
+ font-size: 22px;
686
+ font-weight: 600;
687
+ line-height: 1;
688
+ letter-spacing: -0.012em;
689
+ color: var(--text-1);
690
+ white-space: nowrap;
691
+ overflow: hidden;
692
+ text-overflow: ellipsis;
693
+ padding-bottom: 1px;
694
+ }
695
+ /* The version is a small tag beside the name, set into the frame. */
696
+ .brand .version {
697
+ display: inline-flex;
698
+ align-items: center;
699
+ height: 20px;
700
+ padding: 0 8px;
701
+ flex: none;
702
+ border-radius: 999px;
703
+ background: var(--bg-3);
704
+ font-size: var(--text-xs);
705
+ font-weight: 500;
706
+ color: var(--text-2);
707
+ font-variant-numeric: tabular-nums;
708
+ white-space: nowrap;
709
+ }
710
+ .brand .version:empty { display: none; }
711
+
712
+ /* This session's spend: a fact about the work, read in passing. */
713
+ .spend-readout {
714
+ font-family: var(--font-code);
715
+ font-size: var(--text-xs);
716
+ color: var(--text-3);
717
+ white-space: nowrap;
718
+ font-variant-numeric: tabular-nums;
719
+ }
720
+ .spend-readout[hidden] { display: none; }
721
+
722
+ /* The view controls: two glyphs side by side, at the edge they act on. */
723
+ .toolbar-group {
724
+ display: inline-flex;
725
+ align-items: center;
726
+ gap: 2px;
727
+ flex: none;
728
+ }
729
+
730
+ /* A glyph button: round, quiet, and it only takes a body under the cursor. */
731
+ .chrome-btn {
732
+ display: inline-flex;
733
+ align-items: center;
734
+ justify-content: center;
735
+ width: 32px;
736
+ height: 32px;
737
+ flex: none;
738
+ border-radius: 999px;
739
+ color: var(--text-2);
740
+ text-decoration: none;
741
+ transition: background-color var(--fast) var(--ease), color var(--fast) var(--ease),
742
+ box-shadow var(--fast) var(--ease), transform var(--fast) var(--ease);
743
+ }
744
+ .chrome-btn:hover:not(:disabled) { background: var(--bg-3); color: var(--text-1); }
745
+ .chrome-btn:active:not(:disabled) { transform: scale(0.94); }
746
+ .chrome-btn:disabled { opacity: 0.45; }
747
+ .chrome-btn svg { width: 17px; height: 17px; }
748
+
749
+ .chrome-btn.sm { width: 28px; height: 28px; color: var(--text-3); }
750
+ .chrome-btn.sm svg { width: 15px; height: 15px; }
751
+
752
+ .chrome-btn.danger:hover:not(:disabled) {
753
+ color: var(--danger);
754
+ background: color-mix(in srgb, var(--danger) 9%, transparent);
755
+ }
756
+
757
+ /* Pressed is a quiet fill and the accent on the glyph — the panel it opens
758
+ is the rest of the answer. */
759
+ .toolbar-group .chrome-btn[aria-pressed="true"] {
760
+ background: var(--bg-2);
761
+ color: var(--accent-text);
762
+ }
763
+
764
+ /* ------------------------------------------------------------------------- */
765
+ /* Left well: the explorer */
766
+ /* ------------------------------------------------------------------------- */
767
+
768
+ .lane-head {
769
+ display: flex;
770
+ align-items: center;
771
+ gap: 8px;
772
+ padding: 10px 10px 6px;
773
+ flex: none;
774
+ }
775
+
776
+ /* Source | Workspace, Default | Observed, All | Selected: the product's Aqua
777
+ switch — a recessed track holding a gel thumb that SLIDES between the words
778
+ (the OS ⇄ Chat switch's anatomy). */
779
+ .seg-switch {
780
+ position: relative;
781
+ display: grid;
782
+ grid-auto-flow: column;
783
+ grid-auto-columns: 1fr;
784
+ min-width: 0;
785
+ height: 30px;
786
+ padding: 2px;
787
+ border-radius: 999px;
788
+ background: var(--track);
789
+ box-shadow: var(--track-shade);
790
+ }
791
+ .seg-switch .seg-thumb {
792
+ position: absolute;
793
+ top: 2px;
794
+ bottom: 2px;
795
+ left: 2px;
796
+ width: calc((100% - 4px) / var(--n, 2));
797
+ border-radius: 999px;
798
+ translate: calc(var(--i, 0) * 100%) 0;
799
+ box-shadow: 0 1px 2px rgb(10 35 90 / 0.4);
800
+ transition: translate 0.22s var(--ease-out-strong);
801
+ pointer-events: none;
802
+ }
803
+ .seg-switch button {
804
+ position: relative;
805
+ z-index: 1;
806
+ min-width: 0;
807
+ padding: 0 6px;
808
+ border-radius: 999px;
809
+ font-size: var(--text-sm);
810
+ font-weight: 500;
811
+ color: color-mix(in srgb, var(--ink) 66%, transparent);
812
+ white-space: nowrap;
813
+ overflow: hidden;
814
+ text-overflow: ellipsis;
815
+ transition: color var(--fast) var(--ease);
816
+ }
817
+ .seg-switch button:hover { color: var(--text-1); }
818
+ .seg-switch button[aria-pressed="true"] { color: var(--gel-label); }
819
+ /* Compact, on a section's title line beside the word it sets: the xs control
820
+ height, halves as wide as the longer word needs — and one shared floor, so
821
+ switches stacked on neighbouring title lines keep the same left edge
822
+ (Default | Observed, the widest pair, needs 147px). */
823
+ .seg-switch.compact { flex: none; min-width: 148px; height: var(--h-xs); }
824
+ .seg-switch.compact button { padding: 0 10px; font-size: var(--text-xs); }
825
+ .lane-switch { flex: 1; }
826
+
827
+ .field-wrap { padding: 2px 10px 6px; flex: none; }
828
+
829
+ /* The filter: a recessed capsule, found by its hollow rather than a border. */
830
+ .search-pill {
831
+ display: flex;
832
+ align-items: center;
833
+ gap: 7px;
834
+ height: 30px;
835
+ padding: 0 11px;
836
+ border-radius: 999px;
837
+ background: var(--field-fill);
838
+ color: var(--text-4);
839
+ cursor: text;
840
+ transition: background-color var(--fast) var(--ease), box-shadow var(--fast) var(--ease),
841
+ color var(--fast) var(--ease);
842
+ }
843
+ .search-pill:focus-within {
844
+ background: var(--field-fill-focus);
845
+ box-shadow: inset 0 0 0 0.5px var(--accent-line), var(--ring);
846
+ color: var(--text-2);
847
+ }
848
+ .search-pill svg { width: 14px; height: 14px; flex: none; }
849
+ .search-pill input {
850
+ flex: 1;
851
+ min-width: 0;
852
+ height: 100%;
853
+ padding: 0;
854
+ border: 0;
855
+ background: none;
856
+ outline: none;
857
+ font-size: var(--text-base);
858
+ color: var(--text-1);
859
+ }
860
+ .search-pill input::placeholder { color: var(--text-4); }
861
+ .search-pill input::-webkit-search-cancel-button { -webkit-appearance: none; }
862
+
863
+ /* The general field: the same hollow, squared to a row. */
864
+ .sw-field {
865
+ width: 100%;
866
+ height: var(--h-base);
867
+ padding: 0 10px;
868
+ border: 0;
869
+ border-radius: var(--radius-row);
870
+ background: var(--field-fill);
871
+ color: var(--text-1);
872
+ font-size: var(--text-sm);
873
+ transition: background-color var(--fast) var(--ease), box-shadow var(--fast) var(--ease);
874
+ }
875
+ .sw-field::placeholder { color: var(--text-4); }
876
+ .sw-field:focus {
877
+ outline: none;
878
+ background: var(--field-fill-focus);
879
+ box-shadow: inset 0 0 0 0.5px var(--accent-line), var(--ring);
880
+ }
881
+ .sw-field::-webkit-search-cancel-button { -webkit-appearance: none; }
882
+
883
+ .pane-scroll { flex: 1; min-height: 0; padding: 2px 8px 12px; }
884
+
885
+ .tree { display: flex; flex-direction: column; gap: 1px; }
886
+
887
+ .tree-row {
888
+ position: relative;
889
+ display: flex;
890
+ align-items: center;
891
+ gap: 7px;
892
+ width: 100%;
893
+ height: 28px;
894
+ padding-right: 9px;
895
+ border-radius: var(--radius-row);
896
+ text-align: left;
897
+ color: var(--text-2);
898
+ font-size: var(--text-base);
899
+ transition: background-color var(--fast) var(--ease), color var(--fast) var(--ease);
900
+ }
901
+ .tree-row:hover { background: var(--bg-3); color: var(--text-1); }
902
+ .tree-row .name {
903
+ flex: 1;
904
+ min-width: 0;
905
+ overflow: hidden;
906
+ text-overflow: ellipsis;
907
+ white-space: nowrap;
908
+ }
909
+ /* The size is a fact for the row you are pointing at or have open. At rest
910
+ it takes no room at all — an invisible readout still reserving its width
911
+ would cut every longer name short in a narrow column. */
912
+ .tree-row .size {
913
+ display: none;
914
+ flex: none;
915
+ font-family: var(--font-code);
916
+ font-size: var(--text-xs);
917
+ color: var(--text-4);
918
+ font-variant-numeric: tabular-nums;
919
+ }
920
+ .tree-row:hover .size,
921
+ .tree-row.selected .size { display: inline; }
922
+ /* The selection is the Finder's: the accent gradient, the label white. */
923
+ .tree-row.selected {
924
+ background: var(--select);
925
+ color: #ffffff;
926
+ font-weight: 500;
927
+ }
928
+ .tree-row.selected .size { color: rgb(255 255 255 / 0.78); }
929
+ .tree-row.selected .chev { color: rgb(255 255 255 / 0.8); }
930
+
931
+ /* Every row shares one leading ladder — indent, twisty, glyph, name — so a
932
+ child's icon sits exactly one step right of its parent's. Files carry an
933
+ EMPTY twisty so they line up with folders at the same depth. */
934
+ .tree-row .chev,
935
+ .tree-row .chev-slot {
936
+ display: inline-flex;
937
+ align-items: center;
938
+ justify-content: center;
939
+ width: 12px;
940
+ height: 12px;
941
+ flex: none;
942
+ color: var(--text-4);
943
+ }
944
+ .tree-row .chev { transition: transform var(--fast) var(--ease); }
945
+ .tree-row[aria-expanded="true"] .chev { transform: rotate(90deg); }
946
+
947
+ .glyph-pad { display: inline-flex; align-items: center; justify-content: center; }
948
+ .file-glyph { display: block; width: 16px; height: 16px; flex: none; }
949
+ .folder-glyph { color: color-mix(in srgb, var(--accent) 88%, #ffffff); }
950
+ .tree-row.selected .folder-glyph { color: #ffffff; }
951
+ /* The language tints are drawn for paper; on the night ground they lift. */
952
+ :root[data-theme="dark"] .file-glyph { filter: brightness(1.45) saturate(1.1); }
953
+
954
+ .tree-guide {
955
+ position: absolute;
956
+ top: 0;
957
+ bottom: 0;
958
+ width: 1px;
959
+ background: var(--line-2);
960
+ pointer-events: none;
961
+ }
962
+
963
+ .tree-empty {
964
+ margin: 8px 10px;
965
+ font-size: var(--text-base);
966
+ color: var(--text-3);
967
+ line-height: 1.55;
968
+ }
969
+
970
+ /* ------------------------------------------------------------------------- */
971
+ /* The sheet: conversation, files, history */
972
+ /* ------------------------------------------------------------------------- */
973
+
974
+ .content {
975
+ position: relative;
976
+ display: flex;
977
+ flex: 1;
978
+ flex-direction: column;
979
+ min-width: 0;
980
+ min-height: 0;
981
+ overflow: hidden;
982
+ /* The frame's hairline stays; the fill does not. A conversation stands on
983
+ the window's own glass (Winston, 2026-09-15: no white page), so only a
984
+ FILE — a document — lays paper down inside the frame. */
985
+ border-top: 1px solid var(--rule);
986
+ }
987
+
988
+ /* A file opened from the explorer becomes a TAB here: the content plane is
989
+ where content belongs, and the only place a file gets real width. */
990
+ .tabstrip {
991
+ display: flex;
992
+ align-items: center;
993
+ gap: 4px;
994
+ padding: 8px 10px;
995
+ border-bottom: 1px solid var(--line-3);
996
+ overflow-x: auto;
997
+ scrollbar-width: none;
998
+ flex: none;
999
+ }
1000
+ .tabstrip::-webkit-scrollbar { display: none; }
1001
+ .tabstrip[hidden] { display: none; }
1002
+
1003
+ .tab {
1004
+ display: inline-flex;
1005
+ align-items: center;
1006
+ gap: 7px;
1007
+ height: 30px;
1008
+ padding: 0 11px;
1009
+ flex: none;
1010
+ border-radius: 999px;
1011
+ color: var(--text-3);
1012
+ font-size: 12.5px;
1013
+ font-weight: 500;
1014
+ white-space: nowrap;
1015
+ transition: background-color var(--fast) var(--ease), color var(--fast) var(--ease),
1016
+ box-shadow var(--fast) var(--ease);
1017
+ }
1018
+ .tab:hover { background: var(--bg-3); color: var(--text-1); }
1019
+ .tab[aria-selected="true"] {
1020
+ background: var(--bg-3);
1021
+ color: var(--text-1);
1022
+ }
1023
+ .tab > svg { width: 14px; height: 14px; flex: none; }
1024
+ .tab .glyph-pad { width: 14px; }
1025
+ .tab .file-glyph { width: 14px; height: 14px; }
1026
+ .tab .close {
1027
+ display: inline-flex;
1028
+ align-items: center;
1029
+ justify-content: center;
1030
+ width: 18px;
1031
+ height: 18px;
1032
+ margin-right: -5px;
1033
+ border-radius: 999px;
1034
+ color: var(--text-4);
1035
+ opacity: 0;
1036
+ transition: opacity var(--fast) var(--ease), background-color var(--fast) var(--ease);
1037
+ }
1038
+ .tab:hover .close,
1039
+ .tab[aria-selected="true"] .close { opacity: 1; }
1040
+ .tab .close:hover { background: var(--bg-2); color: var(--text-1); }
1041
+ .tab .close svg { width: 10px; height: 10px; }
1042
+
1043
+ .plane { flex: 1; min-height: 0; display: flex; flex-direction: column; position: relative; }
1044
+ #plane-file { background: var(--paper); }
1045
+ .plane[hidden] { display: none; }
1046
+
1047
+ /* Transcript */
1048
+
1049
+ .transcript {
1050
+ flex: 1;
1051
+ min-height: 0;
1052
+ padding-inline: var(--gutter);
1053
+ /* Reserve the scrollbar on BOTH edges, or a centred column drifts half a
1054
+ scrollbar off the composer below it. */
1055
+ scrollbar-gutter: stable both-edges;
1056
+ }
1057
+
1058
+ /* The gutter lives on the scroller, so this box is EXACTLY the measure — and
1059
+ so is the composer's. */
1060
+ .transcript-inner {
1061
+ max-width: var(--chat-measure);
1062
+ margin: 0 auto;
1063
+ padding: 24px 0 16px;
1064
+ display: flex;
1065
+ flex-direction: column;
1066
+ gap: 24px;
1067
+ }
1068
+
1069
+ /* Nothing said yet: the agent's lockup and the composer, together in the
1070
+ middle of the sheet — the poster, then the one thing to do. The composer
1071
+ docks to the foot the moment a conversation exists. */
1072
+ .plane[data-empty="true"] { justify-content: center; }
1073
+ .plane[data-empty="true"] .transcript { flex: 0 0 auto; overflow: visible; }
1074
+ .plane[data-empty="true"] .transcript-inner { padding: 0 0 8px; }
1075
+ .plane[data-empty="true"] .composer-dock {
1076
+ padding-bottom: min(14vh, 120px);
1077
+ border-top-color: transparent;
1078
+ box-shadow: none;
1079
+ }
1080
+
1081
+ .empty-state {
1082
+ display: flex;
1083
+ flex-direction: column;
1084
+ align-items: center;
1085
+ gap: 8px;
1086
+ padding: 0 0 18px;
1087
+ text-align: center;
1088
+ }
1089
+ .empty-state .mark {
1090
+ display: block;
1091
+ width: 92px;
1092
+ height: 92px;
1093
+ margin-bottom: 6px;
1094
+ object-fit: contain;
1095
+ }
1096
+ .empty-state .big {
1097
+ font-family: var(--font-brand);
1098
+ font-size: clamp(34px, 3.6vw, 46px);
1099
+ font-weight: 600;
1100
+ line-height: 1.04;
1101
+ letter-spacing: -0.022em;
1102
+ color: var(--text-1);
1103
+ }
1104
+ .empty-state .hint {
1105
+ max-width: 34ch;
1106
+ margin: 2px auto 0;
1107
+ font-family: var(--font-brand);
1108
+ font-size: 19px;
1109
+ line-height: 1.42;
1110
+ color: var(--text-2);
1111
+ }
1112
+
1113
+ /* The reader's turn: the product's bubble — the accent held legible top to
1114
+ bottom by its gradient, 16px with an 8px tail, white ink. */
1115
+ .msg-user {
1116
+ align-self: flex-end;
1117
+ display: flex;
1118
+ flex-direction: column;
1119
+ gap: 2px;
1120
+ width: fit-content;
1121
+ max-width: 85%;
1122
+ padding: 8px 14px;
1123
+ border: 0.5px solid var(--you-rim);
1124
+ border-radius: 16px 16px 6px 16px;
1125
+ background: var(--you-fill);
1126
+ box-shadow: var(--you-shine);
1127
+ color: var(--text-1);
1128
+ font-size: 13.5px;
1129
+ line-height: 20px;
1130
+ white-space: pre-wrap;
1131
+ overflow-wrap: anywhere;
1132
+ }
1133
+
1134
+ /* A turn's parts stand apart; an outline and the thing it explains stay
1135
+ together (`.aqua-content > div > .aqua-turn-trace + *`). */
1136
+ .msg-assistant {
1137
+ display: flex;
1138
+ flex-direction: column;
1139
+ min-width: 0;
1140
+ }
1141
+ .msg-assistant > * + * { margin-top: 20px; }
1142
+ .msg-assistant > .disclosure + * { margin-top: 8px; }
1143
+ .msg-assistant > .md + .file-cards { margin-top: 14px; }
1144
+
1145
+ .msg-text {
1146
+ margin: 0;
1147
+ font-size: inherit;
1148
+ color: inherit;
1149
+ overflow-wrap: anywhere;
1150
+ }
1151
+
1152
+ .msg-media { display: flex; flex-wrap: wrap; gap: 6px; }
1153
+
1154
+ /* A billed tool waiting on the reader's answer. The approval is a decision,
1155
+ so it wears the gel. */
1156
+ .spend-card {
1157
+ align-self: center;
1158
+ width: fit-content;
1159
+ max-width: min(100%, 448px);
1160
+ padding: 2px 0;
1161
+ text-align: center;
1162
+ }
1163
+ .spend-card .title {
1164
+ margin: 0;
1165
+ font-size: 13px;
1166
+ font-weight: 600;
1167
+ line-height: 18px;
1168
+ color: var(--text-1);
1169
+ }
1170
+ .spend-card .body { margin: 4px 0 0; font-size: 12px; line-height: 18px; color: var(--text-2); }
1171
+ .spend-card .body code {
1172
+ padding: 0.1em 0.38em;
1173
+ border-radius: 5px;
1174
+ background: var(--bg-3);
1175
+ font-family: var(--font-code);
1176
+ font-size: 0.92em;
1177
+ }
1178
+ .spend-card .actions { display: flex; justify-content: center; margin-top: 8px; }
1179
+
1180
+ /* Delivered files: send_file's handover, an object you can open. */
1181
+ .file-cards { display: flex; flex-direction: column; gap: 8px; }
1182
+ .file-card {
1183
+ display: flex;
1184
+ align-items: center;
1185
+ gap: 12px;
1186
+ max-width: 420px;
1187
+ padding: 9px 14px 9px 9px;
1188
+ border: 0.5px solid var(--glass-card-rim);
1189
+ border-radius: 12px;
1190
+ background: var(--glass-card);
1191
+ box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.45), 0 1px 2px rgb(0 10 40 / 0.06);
1192
+ text-decoration: none;
1193
+ color: var(--text-1);
1194
+ transition: background-color var(--fast) var(--ease), box-shadow var(--fast) var(--ease);
1195
+ }
1196
+ .file-card:hover { background: color-mix(in srgb, var(--glass-card) 60%, rgb(255 255 255 / 0.3)); }
1197
+ .file-card .glyph-pad {
1198
+ width: 36px;
1199
+ height: 36px;
1200
+ flex: none;
1201
+ border-radius: 10px;
1202
+ background: var(--bg-3);
1203
+ }
1204
+ .file-card .glyph-pad .file-glyph { width: 20px; height: 20px; }
1205
+ .file-card .meta { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
1206
+ .file-card .name {
1207
+ font-size: var(--text-base);
1208
+ font-weight: 500;
1209
+ overflow: hidden;
1210
+ text-overflow: ellipsis;
1211
+ white-space: nowrap;
1212
+ }
1213
+ .file-card .sub {
1214
+ font-size: var(--text-xs);
1215
+ color: var(--text-3);
1216
+ overflow: hidden;
1217
+ text-overflow: ellipsis;
1218
+ white-space: nowrap;
1219
+ }
1220
+ .file-card > svg { width: 16px; height: 16px; color: var(--text-3); flex: none; }
1221
+
1222
+ /* The frame hugs the picture; fitShot() sets the exact box once the image
1223
+ reports its natural size, and these caps are the pre-load floor. */
1224
+ .msg-media .shot {
1225
+ display: block;
1226
+ width: fit-content;
1227
+ max-width: 100%;
1228
+ border-radius: 10px;
1229
+ overflow: hidden;
1230
+ line-height: 0;
1231
+ box-shadow: 0 0 0 1px rgb(255 255 255 / 0.3);
1232
+ text-decoration: none;
1233
+ }
1234
+ .msg-media img {
1235
+ display: block;
1236
+ max-width: 260px;
1237
+ max-height: 260px;
1238
+ width: auto;
1239
+ height: auto;
1240
+ }
1241
+
1242
+ .attach-line {
1243
+ display: flex;
1244
+ align-items: center;
1245
+ gap: 6px;
1246
+ min-width: 0;
1247
+ font-size: 12px;
1248
+ font-weight: 500;
1249
+ line-height: 18px;
1250
+ color: var(--text-2);
1251
+ text-decoration: none;
1252
+ }
1253
+ .attach-line svg { width: 14px; height: 14px; flex: none; }
1254
+ .attach-line .name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1255
+
1256
+ /* A notice is housekeeping, told once, in the middle of the flow. */
1257
+ .msg-notice {
1258
+ align-self: center;
1259
+ max-width: 88%;
1260
+ padding: 0 8px;
1261
+ color: var(--text-3);
1262
+ font-size: 12px;
1263
+ line-height: 18px;
1264
+ text-align: center;
1265
+ overflow-wrap: anywhere;
1266
+ }
1267
+
1268
+ /* What went wrong is a line with the orb that says so — the system's own
1269
+ voice in the conversation, not a red panel. */
1270
+ .msg-error {
1271
+ display: flex;
1272
+ align-items: flex-start;
1273
+ gap: 8px;
1274
+ width: fit-content;
1275
+ max-width: 92%;
1276
+ margin: 0;
1277
+ font-size: 12px;
1278
+ line-height: 18px;
1279
+ color: var(--text-1);
1280
+ overflow-wrap: anywhere;
1281
+ }
1282
+ .msg-error .orb { margin-top: 3.5px; }
1283
+
1284
+ /* Markdown inside a reply — the reading scale. Headings take the serif, as
1285
+ every heading in the product does. */
1286
+
1287
+ .md {
1288
+ font-size: var(--text-read);
1289
+ line-height: 22px;
1290
+ color: var(--text-1);
1291
+ overflow-wrap: anywhere;
1292
+ }
1293
+ .md > :first-child { margin-top: 0; }
1294
+ .md > :last-child { margin-bottom: 0; }
1295
+ .md p { margin: 0 0 12px; }
1296
+ .md h1, .md h2, .md h3 {
1297
+ margin: 22px 0 8px;
1298
+ font-family: var(--font-brand);
1299
+ font-weight: 600;
1300
+ line-height: 1.2;
1301
+ letter-spacing: -0.01em;
1302
+ }
1303
+ .md h1 { font-size: 25px; }
1304
+ .md h2 { font-size: 21px; }
1305
+ .md h3 { font-size: 18px; }
1306
+ .md h4, .md h5, .md h6 { margin: 16px 0 6px; font-size: var(--text-read); font-weight: 600; }
1307
+ .md ul, .md ol { margin: 0 0 12px; padding-left: 1.3em; }
1308
+ .md li + li { margin-top: 4px; }
1309
+ .md li::marker { color: var(--text-4); }
1310
+ .md li > p { margin: 0 0 6px; }
1311
+ .md li > ul, .md li > ol { margin: 4px 0 0; }
1312
+ .md li > .code-block { margin: 8px 0; }
1313
+ .md li > :last-child { margin-bottom: 0; }
1314
+ .md a {
1315
+ color: var(--accent-text);
1316
+ text-decoration: underline;
1317
+ text-decoration-color: var(--accent-line);
1318
+ text-underline-offset: 2px;
1319
+ }
1320
+ .md blockquote {
1321
+ margin: 0 0 12px;
1322
+ padding: 2px 0 2px 14px;
1323
+ border-left: 2px solid var(--accent-line);
1324
+ color: var(--text-2);
1325
+ }
1326
+ .md hr { margin: 20px 0; border: 0; border-top: 1px solid var(--line-2); }
1327
+ .md code {
1328
+ padding: 0.12em 0.38em;
1329
+ border-radius: 5px;
1330
+ background: var(--bg-3);
1331
+ font-family: var(--font-code);
1332
+ font-size: 0.88em;
1333
+ }
1334
+ .md strong { font-weight: 600; }
1335
+ .md-img {
1336
+ display: block;
1337
+ max-width: 100%;
1338
+ border-radius: 10px;
1339
+ box-shadow: 0 0 0 1px var(--line-2);
1340
+ }
1341
+
1342
+ /* A reply in the conversation. */
1343
+ .md.reply {
1344
+ max-width: 92%;
1345
+ font-size: 13.5px;
1346
+ line-height: 20px;
1347
+ }
1348
+ .md.reply :is(p, ul, ol, blockquote, .code-block, .table-wrap) { margin: 0 0 12px; }
1349
+ .md.reply > :last-child { margin-bottom: 0; }
1350
+ .md.reply li + li { margin-top: 4px; }
1351
+ .md.reply :is(h1, h2, h3, h4, h5, h6) {
1352
+ margin: 0 0 6px;
1353
+ font-family: var(--font-ui);
1354
+ font-weight: 600;
1355
+ letter-spacing: normal;
1356
+ }
1357
+ .md.reply h1 { font-size: 17px; line-height: 24px; }
1358
+ .md.reply h2 { font-size: 15px; line-height: 22px; }
1359
+ .md.reply :is(h3, h4, h5, h6) { font-size: 13.5px; line-height: 20px; }
1360
+ .md.reply * + :is(h1, h2, h3, h4, h5, h6) { margin-top: 18px; }
1361
+ .md.reply blockquote { padding: 0 0 0 12px; border-left: 2px solid rgb(0 10 40 / 0.18); }
1362
+ .md.reply code { font-size: 12px; }
1363
+ .md.reply hr { margin: 14px 0; }
1364
+
1365
+ /* Code in a reply is a WELL in the glass, the way the window sets it — the
1366
+ dark block is the document viewer's. The copy door waits for the cursor. */
1367
+ .md.reply .code-block {
1368
+ margin: 0 0 12px;
1369
+ border-radius: 8px;
1370
+ background: var(--code-well);
1371
+ box-shadow: inset 0 0 0 1px var(--code-well-rim), inset 0 1px 2px rgb(0 10 40 / 0.06);
1372
+ color: var(--text-1);
1373
+ }
1374
+ .md.reply .code-block > header {
1375
+ position: absolute;
1376
+ top: 4px;
1377
+ right: 4px;
1378
+ height: auto;
1379
+ padding: 0;
1380
+ border: 0;
1381
+ opacity: 0;
1382
+ transition: opacity var(--fast) var(--ease);
1383
+ }
1384
+ .md.reply .code-block:hover > header,
1385
+ .md.reply .code-block:focus-within > header { opacity: 1; }
1386
+ .md.reply .code-block .lang { display: none; }
1387
+ .md.reply .code-block pre {
1388
+ padding: 10px 40px 10px 12px;
1389
+ font-size: 12px;
1390
+ line-height: 18px;
1391
+ }
1392
+ .md.reply .copy-btn {
1393
+ height: 22px;
1394
+ background: var(--well-fill);
1395
+ box-shadow: inset 0 0 0 0.5px var(--glass-card-rim);
1396
+ color: var(--text-3);
1397
+ }
1398
+ .md.reply .copy-btn:hover { background: var(--paper); color: var(--text-1); }
1399
+
1400
+ .code-block {
1401
+ position: relative;
1402
+ margin: 4px 0 14px;
1403
+ border-radius: var(--radius-card);
1404
+ background: var(--code-ground);
1405
+ box-shadow: 0 0 0 1px var(--code-rim);
1406
+ color: var(--code-ink);
1407
+ overflow: hidden;
1408
+ }
1409
+ .code-block > header,
1410
+ .code-block .head {
1411
+ display: flex;
1412
+ align-items: center;
1413
+ justify-content: space-between;
1414
+ gap: 8px;
1415
+ height: 34px;
1416
+ padding: 0 6px 0 14px;
1417
+ border-bottom: 1px solid rgb(255 255 255 / 0.06);
1418
+ }
1419
+ .code-block .lang { font-family: var(--font-code); font-size: var(--text-xs); color: #8a93a4; }
1420
+ .code-block pre {
1421
+ margin: 0;
1422
+ padding: 12px 16px 14px;
1423
+ overflow-x: auto;
1424
+ font-family: var(--font-code);
1425
+ font-size: 12.5px;
1426
+ line-height: 1.65;
1427
+ }
1428
+ .code-block code { padding: 0; background: none; color: inherit; font-size: inherit; }
1429
+
1430
+ .copy-btn {
1431
+ height: 24px;
1432
+ padding: 0 10px;
1433
+ border-radius: 999px;
1434
+ color: #a3abb9;
1435
+ font-size: var(--text-xs);
1436
+ font-weight: 500;
1437
+ transition: background-color var(--fast) var(--ease), color var(--fast) var(--ease);
1438
+ }
1439
+ .copy-btn:hover { background: rgb(255 255 255 / 0.08); color: #ffffff; }
1440
+
1441
+ .table-wrap {
1442
+ margin: 4px 0 14px;
1443
+ overflow-x: auto;
1444
+ border-radius: 10px;
1445
+ box-shadow: inset 0 0 0 1px var(--line-2);
1446
+ }
1447
+ .table-wrap table { width: 100%; border-collapse: collapse; font-size: var(--text-base); }
1448
+ .table-wrap th, .table-wrap td {
1449
+ padding: 7px 12px;
1450
+ text-align: left;
1451
+ border-bottom: 1px solid var(--line-3);
1452
+ }
1453
+ .table-wrap th + th, .table-wrap td + td { border-left: 1px solid var(--line-3); }
1454
+ .table-wrap tr:last-child td { border-bottom: 0; }
1455
+ .table-wrap th { background: var(--bg-4); font-weight: 600; color: var(--text-2); }
1456
+
1457
+ /* The activity outline.
1458
+ *
1459
+ * Norbert's window draws a turn's work as an OUTLINE, not a table: one
1460
+ * disclosure line saying what happened, open while the turn runs, and a row
1461
+ * per call under it — orbs in one column down the left edge, the way a
1462
+ * Settings list lines its states up. A row that has something to show (a
1463
+ * result, an error) opens onto it in a well. */
1464
+
1465
+ .disclosure { min-width: 0; font-size: 12px; line-height: 18px; }
1466
+
1467
+ .disclose-head {
1468
+ display: flex;
1469
+ align-items: center;
1470
+ gap: 6px;
1471
+ width: fit-content;
1472
+ max-width: calc(100% + 12px);
1473
+ margin-left: -6px;
1474
+ padding: 2px 6px;
1475
+ border-radius: 5px;
1476
+ text-align: left;
1477
+ color: var(--text-2);
1478
+ transition: background-color var(--fast) var(--ease);
1479
+ }
1480
+ .disclose-head:hover { background: var(--row-hover); }
1481
+ .disclose-head .tri {
1482
+ width: 10px;
1483
+ height: 10px;
1484
+ flex: none;
1485
+ color: var(--text-3);
1486
+ transition: rotate 200ms var(--ease);
1487
+ }
1488
+ .disclose-head[aria-expanded="true"] .tri { rotate: 90deg; }
1489
+ .disclose-head .label {
1490
+ min-width: 0;
1491
+ overflow: hidden;
1492
+ text-overflow: ellipsis;
1493
+ white-space: nowrap;
1494
+ font-weight: 500;
1495
+ }
1496
+ .disclose-head .clock {
1497
+ flex: none;
1498
+ color: var(--text-3);
1499
+ font-variant-numeric: tabular-nums;
1500
+ }
1501
+
1502
+ .trace-rows, .plan-rows {
1503
+ margin: 0;
1504
+ padding: 2px 0 2px 16px;
1505
+ list-style: none;
1506
+ }
1507
+ .trace-row { min-width: 0; }
1508
+
1509
+ .row-head {
1510
+ display: flex;
1511
+ align-items: center;
1512
+ gap: 8px;
1513
+ width: calc(100% + 12px);
1514
+ min-width: 0;
1515
+ height: 24px;
1516
+ margin-left: -6px;
1517
+ padding: 0 6px;
1518
+ border-radius: 5px;
1519
+ text-align: left;
1520
+ font-size: 12px;
1521
+ color: var(--text-1);
1522
+ cursor: default;
1523
+ }
1524
+ .row-head[data-disclose] { cursor: pointer; transition: background-color var(--fast) var(--ease); }
1525
+ .row-head[data-disclose]:hover { background: var(--row-hover); }
1526
+ .row-head:disabled { opacity: 1; }
1527
+ .row-head .verb { flex: none; }
1528
+ .row-head .arg {
1529
+ min-width: 0;
1530
+ overflow: hidden;
1531
+ text-overflow: ellipsis;
1532
+ white-space: nowrap;
1533
+ color: var(--text-3);
1534
+ }
1535
+ .row-head .arg.mono { font-family: var(--font-code); font-size: 11.5px; }
1536
+ .row-head .ms {
1537
+ flex: none;
1538
+ margin-left: auto;
1539
+ color: var(--text-4);
1540
+ font-size: 11.5px;
1541
+ font-variant-numeric: tabular-nums;
1542
+ }
1543
+
1544
+ /* One column of state: the orb, or the spinner while the call is out. */
1545
+ .state {
1546
+ display: grid;
1547
+ place-items: center;
1548
+ width: 11px;
1549
+ height: 11px;
1550
+ flex: none;
1551
+ color: var(--text-3);
1552
+ }
1553
+ .bars { display: inline-block; flex: none; color: inherit; }
1554
+
1555
+ /* What a row opens onto: the head of the result, set into the glass. */
1556
+ .well-detail {
1557
+ width: fit-content;
1558
+ max-width: calc(100% - 19px);
1559
+ max-height: 112px;
1560
+ margin: 4px 0 4px 19px;
1561
+ padding: 6px 10px;
1562
+ overflow-y: auto;
1563
+ border-radius: 8px;
1564
+ background: var(--code-well);
1565
+ box-shadow: inset 0 0 0 1px var(--code-well-rim), inset 0 1px 2px rgb(0 10 40 / 0.06);
1566
+ color: var(--text-2);
1567
+ font-family: var(--font-code);
1568
+ font-size: 11.5px;
1569
+ line-height: 16px;
1570
+ white-space: pre-wrap;
1571
+ overflow-wrap: anywhere;
1572
+ }
1573
+
1574
+ /* The plan is the same outline: the sentence and its count, the steps under
1575
+ it. While the turn runs the sentence is ink; afterwards it recedes. */
1576
+ .plan:not(.settled) .disclose-head .label { font-weight: 600; color: var(--text-1); }
1577
+ .plan-rows li {
1578
+ display: flex;
1579
+ align-items: center;
1580
+ gap: 8px;
1581
+ min-height: 24px;
1582
+ }
1583
+ .plan-rows .step {
1584
+ min-width: 0;
1585
+ flex: 1;
1586
+ overflow: hidden;
1587
+ text-overflow: ellipsis;
1588
+ white-space: nowrap;
1589
+ color: var(--text-1);
1590
+ }
1591
+ .plan-rows li.completed .step { color: var(--text-3); }
1592
+ .plan-rows li.in_progress .step { font-weight: 500; }
1593
+
1594
+ /* Working */
1595
+
1596
+ .working {
1597
+ display: flex;
1598
+ align-items: center;
1599
+ gap: 8px;
1600
+ margin: 0;
1601
+ font-size: 12px;
1602
+ line-height: 18px;
1603
+ color: var(--text-2);
1604
+ }
1605
+ .working .label { font-weight: 500; }
1606
+ .working .clock {
1607
+ color: var(--text-3);
1608
+ font-variant-numeric: tabular-nums;
1609
+ }
1610
+
1611
+ /* The system's spinner: twelve bars fading round, the one every Aqua row
1612
+ waits with (Spinner.tsx). Sized by its box, so it reads at 11px in a trace
1613
+ row and 12px on the Thinking line. */
1614
+ .t-spinner {
1615
+ /* Block, or the inline box takes no width and the bars — sized in
1616
+ percentages of it — come out 0×0 and invisible. */
1617
+ display: block;
1618
+ position: relative;
1619
+ top: 50%;
1620
+ left: 50%;
1621
+ }
1622
+ .t-spinner__bar {
1623
+ position: absolute;
1624
+ height: 9%;
1625
+ width: 26%;
1626
+ left: -10%;
1627
+ top: -4.5%;
1628
+ border-radius: 6px;
1629
+ background-color: currentColor;
1630
+ animation: t-spinner-fade 1.2s linear infinite;
1631
+ }
1632
+ @keyframes t-spinner-fade {
1633
+ 0% { opacity: 1; }
1634
+ 100% { opacity: 0.15; }
1635
+ }
1636
+
1637
+ .shimmer, .t-shimmer {
1638
+ background: linear-gradient(90deg, var(--text-4) 0%, var(--text-1) 45%, var(--text-4) 90%);
1639
+ background-size: 220% 100%;
1640
+ -webkit-background-clip: text;
1641
+ background-clip: text;
1642
+ color: transparent;
1643
+ animation: shimmer 1.9s linear infinite;
1644
+ }
1645
+ @keyframes shimmer {
1646
+ from { background-position: 120% 0; }
1647
+ to { background-position: -120% 0; }
1648
+ }
1649
+
1650
+ @keyframes spin { to { transform: rotate(360deg); } }
1651
+
1652
+ .stream-caret {
1653
+ display: inline-block;
1654
+ width: 2px;
1655
+ height: 1.05em;
1656
+ margin-left: 1px;
1657
+ vertical-align: text-bottom;
1658
+ border-radius: 1px;
1659
+ background: var(--accent);
1660
+ animation: caret 1.1s steps(2, start) infinite;
1661
+ }
1662
+ @keyframes caret { 50% { opacity: 0; } }
1663
+
1664
+ /* File view ------------------------------------------------------------------
1665
+ A file is a DOCUMENT on the paper: one quiet bar that says where it lives
1666
+ and how it stands, then the text in a centred column, soft-wrapped, with
1667
+ its line numbers in the margin. There is no status strip under it — the
1668
+ bar carries the caret, the save state and the doors. */
1669
+
1670
+ .fileview { flex: 1; min-height: 0; display: flex; flex-direction: column; }
1671
+
1672
+ .fileview-head {
1673
+ display: flex;
1674
+ align-items: center;
1675
+ gap: 12px;
1676
+ height: 44px;
1677
+ padding: 0 10px 0 16px;
1678
+ border-bottom: 1px solid var(--line-3);
1679
+ flex: none;
1680
+ }
1681
+
1682
+ /* Where it lives, in words: lane › folders › file. */
1683
+ .crumbs {
1684
+ display: flex;
1685
+ align-items: center;
1686
+ gap: 6px;
1687
+ min-width: 0;
1688
+ font-size: var(--text-base);
1689
+ white-space: nowrap;
1690
+ }
1691
+ .crumbs .glyph-pad { width: 16px; flex: none; margin-right: 2px; }
1692
+ .crumbs .crumb { flex: none; color: var(--text-3); }
1693
+ .crumbs .crumb.here {
1694
+ flex: 0 1 auto;
1695
+ min-width: 0;
1696
+ overflow: hidden;
1697
+ text-overflow: ellipsis;
1698
+ color: var(--text-1);
1699
+ font-weight: 500;
1700
+ }
1701
+ .crumb-sep { display: inline-flex; flex: none; color: var(--text-4); }
1702
+ .crumb-sep svg { width: 12px; height: 12px; }
1703
+
1704
+ .file-meta {
1705
+ display: flex;
1706
+ align-items: center;
1707
+ gap: 10px;
1708
+ margin-left: auto;
1709
+ flex: none;
1710
+ font-size: var(--text-sm);
1711
+ color: var(--text-4);
1712
+ }
1713
+ .caret-readout { font-variant-numeric: tabular-nums; white-space: nowrap; }
1714
+ .readonly-mark { display: inline-flex; color: var(--text-4); }
1715
+ .readonly-mark svg { width: 13px; height: 13px; }
1716
+ .file-actions { display: flex; align-items: center; gap: 2px; }
1717
+
1718
+ /* The save state: words that change in place, with a mark before them. */
1719
+ .save-state {
1720
+ display: inline-flex;
1721
+ align-items: center;
1722
+ gap: 6px;
1723
+ height: 26px;
1724
+ padding: 0 9px 0 7px;
1725
+ border-radius: 999px;
1726
+ color: var(--text-4);
1727
+ font-size: var(--text-sm);
1728
+ white-space: nowrap;
1729
+ transition: background-color var(--fast) var(--ease), color var(--fast) var(--ease);
1730
+ }
1731
+ .save-state:disabled { cursor: default; }
1732
+ .save-mark { display: inline-flex; align-items: center; justify-content: center; width: 12px; }
1733
+ .save-mark svg { width: 12px; height: 12px; }
1734
+ .save-state[data-state="dirty"] { color: var(--text-2); }
1735
+ .save-state[data-state="dirty"] .save-mark::before {
1736
+ content: "";
1737
+ width: 7px;
1738
+ height: 7px;
1739
+ border-radius: 999px;
1740
+ background: var(--accent);
1741
+ }
1742
+ .save-state[data-state="dirty"]:hover { background: var(--bg-3); color: var(--text-1); }
1743
+ .save-state[data-state="saving"] .save-mark svg { animation: spin 0.9s linear infinite; }
1744
+ .save-state[data-state="error"] { color: color-mix(in srgb, var(--danger) 85%, var(--ink)); }
1745
+ .save-state[data-state="error"]:hover { background: color-mix(in srgb, var(--danger) 9%, transparent); }
1746
+
1747
+ .fileview-body { flex: 1; min-height: 0; padding: 0; }
1748
+
1749
+ /* Rendered HTML: the page is the content, on the white ground pages assume. */
1750
+ .fileview-body.html-frame { display: flex; }
1751
+ .html-preview { flex: 1; width: 100%; border: 0; background: #ffffff; }
1752
+
1753
+ /* Two views of one file, in words: Write | Preview, Preview | Code — the Aqua
1754
+ switch at its small size. */
1755
+ .view-toggle {
1756
+ display: inline-flex;
1757
+ gap: 2px;
1758
+ height: 28px;
1759
+ padding: 2px;
1760
+ border-radius: 999px;
1761
+ background: var(--track);
1762
+ box-shadow: var(--track-shade);
1763
+ }
1764
+ .view-toggle button {
1765
+ padding: 0 11px;
1766
+ border-radius: 999px;
1767
+ color: var(--text-2);
1768
+ font-size: var(--text-sm);
1769
+ font-weight: 500;
1770
+ transition: color var(--fast) var(--ease);
1771
+ }
1772
+ .view-toggle button:hover { color: var(--text-1); }
1773
+ .view-toggle button[aria-pressed="true"] {
1774
+ --gel-crown: var(--accent);
1775
+ border: 1px solid transparent;
1776
+ background-image:
1777
+ linear-gradient(rgb(255 255 255 / 0.85), transparent 10px),
1778
+ linear-gradient(rgb(255 255 255 / 0.25) 10px, transparent 10px),
1779
+ linear-gradient(var(--gel-crown), var(--gel-foot)),
1780
+ linear-gradient(var(--gel-rim-crown), var(--gel-rim-foot));
1781
+ background-size: 100%, calc(100% - 20px), 100%, 100%;
1782
+ background-position: top, top center, center, center;
1783
+ background-repeat: no-repeat;
1784
+ background-origin: padding-box, padding-box, padding-box, border-box;
1785
+ background-clip: padding-box, padding-box, padding-box, border-box;
1786
+ box-shadow: 0 1px 2px rgb(10 35 90 / 0.4);
1787
+ color: var(--gel-label);
1788
+ }
1789
+
1790
+ /* A preview reads at the document's measure, headings in the serif. (Named
1791
+ `doc-page`: `.doc` is already the attachment chip in the transcript.) */
1792
+ .doc-scroll { overflow-y: auto; }
1793
+ .doc-page {
1794
+ box-sizing: content-box;
1795
+ max-width: 720px;
1796
+ margin: 0 auto;
1797
+ padding: 40px max(28px, 5%) 120px;
1798
+ }
1799
+ .doc-page.md { font-size: 15px; line-height: 26px; }
1800
+ .doc-page.md h1 { font-size: 31px; margin-top: 8px; }
1801
+ .doc-page.md h2 { font-size: 24px; margin-top: 30px; }
1802
+ .doc-page.md h3 { font-size: 19px; margin-top: 24px; }
1803
+ .doc-page.md > :is(p, ul, ol, blockquote) { margin-bottom: 14px; }
1804
+
1805
+ .fileview-body .shot {
1806
+ display: block;
1807
+ max-width: min(calc(100% - 48px), 960px);
1808
+ margin: 32px auto;
1809
+ border-radius: 10px;
1810
+ box-shadow: 0 0 0 1px var(--line-2);
1811
+ }
1812
+ .fileview-body .empty {
1813
+ margin: 0;
1814
+ padding: 28px 20px;
1815
+ font-size: var(--text-base);
1816
+ color: var(--text-3);
1817
+ }
1818
+
1819
+ /* ------------------------------------------------------------------------- */
1820
+ /* Editor */
1821
+ /* ------------------------------------------------------------------------- */
1822
+
1823
+ /* Two layers, one string: a transparent textarea over a painted copy of the
1824
+ same text. Every property that decides where a glyph lands is declared ONCE
1825
+ for both below — family, size, line-height, padding, wrapping, the
1826
+ scrollbar's gutter — or the colour slides off the characters under it. The
1827
+ column is centred by padding computed from the pane's width, so both layers
1828
+ wrap at exactly the same measure. */
1829
+ .editor {
1830
+ position: relative;
1831
+ flex: 1;
1832
+ min-height: 0;
1833
+ overflow: hidden;
1834
+ --doc-measure: 720px;
1835
+ --editor-font: var(--font-ui);
1836
+ --editor-size: 15px;
1837
+ --editor-line: 26px;
1838
+ }
1839
+ .editor[data-kind="code"] {
1840
+ --doc-measure: 880px;
1841
+ --editor-font: var(--font-code);
1842
+ --editor-size: 13px;
1843
+ --editor-line: 22px;
1844
+ }
1845
+
1846
+ .editor-hl,
1847
+ .editor-input {
1848
+ position: absolute;
1849
+ inset: 0;
1850
+ margin: 0;
1851
+ border: 0;
1852
+ /* Room above the first line, the margin the numbers stand in, and half a
1853
+ screen under the last line — the end of a document is written in the
1854
+ middle of the window, not against its bottom edge. */
1855
+ padding: 34px max(76px, calc((100% - var(--doc-measure)) / 2)) 45vh;
1856
+ font-family: var(--editor-font);
1857
+ font-size: var(--editor-size);
1858
+ font-weight: 400;
1859
+ font-style: normal;
1860
+ line-height: var(--editor-line);
1861
+ letter-spacing: 0;
1862
+ word-spacing: 0;
1863
+ font-kerning: normal;
1864
+ font-variant-ligatures: none;
1865
+ text-rendering: auto;
1866
+ tab-size: 2;
1867
+ white-space: pre-wrap;
1868
+ overflow-wrap: break-word;
1869
+ word-break: normal;
1870
+ overflow-x: hidden;
1871
+ overflow-y: auto;
1872
+ scrollbar-gutter: stable;
1873
+ scrollbar-width: thin;
1874
+ scrollbar-color: var(--bg-1) transparent;
1875
+ }
1876
+
1877
+ /* The painted copy never scrolls on its own (the textarea's scroll drives it)
1878
+ and draws no scrollbar — but it keeps the same gutter, so it wraps where
1879
+ the textarea wraps. */
1880
+ .editor-hl {
1881
+ z-index: 0;
1882
+ overflow-y: hidden;
1883
+ pointer-events: none;
1884
+ color: var(--text-1);
1885
+ }
1886
+ /* A read-only file is the painted copy alone, scrolling itself. */
1887
+ .editor-hl.static {
1888
+ overflow-y: auto;
1889
+ pointer-events: auto;
1890
+ user-select: text;
1891
+ cursor: text;
1892
+ }
1893
+
1894
+ /* The text itself is invisible; only the caret and the selection show, so what
1895
+ you read is always the painted copy. */
1896
+ .editor-input {
1897
+ z-index: 1;
1898
+ background: none;
1899
+ color: transparent;
1900
+ -webkit-text-fill-color: transparent;
1901
+ caret-color: var(--accent);
1902
+ resize: none;
1903
+ outline: none;
1904
+ }
1905
+ .editor-input::placeholder { color: var(--text-4); -webkit-text-fill-color: var(--text-4); }
1906
+ .editor-input::selection { background: color-mix(in srgb, var(--accent) 22%, transparent); }
1907
+
1908
+ /* Each line carries its number in the margin. The caret's line gets the accent
1909
+ number and — while you are writing — a soft band. */
1910
+ .hl-line {
1911
+ position: relative;
1912
+ z-index: 0;
1913
+ display: block;
1914
+ min-height: var(--editor-line);
1915
+ }
1916
+ .hl-line::before {
1917
+ content: attr(data-n);
1918
+ position: absolute;
1919
+ top: 0;
1920
+ right: calc(100% + 24px);
1921
+ min-width: 32px;
1922
+ font-family: var(--font-code);
1923
+ font-size: var(--text-xs);
1924
+ font-weight: 400;
1925
+ line-height: var(--editor-line);
1926
+ text-align: right;
1927
+ color: var(--text-4);
1928
+ opacity: 0.55;
1929
+ font-variant-numeric: tabular-nums;
1930
+ -webkit-text-stroke: 0;
1931
+ pointer-events: none;
1932
+ }
1933
+ .hl-line.is-active::before { color: var(--accent-text); opacity: 1; }
1934
+ .editor:focus-within .hl-line.is-active::after {
1935
+ content: "";
1936
+ position: absolute;
1937
+ inset: 0 -14px;
1938
+ z-index: -1;
1939
+ border-radius: 6px;
1940
+ background: var(--bg-4);
1941
+ }
1942
+
1943
+ /* Markdown, painted without moving a glyph: colour, a tint, or a stroke. The
1944
+ markup characters step back so the words lead. */
1945
+ .md-mark { color: var(--text-4); -webkit-text-stroke: 0; }
1946
+ .md-bullet { color: color-mix(in srgb, var(--accent) 65%, var(--text-3)); }
1947
+ .md-h { color: var(--text-1); -webkit-text-stroke: 0.5px currentColor; }
1948
+ .md-strong { color: var(--text-1); -webkit-text-stroke: 0.4px currentColor; }
1949
+ .md-em { color: var(--text-2); }
1950
+ .md-code {
1951
+ border-radius: 4px;
1952
+ background: var(--bg-3);
1953
+ color: color-mix(in srgb, var(--accent) 78%, var(--ink));
1954
+ -webkit-text-stroke: 0;
1955
+ }
1956
+ .md-code .md-mark { color: color-mix(in srgb, var(--accent) 40%, var(--text-4)); }
1957
+ .md-link {
1958
+ color: var(--accent-text);
1959
+ text-decoration: underline;
1960
+ text-decoration-color: var(--accent-line);
1961
+ text-underline-offset: 3px;
1962
+ }
1963
+ .md-quote { color: var(--text-3); }
1964
+ .hl-line.md-fence {
1965
+ background: var(--bg-4);
1966
+ box-shadow: -14px 0 0 var(--bg-4), 14px 0 0 var(--bg-4);
1967
+ }
1968
+ .md-fenced { color: var(--text-2); }
1969
+
1970
+ /* Syntax tokens for code, the studio editor's vocabulary. Weight and slant are
1971
+ safe here only because the mono face keeps every advance the same. */
1972
+ .cs-k { color: #0f766e; font-weight: 550; }
1973
+ .cs-s { color: #a16207; }
1974
+ .cs-c { color: #94918c; font-style: italic; }
1975
+ .cs-n { color: #9d174d; }
1976
+ .cs-f { color: #1d4ed8; }
1977
+ .cs-t { color: #0369a1; }
1978
+ .cs-p { color: #6d28d9; }
1979
+ .cs-v { color: var(--text-3); }
1980
+
1981
+ :root[data-theme="dark"] .cs-k { color: #5eead4; }
1982
+ :root[data-theme="dark"] .cs-s { color: #fcd34d; }
1983
+ :root[data-theme="dark"] .cs-c { color: #7c8594; }
1984
+ :root[data-theme="dark"] .cs-n { color: #f9a8d4; }
1985
+ :root[data-theme="dark"] .cs-f { color: #93c5fd; }
1986
+ :root[data-theme="dark"] .cs-t { color: #7dd3fc; }
1987
+ :root[data-theme="dark"] .cs-p { color: #c4b5fd; }
1988
+
1989
+ /* ------------------------------------------------------------------------- */
1990
+ /* Composer */
1991
+ /* ------------------------------------------------------------------------- */
1992
+
1993
+ /* The dock is the jump chip's anchor too, so the chip always floats just
1994
+ above the composer, whatever height the composer has grown to. */
1995
+ .composer-dock {
1996
+ position: relative;
1997
+ flex: none;
1998
+ padding: 6px var(--gutter);
1999
+ border-top: 1px solid var(--dock-line);
2000
+ box-shadow: inset 0 1px 0 var(--dock-shine);
2001
+ }
2002
+
2003
+ .composer-inner { max-width: var(--chat-measure); margin: 0 auto; }
2004
+
2005
+ /* The form is inert layout: a row of chips, then the well and the controls
2006
+ beside it. The RECESS is the affordance — the words are typed into a hollow
2007
+ in the glass, and the accent only shows while you are in it. */
2008
+ .composer-form { display: flex; flex-direction: column; gap: 6px; }
2009
+
2010
+ .composer-row { display: flex; align-items: flex-end; gap: 6px; min-width: 0; }
2011
+
2012
+ .composer-well {
2013
+ display: flex;
2014
+ align-items: flex-end;
2015
+ gap: 4px;
2016
+ flex: 1;
2017
+ min-width: 0;
2018
+ padding: 4px 12px 4px 6px;
2019
+ border: 1px solid var(--well-rim);
2020
+ border-radius: 16px;
2021
+ background: var(--well-fill);
2022
+ box-shadow: var(--well-shade);
2023
+ transition: border-color var(--fast) var(--ease), box-shadow var(--fast) var(--ease);
2024
+ }
2025
+ .composer-well:focus-within {
2026
+ border-color: color-mix(in srgb, var(--accent) 55%, var(--well-rim));
2027
+ box-shadow: inset 0 1.5px 3px rgb(0 10 40 / 0.1),
2028
+ 0 0 0 3px color-mix(in srgb, var(--accent) 25%, transparent);
2029
+ }
2030
+
2031
+ .composer-textarea {
2032
+ flex: 1;
2033
+ min-width: 0;
2034
+ min-height: 20px;
2035
+ max-height: 160px;
2036
+ padding: 0;
2037
+ border: 0;
2038
+ background: none;
2039
+ color: var(--text-1);
2040
+ font-size: 13.5px;
2041
+ line-height: 20px;
2042
+ resize: none;
2043
+ overflow-y: auto;
2044
+ }
2045
+ .composer-textarea:focus { outline: none; }
2046
+ .composer-textarea::placeholder { color: var(--text-3); }
2047
+
2048
+ .composer-cluster { display: flex; align-items: center; gap: 2px; height: 30px; flex: none; }
2049
+
2050
+ /* The well's own door sits on the text line, so it is exactly one line
2051
+ tall — a taller one would grow the bar past the window's 43px. */
2052
+ .well-btn {
2053
+ display: grid;
2054
+ place-items: center;
2055
+ width: 20px;
2056
+ height: 20px;
2057
+ flex: none;
2058
+ border-radius: 999px;
2059
+ color: var(--text-3);
2060
+ transition: background-color var(--fast) var(--ease), color var(--fast) var(--ease);
2061
+ }
2062
+ .well-btn:hover { background: var(--row-hover); color: var(--text-1); }
2063
+ .well-btn svg { width: 14px; height: 14px; }
2064
+
2065
+ .model-trigger {
2066
+ display: inline-flex;
2067
+ align-items: center;
2068
+ gap: 6px;
2069
+ min-width: 0;
2070
+ height: 24px;
2071
+ padding: 0 8px;
2072
+ flex: none;
2073
+ border-radius: 999px;
2074
+ color: var(--text-2);
2075
+ font-size: 12px;
2076
+ font-weight: 500;
2077
+ transition: background-color var(--fast) var(--ease), color var(--fast) var(--ease);
2078
+ }
2079
+ .model-trigger:hover:not(:disabled) { background: var(--row-hover); color: var(--text-1); }
2080
+ .model-trigger:disabled { color: var(--text-3); }
2081
+ .model-trigger .label { max-width: 22ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2082
+ .model-trigger .lock, .model-trigger .chev { display: inline-flex; color: var(--text-4); }
2083
+ .model-trigger .lock svg { width: 11px; height: 11px; }
2084
+ .model-trigger .chev svg { width: 12px; height: 12px; }
2085
+
2086
+ .provider-mark { display: inline-flex; width: 14px; height: 14px; flex: none; color: var(--text-2); }
2087
+ .provider-mark svg { width: 14px; height: 14px; }
2088
+ :root[data-theme="dark"] .provider-mark svg[fill="#111827"] { fill: #e7e9ef; }
2089
+ .provider-fallback {
2090
+ display: inline-flex;
2091
+ align-items: center;
2092
+ justify-content: center;
2093
+ width: 16px;
2094
+ height: 16px;
2095
+ border-radius: 5px;
2096
+ background: var(--bg-2);
2097
+ color: var(--text-2);
2098
+ font-size: 8px;
2099
+ font-weight: 700;
2100
+ letter-spacing: 0.02em;
2101
+ flex: none;
2102
+ }
2103
+
2104
+ /* Send: the gel, the one press this page exists for. With nothing to send it
2105
+ is not a button at all — a grey arrow, the way the window's is. */
2106
+ .send-btn {
2107
+ display: inline-flex;
2108
+ align-items: center;
2109
+ justify-content: center;
2110
+ width: 24px;
2111
+ height: 24px;
2112
+ flex: none;
2113
+ border-radius: 999px;
2114
+ box-shadow: 0 1px 2px rgb(10 35 90 / 0.28);
2115
+ transition: filter 220ms var(--ease), transform 220ms var(--ease), opacity 220ms var(--ease);
2116
+ }
2117
+ .send-btn:hover:not(:disabled) { filter: brightness(1.05); }
2118
+ .send-btn:active:not(:disabled) { filter: brightness(0.95); transform: scale(0.94); }
2119
+ .send-btn:disabled {
2120
+ background-image: none;
2121
+ border-color: transparent;
2122
+ box-shadow: none;
2123
+ color: var(--text-4);
2124
+ }
2125
+ .send-btn:focus-visible { outline: none; box-shadow: 0 1px 2px rgb(10 35 90 / 0.28), var(--ring); }
2126
+ .send-btn svg { width: 14px; height: 14px; }
2127
+
2128
+ .stop-square {
2129
+ width: 8px;
2130
+ height: 8px;
2131
+ border-radius: 2px;
2132
+ background: currentColor;
2133
+ }
2134
+
2135
+ .attach-chips { display: flex; flex-wrap: wrap; gap: 6px; padding: 0 2px; }
2136
+ .attach-chips[hidden] { display: none; }
2137
+
2138
+ .attach-chip {
2139
+ display: inline-flex;
2140
+ align-items: center;
2141
+ gap: 6px;
2142
+ height: 24px;
2143
+ padding: 0 3px 0 8px;
2144
+ border-radius: 999px;
2145
+ background: var(--chip-fill);
2146
+ font-size: 11.5px;
2147
+ font-weight: 500;
2148
+ color: var(--chip-ink);
2149
+ }
2150
+ .attach-chip img { width: 20px; height: 20px; border-radius: 999px; object-fit: cover; }
2151
+ .attach-chip svg { width: 13px; height: 13px; color: var(--text-3); }
2152
+ .attach-chip .name { max-width: 18ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2153
+ .attach-chip .remove {
2154
+ display: inline-flex;
2155
+ align-items: center;
2156
+ justify-content: center;
2157
+ width: 20px;
2158
+ height: 20px;
2159
+ border-radius: 999px;
2160
+ color: var(--text-4);
2161
+ }
2162
+ .attach-chip .remove:hover { background: var(--bg-2); color: var(--text-1); }
2163
+ .attach-chip .remove svg { width: 10px; height: 10px; }
2164
+
2165
+ .jump-chip {
2166
+ position: absolute;
2167
+ left: 50%;
2168
+ bottom: calc(100% + 4px);
2169
+ translate: -50% 0;
2170
+ display: inline-flex;
2171
+ align-items: center;
2172
+ justify-content: center;
2173
+ width: 34px;
2174
+ height: 34px;
2175
+ border-radius: 999px;
2176
+ background: var(--paper);
2177
+ box-shadow: 0 0 0 1px var(--line-2), 0 4px 12px -6px rgb(8 28 74 / 0.25);
2178
+ color: var(--text-2);
2179
+ z-index: 3;
2180
+ }
2181
+ .jump-chip[hidden] { display: none; }
2182
+ .jump-chip:hover { color: var(--text-1); }
2183
+ .jump-chip svg { width: 16px; height: 16px; }
2184
+
2185
+ /* ------------------------------------------------------------------------- */
2186
+ /* Right well: settings */
2187
+ /* ------------------------------------------------------------------------- */
2188
+
2189
+ .lane-col.right .lane-head { justify-content: space-between; min-height: 50px; padding: 12px 18px 0; }
2190
+ /* Light | dark is a preference of this page, so it sits on the Settings
2191
+ title line, in the same column as the section doors below it. */
2192
+ .theme-btn { margin-top: 2px; }
2193
+
2194
+ .well-title {
2195
+ margin: 0;
2196
+ font-family: var(--font-brand);
2197
+ font-size: 25px;
2198
+ font-weight: 600;
2199
+ line-height: 1.2;
2200
+ letter-spacing: -0.012em;
2201
+ color: var(--text-1);
2202
+ }
2203
+
2204
+ .inspector-body { flex: 1; min-height: 0; padding: 0 18px 24px; }
2205
+
2206
+ .inspector-section { padding: 16px 0 2px; }
2207
+
2208
+ /* A section is titled the way the admin console titles one: the serif at
2209
+ 17px, with its actions on the title line. */
2210
+ .sw-heading {
2211
+ font-family: var(--font-brand);
2212
+ font-size: 17px;
2213
+ font-weight: 600;
2214
+ line-height: 1.25;
2215
+ letter-spacing: -0.005em;
2216
+ color: var(--text-1);
2217
+ }
2218
+ .inspector-section > .sw-heading { display: block; margin-bottom: 6px; }
2219
+
2220
+ .sw-heading-row {
2221
+ display: flex;
2222
+ align-items: center;
2223
+ justify-content: space-between;
2224
+ gap: 12px;
2225
+ min-height: 28px;
2226
+ margin-bottom: 6px;
2227
+ }
2228
+ .sw-heading-row .sw-heading { display: inline; margin: 0; }
2229
+
2230
+ /* A title with an ⓘ: the glyph sits right against the word it explains,
2231
+ quieter than the title, and wears the accent while its window is up. */
2232
+ .sw-title { position: relative; display: inline-flex; align-items: center; gap: 2px; min-width: 0; }
2233
+ .info-wrap { display: inline-flex; }
2234
+ .info-door {
2235
+ display: inline-flex;
2236
+ align-items: center;
2237
+ justify-content: center;
2238
+ width: 22px;
2239
+ height: 22px;
2240
+ flex: none;
2241
+ border-radius: 999px;
2242
+ color: var(--text-3);
2243
+ transition: background-color var(--fast) var(--ease), color var(--fast) var(--ease);
2244
+ }
2245
+ .info-door svg { width: 14px; height: 14px; }
2246
+ .info-wrap:hover .info-door,
2247
+ .info-door:focus-visible { background: var(--bg-2); color: var(--accent-text); }
2248
+
2249
+ /* What an ⓘ means, in a small window that floats under the title while the
2250
+ ⓘ is hovered or has keyboard focus — over the settings below, never pushing
2251
+ them down. The model picker's popover material, over a blur: it lands on
2252
+ the settings' own words, which must not read through its sentences. It
2253
+ waits a beat before it shows, so a pointer passing by does not flash it,
2254
+ and lingers a moment when left, so the pointer can travel onto it. */
2255
+ .info-tip {
2256
+ position: absolute;
2257
+ top: calc(100% + 8px);
2258
+ left: 0;
2259
+ z-index: 20;
2260
+ width: min(272px, calc(var(--inspector-w) - 37px));
2261
+ padding: 10px 12px;
2262
+ border-radius: 12px;
2263
+ background: var(--pop-fill);
2264
+ -webkit-backdrop-filter: blur(24px) saturate(180%);
2265
+ backdrop-filter: blur(24px) saturate(180%);
2266
+ box-shadow: var(--pop-throw);
2267
+ text-align: left;
2268
+ opacity: 0;
2269
+ visibility: hidden;
2270
+ translate: 0 -3px;
2271
+ transition: opacity 120ms var(--ease), translate 160ms var(--ease-out-strong), visibility 0s linear;
2272
+ transition-delay: 80ms, 80ms, 240ms;
2273
+ }
2274
+ /* The gap under the ⓘ belongs to the window, so crossing it keeps it up. */
2275
+ .info-tip::before {
2276
+ content: "";
2277
+ position: absolute;
2278
+ left: 0;
2279
+ right: 0;
2280
+ bottom: 100%;
2281
+ height: 10px;
2282
+ }
2283
+ .info-wrap:hover .info-tip,
2284
+ .info-door:focus-visible + .info-tip {
2285
+ opacity: 1;
2286
+ visibility: visible;
2287
+ translate: 0 0;
2288
+ transition-delay: 150ms;
2289
+ }
2290
+ /* Escape closes it until the pointer or focus comes back. */
2291
+ .info-wrap[data-dismissed] .info-tip {
2292
+ opacity: 0;
2293
+ visibility: hidden;
2294
+ transition-delay: 0s;
2295
+ }
2296
+ .info-tip p {
2297
+ margin: 0;
2298
+ font-size: var(--text-sm);
2299
+ line-height: 1.45;
2300
+ color: var(--text-2);
2301
+ }
2302
+ .info-tip p + p { margin-top: 6px; }
2303
+ .info-tip b { font-weight: 600; color: var(--text-1); }
2304
+
2305
+ /* Rows: one setting per line — its name on the left, where it stands on the
2306
+ right, a hairline between neighbours, and nothing under it. */
2307
+ .rows {
2308
+ border-top: 1px solid var(--line-2);
2309
+ border-bottom: 1px solid var(--line-2);
2310
+ }
2311
+ .rows > * + * { border-top: 1px solid var(--line-2); }
2312
+
2313
+ .setting-row {
2314
+ display: flex;
2315
+ align-items: center;
2316
+ gap: 10px;
2317
+ min-height: 40px;
2318
+ padding: 4px 0;
2319
+ }
2320
+ .setting-row .meta { flex: 1; min-width: 0; display: flex; align-items: center; gap: 8px; }
2321
+ .setting-row .label {
2322
+ min-width: 0;
2323
+ font-size: var(--text-base);
2324
+ font-weight: 500;
2325
+ color: var(--text-1);
2326
+ overflow: hidden;
2327
+ text-overflow: ellipsis;
2328
+ white-space: nowrap;
2329
+ }
2330
+ .setting-row .label.mono { font-family: var(--font-code); font-size: 12.5px; font-weight: 400; }
2331
+ .setting-row.off .label { color: var(--text-3); }
2332
+
2333
+ /* A row that opens a picker states its setting on the right, with the door
2334
+ that changes it — the whole line is the button. */
2335
+ .setting-row.pick {
2336
+ width: 100%;
2337
+ text-align: left;
2338
+ color: inherit;
2339
+ }
2340
+ .setting-row.pick:hover .value-chip { color: var(--text-1); }
2341
+ .setting-row.pick:hover .value-chip svg { color: var(--text-2); }
2342
+
2343
+ .value-chip {
2344
+ display: inline-flex;
2345
+ align-items: center;
2346
+ gap: 6px;
2347
+ min-width: 0;
2348
+ max-width: 66%;
2349
+ flex: none;
2350
+ color: var(--text-3);
2351
+ font-size: var(--text-base);
2352
+ transition: color var(--fast) var(--ease);
2353
+ }
2354
+ .value-chip .name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2355
+ .value-chip > svg { width: 12px; height: 12px; color: var(--text-4); flex: none; transition: color var(--fast) var(--ease); }
2356
+
2357
+ /* Which models the agent runs on: the switch on the title line, then what it
2358
+ implies. The rows settle in when the switch has finished moving. */
2359
+ .rows[data-settle] { animation: settle 220ms var(--ease-out-strong) both; }
2360
+ @keyframes settle {
2361
+ from { opacity: 0; translate: 0 -4px; }
2362
+ to { opacity: 1; translate: 0 0; }
2363
+ }
2364
+
2365
+ /* A model line puts its maker's mark in one column, whether that mark is a
2366
+ logo or the two-letter stand-in, so the names start on one edge. */
2367
+ .model-row .meta > .provider-mark,
2368
+ .model-row .meta > .provider-fallback,
2369
+ .add-row > .provider-mark,
2370
+ .add-row > .provider-fallback {
2371
+ width: 18px;
2372
+ height: 18px;
2373
+ justify-content: center;
2374
+ flex: none;
2375
+ }
2376
+
2377
+ /* Which models the agent offers is a list you TICK: one line per model, the
2378
+ whole line is the control, and the box stands in the same column as the
2379
+ switches below. */
2380
+ .model-row { width: 100%; min-height: 36px; text-align: left; color: inherit; }
2381
+ .model-row.off .label { color: var(--text-2); }
2382
+ .model-row[aria-disabled="true"] { cursor: default; }
2383
+ .check-box {
2384
+ display: inline-flex;
2385
+ align-items: center;
2386
+ justify-content: center;
2387
+ width: 19px;
2388
+ height: 19px;
2389
+ flex: none;
2390
+ border-radius: 6px;
2391
+ box-shadow: inset 0 0 0 1.5px var(--line-1);
2392
+ color: transparent;
2393
+ transition: background-color var(--fast) var(--ease), box-shadow var(--fast) var(--ease),
2394
+ color var(--fast) var(--ease);
2395
+ }
2396
+ .check-box svg { width: 12px; height: 12px; }
2397
+ .model-row:hover:not([aria-disabled="true"]) .check-box { box-shadow: inset 0 0 0 1.5px var(--text-4); }
2398
+ .model-row[aria-checked="true"] .check-box,
2399
+ .model-row[aria-checked="true"]:hover .check-box {
2400
+ background: var(--accent);
2401
+ box-shadow: none;
2402
+ color: #ffffff;
2403
+ }
2404
+ .model-row[aria-disabled="true"][aria-checked="true"] .check-box { background: var(--text-4); }
2405
+ .model-row:focus-visible { outline: none; }
2406
+ .model-row:focus-visible .check-box {
2407
+ box-shadow: inset 0 0 0 1.5px var(--accent-line), 0 0 0 3px color-mix(in srgb, var(--accent) 13%, transparent);
2408
+ }
2409
+
2410
+ /* The filter over a long catalog sits between the switch and the rows. */
2411
+ .search-pill.model-filter { margin: 0 0 8px; }
2412
+
2413
+ /* What the agent already has, as a GRID of tiles — tools, skills, services
2414
+ and agents are many, and one row each would bury every section below.
2415
+ Tiles are glass things on the column's glass: a hairline rim, one line of
2416
+ name, the maker's glyph, and a door that takes the thing away. The grid
2417
+ fills the column, two across at its default width, more when dragged. */
2418
+ .cap-grid {
2419
+ display: grid;
2420
+ grid-template-columns: repeat(auto-fill, minmax(132px, 1fr));
2421
+ gap: 1px 6px;
2422
+ margin: 0 -4px;
2423
+ }
2424
+ /* Not cards: a card each would make the column a wall of boxes. A line with
2425
+ its glyph, and the ground under it only when the cursor is there. */
2426
+ .cap-tile {
2427
+ position: relative;
2428
+ display: flex;
2429
+ align-items: center;
2430
+ gap: 7px;
2431
+ min-width: 0;
2432
+ height: 28px;
2433
+ padding: 0 4px 0 4px;
2434
+ border-radius: 6px;
2435
+ color: var(--text-1);
2436
+ font-size: 12px;
2437
+ transition: background-color var(--fast) var(--ease);
2438
+ }
2439
+ .cap-tile:hover, .cap-tile:focus-within { background: var(--row-hover); }
2440
+ .cap-glyph {
2441
+ display: grid;
2442
+ place-items: center;
2443
+ width: 16px;
2444
+ height: 16px;
2445
+ flex: none;
2446
+ color: var(--text-3);
2447
+ }
2448
+ .cap-glyph svg { width: 14px; height: 14px; }
2449
+ .cap-glyph .orb { width: 9px; height: 9px; }
2450
+ /* The same mark the window's gallery paints, at a row's size: no plate of
2451
+ its own here, because a column of plates is a wall of boxes. */
2452
+ .cap-glyph .thing-mark {
2453
+ width: 17px;
2454
+ height: 17px;
2455
+ border-radius: 4px;
2456
+ background: transparent;
2457
+ box-shadow: none;
2458
+ }
2459
+ .cap-glyph .thing-mark img { width: 100%; height: 100%; }
2460
+ .cap-glyph .thing-mark.plate img { width: 14px; height: 14px; }
2461
+ .cap-glyph .thing-mark svg { width: 14px; height: 14px; }
2462
+ .cap-glyph .thing-mark .mark-letters { font-size: 8px; }
2463
+ .cap-glyph .thing-mark.bare { background: var(--bg-3); box-shadow: none; }
2464
+ /* The tile's own door, under its name and glyph — it opens the thing's page
2465
+ in the window, where the grant is narrowed. The drop door rides above it. */
2466
+ .cap-open {
2467
+ position: absolute;
2468
+ inset: 0;
2469
+ z-index: 1;
2470
+ border-radius: 6px;
2471
+ cursor: pointer;
2472
+ }
2473
+ .cap-open:focus-visible { outline: none; box-shadow: var(--ring); }
2474
+ .cap-tile > .cap-name,
2475
+ .cap-tile > .cap-count,
2476
+ .cap-tile > .orb { pointer-events: none; }
2477
+ .cap-name {
2478
+ flex: 1;
2479
+ min-width: 0;
2480
+ overflow: hidden;
2481
+ text-overflow: ellipsis;
2482
+ white-space: nowrap;
2483
+ }
2484
+ .cap-name.mono { font-family: var(--font-code); font-size: 11.5px; }
2485
+ .cap-count {
2486
+ flex: none;
2487
+ color: var(--text-3);
2488
+ font-size: 11px;
2489
+ font-variant-numeric: tabular-nums;
2490
+ }
2491
+ .cap-door {
2492
+ position: relative;
2493
+ z-index: 2;
2494
+ display: grid;
2495
+ place-items: center;
2496
+ width: 20px;
2497
+ height: 20px;
2498
+ flex: none;
2499
+ border-radius: 999px;
2500
+ color: var(--text-4);
2501
+ transition: background-color var(--fast) var(--ease), color var(--fast) var(--ease), opacity var(--fast) var(--ease);
2502
+ }
2503
+ .cap-door svg { width: 11px; height: 11px; }
2504
+ .cap-door:hover { background: var(--row-hover); color: var(--text-1); }
2505
+ .cap-door.drop:hover { background: color-mix(in srgb, var(--danger) 12%, transparent); color: var(--danger); }
2506
+ /* The drop door is there for the cursor; at rest the tile is just the thing,
2507
+ and its name has the whole width. The door floats over the tile's end on a
2508
+ wash of the tile, so it never takes room a name could use. Without a cursor
2509
+ it always stands. */
2510
+ @media (hover: hover) {
2511
+ .cap-tile .cap-door.drop {
2512
+ position: absolute;
2513
+ top: 50%;
2514
+ right: 2px;
2515
+ translate: 0 -50%;
2516
+ opacity: 0;
2517
+ }
2518
+ .cap-tile:hover .cap-door.drop,
2519
+ .cap-tile:focus-within .cap-door.drop { opacity: 1; }
2520
+ }
2521
+ /* A section's grid can hold two kinds — the system's lanes and the
2522
+ connectors — told apart by a quiet word above each. */
2523
+ .cap-group, .add-group {
2524
+ margin: 10px 0 5px;
2525
+ color: var(--text-3);
2526
+ font-size: 11px;
2527
+ font-weight: 500;
2528
+ line-height: 16px;
2529
+ }
2530
+ .cap-group:first-of-type { margin-top: 2px; }
2531
+ .add-group { margin: 6px 8px 2px; }
2532
+ .add-group:first-child { margin-top: 2px; }
2533
+ .cap-tile.leaving { opacity: 0.4; transition: opacity var(--fast) var(--ease); }
2534
+ /* The section's one switch, in the row grammar: whether the agent may go
2535
+ looking in the Store beyond what it is given. */
2536
+ .store-row {
2537
+ margin-bottom: 2px;
2538
+ border-top: 1px solid var(--line-2);
2539
+ border-bottom: 1px solid var(--line-2);
2540
+ }
2541
+ .cap-empty {
2542
+ padding: 4px 0;
2543
+ color: var(--text-3);
2544
+ font-size: 12px;
2545
+ line-height: 18px;
2546
+ }
2547
+
2548
+ /* The Switch (components/Switch.tsx, sm): a 32×18 track, a 14px thumb, and
2549
+ Transitions.dev's double bounce armed by `is-init` on the thumb. */
2550
+ .t-toggle {
2551
+ --toggle-dur: 350ms;
2552
+ --toggle-ov1: 1px;
2553
+ --toggle-ov2: 0px;
2554
+ --toggle-ease: cubic-bezier(0.34, 1.35, 0.64, 1);
2555
+ --toggle-travel: 14px; /* 32 − 4 − 14 */
2556
+ position: relative;
2557
+ display: inline-flex;
2558
+ align-items: center;
2559
+ flex: none;
2560
+ width: 32px;
2561
+ height: 18px;
2562
+ padding: 0 2px;
2563
+ border-radius: 999px;
2564
+ background: var(--bg-1);
2565
+ }
2566
+ .t-toggle:hover:not(:disabled) { background: color-mix(in srgb, var(--ink) 18%, transparent); }
2567
+ .t-toggle[data-on="true"],
2568
+ .t-toggle[data-on="true"]:hover:not(:disabled) { background: var(--accent); }
2569
+ .t-toggle:focus-visible { outline: none; box-shadow: var(--ring); }
2570
+ .t-toggle-thumb {
2571
+ display: inline-block;
2572
+ width: 14px;
2573
+ height: 14px;
2574
+ border-radius: 999px;
2575
+ background: #ffffff;
2576
+ box-shadow: 0 1px 2px rgb(9 9 11 / 0.25);
2577
+ translate: 0 0;
2578
+ will-change: translate;
2579
+ pointer-events: none;
2580
+ }
2581
+ .t-toggle[data-on="true"] .t-toggle-thumb { translate: var(--toggle-travel) 0; }
2582
+ .t-toggle[data-on="true"] .t-toggle-thumb.is-init {
2583
+ animation: t-toggle-on var(--toggle-dur) var(--toggle-ease) both;
2584
+ }
2585
+ .t-toggle[data-on="false"] .t-toggle-thumb.is-init {
2586
+ animation: t-toggle-off var(--toggle-dur) var(--toggle-ease) both;
2587
+ }
2588
+ @keyframes t-toggle-on {
2589
+ 0% { translate: 0 0; }
2590
+ 55% { translate: calc(var(--toggle-travel) + var(--toggle-ov1)) 0; }
2591
+ 80% { translate: calc(var(--toggle-travel) - var(--toggle-ov2)) 0; }
2592
+ 100% { translate: var(--toggle-travel) 0; }
2593
+ }
2594
+ @keyframes t-toggle-off {
2595
+ 0% { translate: var(--toggle-travel) 0; }
2596
+ 55% { translate: calc(0px - var(--toggle-ov1)) 0; }
2597
+ 80% { translate: var(--toggle-ov2) 0; }
2598
+ 100% { translate: 0 0; }
2599
+ }
2600
+
2601
+ /* Schedules: the same rows. A schedule says its cadence; the facts behind it
2602
+ (name, zone, the wake's words) ride in the hover title and in the rows the
2603
+ disclosure opens, never as a caption under it. */
2604
+ .sched-item.open { padding-bottom: 8px; }
2605
+ .sched-row {
2606
+ display: flex;
2607
+ align-items: center;
2608
+ gap: 6px;
2609
+ min-height: 40px;
2610
+ padding: 4px 0;
2611
+ border-radius: var(--radius-row);
2612
+ }
2613
+ .sched-row.editable { cursor: pointer; }
2614
+ .sched-row .meta {
2615
+ flex: 1;
2616
+ min-width: 0;
2617
+ display: flex;
2618
+ flex-direction: column;
2619
+ gap: 1px;
2620
+ }
2621
+ .sched-row .cadence {
2622
+ font-size: var(--text-base);
2623
+ font-weight: 500;
2624
+ color: var(--text-1);
2625
+ white-space: nowrap;
2626
+ overflow: hidden;
2627
+ text-overflow: ellipsis;
2628
+ }
2629
+ .sched-item.disabled .cadence { color: var(--text-3); }
2630
+ /* A webhook's local ingress is data you copy, so it keeps its line. */
2631
+ .sched-row .sub.mono {
2632
+ font-family: var(--font-code);
2633
+ font-size: var(--text-xs);
2634
+ color: var(--text-3);
2635
+ user-select: all;
2636
+ overflow-wrap: anywhere;
2637
+ }
2638
+ .sched-row .glyph { display: inline-flex; color: var(--text-3); flex: none; }
2639
+ /* The door to a schedule's own settings: a gear, the grammar's door for "a
2640
+ small editor for this value", sitting BEFORE the switch so every switch in
2641
+ the well stands in one column at the right edge. */
2642
+ .sched-row .glyph.door {
2643
+ align-items: center;
2644
+ justify-content: center;
2645
+ width: 28px;
2646
+ height: 28px;
2647
+ border-radius: 999px;
2648
+ color: var(--text-4);
2649
+ transition: background-color var(--fast) var(--ease), color var(--fast) var(--ease),
2650
+ rotate var(--normal) var(--ease-out-strong);
2651
+ }
2652
+ .sched-row .glyph.door svg { width: 15px; height: 15px; }
2653
+ .sched-row.editable:hover .glyph.door { background: var(--bg-3); color: var(--text-1); }
2654
+ .sched-row .glyph.door.open { background: var(--bg-2); color: var(--accent-text); }
2655
+ /* Run now earns its place quietly: on hover, on focus, and while open. */
2656
+ .sched-row [data-fire-trigger] { opacity: 0; }
2657
+ .sched-row:hover [data-fire-trigger],
2658
+ .sched-row [data-fire-trigger]:focus-visible,
2659
+ .sched-item.open [data-fire-trigger] { opacity: 1; }
2660
+ .sched-row .badge.off {
2661
+ flex: none;
2662
+ padding: 1px 8px;
2663
+ border-radius: 999px;
2664
+ background: var(--bg-3);
2665
+ font-size: var(--text-xs);
2666
+ color: var(--text-3);
2667
+ }
2668
+
2669
+ /* An open schedule: its own rows on a faint recess, so the group reads as one
2670
+ thing without a border around it. */
2671
+ /* The open editor: one decision per row, each row carrying the control
2672
+ itself — nothing here opens a further thing to set. */
2673
+ .sched-editor {
2674
+ display: flex;
2675
+ flex-direction: column;
2676
+ padding: 2px 12px 10px;
2677
+ border-radius: 12px;
2678
+ background: var(--bg-4);
2679
+ box-shadow: inset 0 0 0 1px var(--line-3);
2680
+ }
2681
+ .sched-editor > * + * { border-top: 1px solid var(--line-3); }
2682
+ .sched-field { min-height: 38px; padding-block: 3px; }
2683
+ .sched-field .label { font-weight: 400; color: var(--text-2); }
2684
+ .sched-field .value-chip {
2685
+ gap: 6px;
2686
+ max-width: 68%;
2687
+ flex: 1;
2688
+ justify-content: flex-end;
2689
+ color: var(--text-1);
2690
+ }
2691
+ .sched-field .sw-field { width: auto; height: 30px; }
2692
+ .sched-field .sw-field.grow { flex: 1; min-width: 0; }
2693
+ /* A field that holds a url or an expression gets the room a value does not. */
2694
+ .sched-field:has(.grow) .value-chip { max-width: 78%; }
2695
+ .sched-field .sw-field.mono { font-family: var(--font-code); font-size: 12.5px; }
2696
+ .sched-field .sw-field.num { width: 62px; text-align: center; }
2697
+ .sched-field .sw-field.time { font-variant-numeric: tabular-nums; }
2698
+ .sched-field .unit { font-size: var(--text-sm); color: var(--text-3); flex: none; }
2699
+
2700
+ /* A menu of plain words, wearing the value chip's clothes: the OS draws the
2701
+ list, the row only says what is chosen. */
2702
+ .sw-select {
2703
+ position: relative;
2704
+ display: inline-flex;
2705
+ align-items: center;
2706
+ gap: 4px;
2707
+ min-width: 0;
2708
+ max-width: 100%;
2709
+ height: 30px;
2710
+ padding: 0 8px 0 10px;
2711
+ border-radius: var(--radius-row);
2712
+ background: var(--field-fill);
2713
+ color: var(--text-1);
2714
+ font-size: var(--text-sm);
2715
+ transition: background-color var(--fast) var(--ease), box-shadow var(--fast) var(--ease);
2716
+ }
2717
+ .sw-select select {
2718
+ appearance: none;
2719
+ min-width: 0;
2720
+ max-width: 100%;
2721
+ background: none;
2722
+ border: 0;
2723
+ color: inherit;
2724
+ font-size: inherit;
2725
+ text-overflow: ellipsis;
2726
+ }
2727
+ .sw-select svg { width: 12px; height: 12px; color: var(--text-4); flex: none; }
2728
+ .sw-select:hover { background: var(--field-fill-focus); }
2729
+ .sw-select:focus-within {
2730
+ background: var(--field-fill-focus);
2731
+ box-shadow: inset 0 0 0 0.5px var(--accent-line), var(--ring);
2732
+ }
2733
+ .sw-select select:focus { outline: none; }
2734
+
2735
+ /* What the wake says, in its own field — the width of the editor, because it
2736
+ is sentences and not a value. */
2737
+ .sched-prompt { padding: 8px 0 2px; }
2738
+ .sched-prompt-input {
2739
+ width: 100%;
2740
+ height: auto;
2741
+ min-height: 66px;
2742
+ padding: 8px 10px;
2743
+ resize: vertical;
2744
+ font-family: inherit;
2745
+ font-size: var(--text-sm);
2746
+ line-height: 1.5;
2747
+ }
2748
+
2749
+ /* Removal is the last line of the editor, in words — never a glyph sitting
2750
+ beside the switch, where it could be hit for one. */
2751
+ .sched-delete {
2752
+ display: flex;
2753
+ align-items: center;
2754
+ gap: 7px;
2755
+ width: 100%;
2756
+ min-height: 36px;
2757
+ padding: 0;
2758
+ color: var(--text-3);
2759
+ font-size: var(--text-sm);
2760
+ transition: color var(--fast) var(--ease);
2761
+ }
2762
+ .sched-delete svg { width: 14px; height: 14px; }
2763
+ .sched-delete:hover { color: var(--danger); }
2764
+
2765
+ .pill-btn {
2766
+ display: inline-flex;
2767
+ align-items: center;
2768
+ justify-content: center;
2769
+ height: var(--h-sm);
2770
+ padding: 0 13px;
2771
+ flex: none;
2772
+ border-radius: 999px;
2773
+ font-size: var(--text-sm);
2774
+ font-weight: 600;
2775
+ white-space: nowrap;
2776
+ }
2777
+
2778
+ /* Adding, in place ----------------------------------------------------------
2779
+ The + on a section's title line is the door. Opened, it turns into the x
2780
+ that closes what it opened, and the panel grows out from under the title —
2781
+ pushing the rows down, never landing on top of them — so what the agent
2782
+ already has stays in sight while you add to it. */
2783
+ .add-door svg { transition: rotate var(--normal) var(--ease-out-strong); }
2784
+ .add-door[aria-expanded="true"],
2785
+ .add-door[data-open="true"] { background: var(--bg-2); color: var(--accent-text); }
2786
+ .add-door[aria-expanded="true"] svg { rotate: 45deg; }
2787
+
2788
+ /* The reveal is the grid track growing from 0fr, so the rows below slide
2789
+ rather than jump; the panel's contents rise a few pixels into place. */
2790
+ .add-panel {
2791
+ display: grid;
2792
+ grid-template-rows: 0fr;
2793
+ transition: grid-template-rows 240ms var(--ease-out-strong);
2794
+ }
2795
+ .add-panel[data-open="true"] { grid-template-rows: 1fr; }
2796
+ .add-clip { min-height: 0; overflow: hidden; }
2797
+ /* A faint recess, the same one an open schedule's rows sit in: an open thing
2798
+ in this column is set INTO it, not raised off it. */
2799
+ .add-inner {
2800
+ margin: 0 0 10px;
2801
+ padding: 6px;
2802
+ border-radius: 12px;
2803
+ background: var(--bg-4);
2804
+ box-shadow: inset 0 0 0 1px var(--line-3);
2805
+ opacity: 0;
2806
+ translate: 0 -6px;
2807
+ transition: opacity 140ms var(--ease), translate 240ms var(--ease-out-strong);
2808
+ }
2809
+ .add-panel[data-open="true"] .add-inner {
2810
+ opacity: 1;
2811
+ translate: 0 0;
2812
+ transition-delay: 40ms;
2813
+ }
2814
+
2815
+ /* In the recess the field is paper with a hairline — a hollow inside a hollow
2816
+ would disappear. */
2817
+ .search-pill.add-search {
2818
+ background: var(--paper);
2819
+ box-shadow: inset 0 0 0 1px var(--line-2);
2820
+ }
2821
+ .search-pill.add-search:focus-within {
2822
+ background: var(--paper);
2823
+ box-shadow: inset 0 0 0 1px var(--accent-line),
2824
+ 0 0 0 3px color-mix(in srgb, var(--accent) 13%, transparent);
2825
+ }
2826
+ .add-busy { display: inline-flex; width: 14px; height: 14px; flex: none; }
2827
+ .add-busy[hidden] { display: none; }
2828
+ .add-busy svg { width: 14px; height: 14px; animation: spin 0.9s linear infinite; }
2829
+
2830
+ .add-list {
2831
+ display: flex;
2832
+ flex-direction: column;
2833
+ gap: 1px;
2834
+ max-height: 248px;
2835
+ overflow-y: auto;
2836
+ }
2837
+ .add-search + .add-list:not(:empty) { margin-top: 6px; }
2838
+
2839
+ /* One line per candidate: its name, where it comes from, and a round door. */
2840
+ .add-row {
2841
+ display: flex;
2842
+ align-items: center;
2843
+ gap: 8px;
2844
+ width: 100%;
2845
+ min-height: 34px;
2846
+ padding: 0 5px 0 8px;
2847
+ flex: none;
2848
+ border-radius: var(--radius-row);
2849
+ text-align: left;
2850
+ color: var(--text-1);
2851
+ transition: background-color var(--fast) var(--ease);
2852
+ }
2853
+ .add-row:hover { background: var(--bg-3); }
2854
+ .add-row:focus-visible {
2855
+ outline: none;
2856
+ background: var(--bg-3);
2857
+ box-shadow: inset 0 0 0 1px var(--accent-line);
2858
+ }
2859
+ .add-row .glyph {
2860
+ display: inline-flex;
2861
+ justify-content: center;
2862
+ width: 16px;
2863
+ flex: none;
2864
+ color: var(--text-3);
2865
+ }
2866
+ .add-row .glyph svg { width: 15px; height: 15px; }
2867
+ .add-row .name {
2868
+ flex: 1;
2869
+ min-width: 0;
2870
+ overflow: hidden;
2871
+ text-overflow: ellipsis;
2872
+ white-space: nowrap;
2873
+ font-size: var(--text-base);
2874
+ font-weight: 500;
2875
+ }
2876
+ .add-row .name.mono { font-family: var(--font-code); font-size: 12.5px; font-weight: 400; }
2877
+ .add-row .value {
2878
+ flex: 0 1 auto;
2879
+ max-width: 50%;
2880
+ overflow: hidden;
2881
+ text-overflow: ellipsis;
2882
+ white-space: nowrap;
2883
+ font-size: var(--text-sm);
2884
+ color: var(--text-3);
2885
+ }
2886
+ .add-row .value.mono { font-family: var(--font-code); font-size: var(--text-xs); }
2887
+ .add-row .door {
2888
+ display: inline-flex;
2889
+ align-items: center;
2890
+ justify-content: center;
2891
+ width: 24px;
2892
+ height: 24px;
2893
+ flex: none;
2894
+ border-radius: 999px;
2895
+ color: var(--accent-text);
2896
+ transition: background-color var(--fast) var(--ease), color var(--fast) var(--ease);
2897
+ }
2898
+ .add-row .door svg { width: 14px; height: 14px; }
2899
+ .add-row:hover .door { background: var(--accent-soft); }
2900
+ /* Already in: the row stays where it was — a list that reshuffles under the
2901
+ cursor loses your place — and its door becomes a quiet check. */
2902
+ .add-row[aria-disabled="true"] { cursor: default; }
2903
+ .add-row[aria-disabled="true"]:hover { background: none; }
2904
+ .add-row[aria-disabled="true"] .name { color: var(--text-3); }
2905
+ .add-row[aria-disabled="true"] .door,
2906
+ .add-row[aria-disabled="true"]:hover .door { background: none; color: var(--text-3); }
2907
+ .add-row.empty { cursor: default; }
2908
+ .add-row.empty:hover { background: none; }
2909
+ .add-row.empty .name { font-weight: 400; color: var(--text-3); }
2910
+
2911
+ /* A newcomer arrives with a wash of the accent that fades where it landed. */
2912
+ [data-arrive] { animation: arrive 1600ms var(--ease) both; }
2913
+ @keyframes arrive {
2914
+ 0%, 30% { background-color: color-mix(in srgb, var(--accent) 12%, transparent); }
2915
+ 100% { background-color: transparent; }
2916
+ }
2917
+
2918
+ /* ---------------------------------------------------------------------------
2919
+ The add window
2920
+ ---------------------------------------------------------------------------
2921
+ Searching a catalog is a place of its own, so it opens the way the OS view
2922
+ opens one: a real window over the page — glass with a 26px bar, the three
2923
+ lights at its left, dragged by that bar. The column behind it does not
2924
+ move, and what you add appears there while it is still open. */
2925
+ .os-window {
2926
+ position: fixed;
2927
+ z-index: 60;
2928
+ display: flex;
2929
+ flex-direction: column;
2930
+ /* A gallery needs a gallery's room: three tiles across at rest, more when
2931
+ the page is wide. The Store window learned the same thing. */
2932
+ width: min(720px, calc(100vw - 32px));
2933
+ height: min(520px, calc(100vh - 110px));
2934
+ overflow: hidden;
2935
+ border: 1px solid var(--window-rim);
2936
+ border-radius: 15.6px;
2937
+ background-image: linear-gradient(to bottom,
2938
+ color-mix(in srgb, var(--window-glass) 78%, transparent), var(--window-glass));
2939
+ -webkit-backdrop-filter: blur(26px) saturate(185%);
2940
+ backdrop-filter: blur(26px) saturate(185%);
2941
+ box-shadow: 0 18px 50px rgb(0 0 0 / 0.28), 0 2px 10px rgb(0 0 0 / 0.12),
2942
+ inset 0 1px 0 rgb(255 255 255 / 0.4);
2943
+ animation: window-in 180ms var(--ease-out-strong) both;
2944
+ }
2945
+ .os-window[hidden] { display: none; }
2946
+ @keyframes window-in {
2947
+ from { opacity: 0; transform: translateY(6px) scale(0.985); }
2948
+ to { opacity: 1; transform: none; }
2949
+ }
2950
+
2951
+ .os-titlebar {
2952
+ display: flex;
2953
+ align-items: center;
2954
+ gap: 8px;
2955
+ height: 26px;
2956
+ padding: 0 8px;
2957
+ flex: none;
2958
+ cursor: grab;
2959
+ user-select: none;
2960
+ }
2961
+ .os-titlebar:active { cursor: grabbing; }
2962
+ .os-title {
2963
+ flex: 1;
2964
+ min-width: 0;
2965
+ text-align: center;
2966
+ font-size: 12.5px;
2967
+ font-weight: 600;
2968
+ color: var(--text-1);
2969
+ text-shadow: 0 1px 0 rgb(255 255 255 / 0.7);
2970
+ }
2971
+ :root[data-theme="dark"] .os-title { text-shadow: none; }
2972
+
2973
+ /* The three lights: the product's tinted-glass orbs, the red one live. */
2974
+ .os-lights { display: flex; align-items: center; gap: 6px; width: 64px; flex: none; }
2975
+ .os-light-hit {
2976
+ display: grid;
2977
+ place-items: center;
2978
+ width: 21px;
2979
+ height: 21px;
2980
+ margin: -3px;
2981
+ }
2982
+ .os-light {
2983
+ display: grid;
2984
+ place-items: center;
2985
+ position: relative;
2986
+ overflow: hidden;
2987
+ width: 13px;
2988
+ height: 13px;
2989
+ border-radius: 999px;
2990
+ font-size: 10px;
2991
+ font-weight: 700;
2992
+ line-height: 1;
2993
+ color: transparent;
2994
+ background: linear-gradient(color-mix(in srgb, var(--accent) 80%, transparent),
2995
+ color-mix(in srgb, var(--accent) 58%, transparent));
2996
+ box-shadow: 0 2px 3px rgb(0 0 0 / 0.2), 0 1px 1px rgb(0 0 0 / 0.3),
2997
+ inset 0 0 0 0.5px rgb(0 0 0 / 0.45), inset 0 1px 1px rgb(255 255 255 / 0.45),
2998
+ inset 0 2px 3px 1px color-mix(in srgb, var(--accent) 60%, transparent);
2999
+ transition: filter 150ms linear, color 150ms linear;
3000
+ }
3001
+ .os-light::before {
3002
+ content: "";
3003
+ position: absolute;
3004
+ top: 1px;
3005
+ left: 3px;
3006
+ right: 3px;
3007
+ height: 28%;
3008
+ border-radius: 6px 6px 0 0;
3009
+ background: linear-gradient(rgb(255 255 255 / 0.9), rgb(255 255 255 / 0.3));
3010
+ }
3011
+ .os-light::after {
3012
+ content: "";
3013
+ position: absolute;
3014
+ bottom: 1px;
3015
+ left: 1.5px;
3016
+ right: 1.5px;
3017
+ height: 33%;
3018
+ border-radius: 0 0 6px 6px;
3019
+ background: linear-gradient(rgb(255 255 255 / 0.2), rgb(255 255 255 / 0.5));
3020
+ }
3021
+ .os-lights:hover .os-light:not(.idle) { color: rgb(0 0 0 / 0.55); }
3022
+ .os-light-hit:hover .os-light:not(.idle) { filter: brightness(1.1); }
3023
+ .os-light.idle {
3024
+ background: linear-gradient(rgb(160 160 160 / 0.625), rgb(255 255 255 / 0.625));
3025
+ box-shadow: 0 2px 3px rgb(0 0 0 / 0.2), 0 1px 1px rgb(0 0 0 / 0.3),
3026
+ inset 0 0 0 0.5px rgb(0 0 0 / 0.3), inset 0 1px 2px rgb(0 0 0 / 0.4),
3027
+ inset 0 2px 3px 1px #bbbbbb;
3028
+ }
3029
+
3030
+ /* The body is the OS view's own frame (globals.css `.settings-frame`): two
3031
+ wells recessed into the window's glass, a 5px seam of pane between them,
3032
+ 4px of pane all round — and the OS face on everything inside, because
3033
+ that face IS half of what makes a window read as a window. */
3034
+ .os-body {
3035
+ display: flex;
3036
+ flex: 1;
3037
+ min-height: 0;
3038
+ gap: 5px;
3039
+ padding: 4px;
3040
+ font-family: var(--font-os);
3041
+ }
3042
+
3043
+ .os-well {
3044
+ border: 1px solid var(--well-recess-rim);
3045
+ background: var(--well-recess);
3046
+ box-shadow: var(--well-recess-shade);
3047
+ }
3048
+
3049
+ /* The places, down the left. Bare glass: it is chrome, not content. */
3050
+ .os-rail {
3051
+ display: flex;
3052
+ flex-direction: column;
3053
+ gap: 1px;
3054
+ width: 128px;
3055
+ flex: none;
3056
+ overflow-y: auto;
3057
+ padding: 6px;
3058
+ }
3059
+ /* A place is its object and its name — the rail rows in the Store and
3060
+ Creations carry the system's own artwork, and a rail of bare words is the
3061
+ other half of why this window did not read as one of theirs. */
3062
+ .os-place {
3063
+ display: flex;
3064
+ align-items: center;
3065
+ gap: 8px;
3066
+ width: 100%;
3067
+ min-height: 32px;
3068
+ padding: 4px 8px;
3069
+ border-radius: 5px;
3070
+ text-align: left;
3071
+ font-size: 12.5px;
3072
+ color: #1c1c1c;
3073
+ transition: background-color var(--fast) var(--ease), color var(--fast) var(--ease);
3074
+ }
3075
+ :root[data-theme="dark"] .os-place { color: var(--text-1); }
3076
+ .os-place .place-mark { width: 24px; height: 24px; flex: none; object-fit: contain; }
3077
+ .os-place:hover { background: rgb(0 0 0 / 0.05); }
3078
+ :root[data-theme="dark"] .os-place:hover { background: var(--row-hover); }
3079
+ .os-place[aria-pressed="true"],
3080
+ .os-place[aria-pressed="true"]:hover {
3081
+ background: linear-gradient(180deg, color-mix(in srgb, var(--accent) 72%, #ffffff) 0%, var(--accent) 100%);
3082
+ color: #ffffff;
3083
+ font-weight: 500;
3084
+ }
3085
+
3086
+ /* The sheet: the same recessed well, in paper. */
3087
+ .os-sheet {
3088
+ display: flex;
3089
+ flex-direction: column;
3090
+ flex: 1;
3091
+ min-width: 0;
3092
+ min-height: 0;
3093
+ background: var(--sheet-fill);
3094
+ }
3095
+ /* Its title line names the place and carries the field at the far end —
3096
+ the arrangement the Store window wears. */
3097
+ .os-head {
3098
+ display: flex;
3099
+ align-items: center;
3100
+ justify-content: space-between;
3101
+ gap: 8px;
3102
+ min-height: 36px;
3103
+ flex: none;
3104
+ padding: 2px 8px 0 12px;
3105
+ }
3106
+ .os-head[hidden] { display: none; }
3107
+ .os-place-title {
3108
+ min-width: 0;
3109
+ overflow: hidden;
3110
+ text-overflow: ellipsis;
3111
+ white-space: nowrap;
3112
+ font-size: 12px;
3113
+ font-weight: 600;
3114
+ color: #222222;
3115
+ text-shadow: var(--etch);
3116
+ }
3117
+ :root[data-theme="dark"] .os-place-title { color: var(--text-1); }
3118
+ .search-pill.os-search {
3119
+ flex: 0 1 auto;
3120
+ width: 200px;
3121
+ height: 28px;
3122
+ padding: 0 6px 0 10px;
3123
+ background: rgb(255 255 255 / 0.6);
3124
+ box-shadow: inset 0 1.5px 3px rgb(0 10 40 / 0.14);
3125
+ border: 1px solid rgb(0 0 0 / 0.18);
3126
+ border-radius: 999px;
3127
+ }
3128
+ :root[data-theme="dark"] .search-pill.os-search {
3129
+ background: var(--well-fill);
3130
+ border-color: var(--well-rim);
3131
+ box-shadow: var(--well-shade);
3132
+ }
3133
+ .search-pill.os-search input { font-size: 12.5px; }
3134
+ .search-pill.os-search:focus-within {
3135
+ border-color: color-mix(in srgb, var(--accent) 55%, rgb(0 0 0 / 0.1));
3136
+ box-shadow: inset 0 1.5px 3px rgb(0 10 40 / 0.1), 0 0 0 3px color-mix(in srgb, var(--accent) 25%, transparent);
3137
+ }
3138
+ /* The scroller under the title line, hairlined off it the way the Store's
3139
+ list is. */
3140
+ .os-rows {
3141
+ flex: 1;
3142
+ min-height: 0;
3143
+ overflow-y: auto;
3144
+ padding: 2px 8px 10px;
3145
+ border-top: 1px solid var(--sheet-rule);
3146
+ }
3147
+ .os-rows.on-page { border-top: 0; padding-top: 0; }
3148
+
3149
+ /* ---------------------------------------------------------------------------
3150
+ The gallery
3151
+ ---------------------------------------------------------------------------
3152
+ A catalog is looked OVER, not read line by line: one tile per thing, no
3153
+ rules between them, and a band heading only where two kinds of thing share
3154
+ a place. The grid asks for a tile floor rather than breakpoints — below it
3155
+ a name truncates mid-word, above it the field simply fills. */
3156
+ .os-band {
3157
+ margin: 12px 4px 4px;
3158
+ font-size: 10.5px;
3159
+ font-weight: 600;
3160
+ letter-spacing: 0.06em;
3161
+ text-transform: uppercase;
3162
+ color: var(--text-4);
3163
+ }
3164
+ .os-band:first-child { margin-top: 2px; }
3165
+ .os-tiles {
3166
+ display: grid;
3167
+ /* A tile floor rather than breakpoints. The Store sits at 9rem in a
3168
+ 1040px window; this window is 720, so the floor comes down to keep the
3169
+ same NUMBER of tiles across — four, where three left a field of air. */
3170
+ grid-template-columns: repeat(auto-fill, minmax(8.25rem, 1fr));
3171
+ gap: 2px;
3172
+ }
3173
+
3174
+ /* A tile is four things and no more: the mark, the name, one line of what it
3175
+ is, and the way in. Its rhythm is fixed — 8px under the mark, 4px between
3176
+ the name and the line about it, 10px before the verb — so a tile whose
3177
+ name wraps is simply a line taller and its neighbours' slack falls below
3178
+ their verbs, where it is just space. Never a backdrop filter: this is an
3179
+ element that MULTIPLIES with the data, and a field of glass stops the
3180
+ window compositing at all. */
3181
+ .os-tile {
3182
+ position: relative;
3183
+ display: flex;
3184
+ flex-direction: column;
3185
+ align-items: center;
3186
+ gap: 0;
3187
+ padding: 8px 6px;
3188
+ border-radius: 10px;
3189
+ text-align: center;
3190
+ transition: background-color var(--fast) var(--ease);
3191
+ }
3192
+ .os-tile:hover { background: var(--row-hover); }
3193
+ /* The tile's own door, under everything it holds — the way in to the page
3194
+ where the grant is narrowed. */
3195
+ .tile-door {
3196
+ position: absolute;
3197
+ inset: 0;
3198
+ /* Over everything the tile holds: the mark is positioned (its letters sit
3199
+ under the artwork), so without this the door would paint beneath it and
3200
+ a click on the logo would land on nothing. */
3201
+ z-index: 1;
3202
+ border-radius: 10px;
3203
+ cursor: pointer;
3204
+ }
3205
+ .tile-door:focus-visible { outline: none; box-shadow: var(--ring); }
3206
+ .tile-name {
3207
+ margin-top: 6px;
3208
+ max-width: 100%;
3209
+ display: flex;
3210
+ align-items: center;
3211
+ justify-content: center;
3212
+ gap: 3px;
3213
+ font-size: 12px;
3214
+ font-weight: 500;
3215
+ line-height: 1.25;
3216
+ color: var(--text-1);
3217
+ overflow: hidden;
3218
+ }
3219
+ /* One line, always: a field of tiles that do not line up is worse to read
3220
+ than a name that ends in an ellipsis, and the page carries the whole name
3221
+ one click away. The ellipsis has to live on a CHILD — bare text in a flex
3222
+ row is an anonymous item and truncates nowhere, which is how a long name
3223
+ came to hang out of the window. The seal beside it is never squeezed. */
3224
+ .tile-name .name-text,
3225
+ .page-name .name-text {
3226
+ min-width: 0;
3227
+ white-space: nowrap;
3228
+ overflow: hidden;
3229
+ text-overflow: ellipsis;
3230
+ }
3231
+ /* ONE line, whatever it says — what a system tool does, or whether a
3232
+ connection is linked. A second line of prose is the difference between six
3233
+ tiles on screen and twelve, and the whole of it waits on the page. */
3234
+ .tile-line {
3235
+ display: flex;
3236
+ align-items: center;
3237
+ justify-content: center;
3238
+ gap: 4px;
3239
+ margin-top: 3px;
3240
+ width: 100%;
3241
+ font-size: 10.5px;
3242
+ line-height: 1.4;
3243
+ color: var(--text-4);
3244
+ overflow: hidden;
3245
+ }
3246
+ /* The ellipsis lives on the span, never on the row: bare text in a flex
3247
+ container is an anonymous item, which truncates nowhere and bleeds out of
3248
+ both ends of a centred tile. Same lesson as the name above. */
3249
+ .tile-line .line-text {
3250
+ min-width: 0;
3251
+ white-space: nowrap;
3252
+ overflow: hidden;
3253
+ text-overflow: ellipsis;
3254
+ }
3255
+ /* A connection's line is its state, in the tone of its orb. */
3256
+ .tile-line.link-state { color: var(--text-3); }
3257
+ .tile-line.link-state.ok { color: var(--success); }
3258
+ .tile-line.link-state.danger { color: var(--danger); }
3259
+ .tile-line .orb { width: 8px; height: 8px; }
3260
+ .tile-verb {
3261
+ position: relative;
3262
+ z-index: 2;
3263
+ margin-top: 8px;
3264
+ height: 21px;
3265
+ padding: 0 11px;
3266
+ font-size: 11px;
3267
+ border-radius: 999px;
3268
+ }
3269
+ .tile-state {
3270
+ /* A state, not a control: the click falls through to the tile's own door,
3271
+ which is where the grant is narrowed. */
3272
+ pointer-events: none;
3273
+ display: inline-flex;
3274
+ align-items: center;
3275
+ gap: 3px;
3276
+ margin-top: 8px;
3277
+ height: 21px;
3278
+ padding: 0 9px;
3279
+ border-radius: 999px;
3280
+ font-size: 11px;
3281
+ font-weight: 500;
3282
+ color: var(--text-3);
3283
+ background: var(--bg-3);
3284
+ }
3285
+ .tile-state.whole { color: var(--success); }
3286
+ .tile-state svg { width: 12px; height: 12px; }
3287
+ .os-empty {
3288
+ margin: 16px 8px;
3289
+ font-size: 12px;
3290
+ color: var(--text-4);
3291
+ text-align: center;
3292
+ }
3293
+
3294
+ /* The mark. An OBJECT stands free — the platform's own artwork is a lit
3295
+ thing on transparency, and a plate behind it reads as a frame around a
3296
+ cut-out (ArtifactIcon.tsx says the same). A connector's brand LOGO keeps
3297
+ the white tile the product draws it on: a mark in flat black would
3298
+ otherwise vanish into a dark theme. */
3299
+ .thing-mark {
3300
+ position: relative;
3301
+ display: grid;
3302
+ place-items: center;
3303
+ width: 36px;
3304
+ height: 36px;
3305
+ flex: none;
3306
+ color: var(--text-2);
3307
+ }
3308
+ .thing-mark img {
3309
+ position: relative;
3310
+ z-index: 1;
3311
+ width: 100%;
3312
+ height: 100%;
3313
+ object-fit: contain;
3314
+ }
3315
+ .thing-mark.plate {
3316
+ border-radius: 10px;
3317
+ background: var(--card-fill);
3318
+ box-shadow: inset 0 0 0 1px var(--line-2);
3319
+ overflow: hidden;
3320
+ }
3321
+ .thing-mark.plate img { width: 22px; height: 22px; }
3322
+ .thing-mark svg { width: 19px; height: 19px; }
3323
+ .thing-mark .mark-letters {
3324
+ position: absolute;
3325
+ font-size: 12px;
3326
+ font-weight: 600;
3327
+ letter-spacing: 0.01em;
3328
+ text-transform: capitalize;
3329
+ color: var(--text-3);
3330
+ }
3331
+ .thing-mark.glyph .mark-letters { display: none; }
3332
+ /* White under a brand logo in both themes, the way ConnectorIcon paints it. */
3333
+ .thing-mark.plate:has(img) { background: #ffffff; box-shadow: inset 0 0 0 1px rgb(0 0 0 / 0.1); }
3334
+ /* Artwork that never arrived: the plate comes back, with the initials on it. */
3335
+ .thing-mark.bare { border-radius: 10px; background: var(--bg-3); box-shadow: inset 0 0 0 1px var(--line-3); }
3336
+
3337
+ /* Terminus's own seal, worn after an official name. */
3338
+ .seal { display: inline-block; flex: none; width: 12px; height: 12px; vertical-align: -1px; overflow: visible; }
3339
+ .page-name .seal { width: 15px; height: 15px; }
3340
+
3341
+ /* ---------------------------------------------------------------------------
3342
+ A thing's page
3343
+ ---------------------------------------------------------------------------
3344
+ Where the decision is made: what it is, and a line per thing it may do,
3345
+ each a box you tick. Never an add-then-remove flow — the page shows every
3346
+ candidate and the box says yes. */
3347
+ .os-page { flex: 1; min-height: 0; overflow-y: auto; padding: 2px 4px 10px; }
3348
+ .page-back {
3349
+ display: inline-flex;
3350
+ align-items: center;
3351
+ gap: 2px;
3352
+ margin-bottom: 8px;
3353
+ padding: 2px 6px 2px 2px;
3354
+ border-radius: 6px;
3355
+ font-size: 11.5px;
3356
+ color: var(--text-3);
3357
+ }
3358
+ .page-back:hover { background: var(--row-hover); color: var(--text-1); }
3359
+ .page-back svg { width: 14px; height: 14px; rotate: 90deg; }
3360
+ .page-head { display: flex; align-items: center; gap: 10px; }
3361
+ .page-titles { display: flex; flex-direction: column; min-width: 0; flex: 1; gap: 1px; }
3362
+ .page-name {
3363
+ display: flex;
3364
+ align-items: center;
3365
+ gap: 4px;
3366
+ font-family: var(--font-brand);
3367
+ font-size: 19px;
3368
+ font-weight: 500;
3369
+ color: var(--text-1);
3370
+ }
3371
+ .page-sub {
3372
+ font-family: var(--font-code);
3373
+ font-size: 11px;
3374
+ color: var(--text-4);
3375
+ overflow: hidden;
3376
+ text-overflow: ellipsis;
3377
+ white-space: nowrap;
3378
+ }
3379
+ .page-action { flex: none; height: 24px; padding: 0 13px; font-size: 11.5px; }
3380
+ .page-note { margin: 8px 2px 0; font-size: 12px; line-height: 1.45; color: var(--text-2); }
3381
+ .page-facts {
3382
+ display: flex;
3383
+ flex-wrap: wrap;
3384
+ align-items: center;
3385
+ gap: 6px;
3386
+ margin: 7px 2px 0;
3387
+ font-size: 11px;
3388
+ color: var(--text-3);
3389
+ }
3390
+ .page-facts .dot { color: var(--text-4); }
3391
+ .page-facts .orb { margin-right: 3px; vertical-align: -1px; }
3392
+ /* What the manifest says about WHEN to reach for this — the note compiled
3393
+ into the agent's prompt, shown where the grant is. */
3394
+ .page-when {
3395
+ display: flex;
3396
+ gap: 6px;
3397
+ margin: 7px 2px 0;
3398
+ font-size: 11.5px;
3399
+ line-height: 1.45;
3400
+ color: var(--text-3);
3401
+ }
3402
+ .page-when span {
3403
+ flex: none;
3404
+ font-size: 10.5px;
3405
+ font-weight: 600;
3406
+ letter-spacing: 0.05em;
3407
+ line-height: 1.7;
3408
+ text-transform: uppercase;
3409
+ color: var(--text-4);
3410
+ }
3411
+ .page-link {
3412
+ display: inline-flex;
3413
+ align-items: center;
3414
+ gap: 5px;
3415
+ margin: 8px 2px 0;
3416
+ font-size: 11.5px;
3417
+ text-decoration: none;
3418
+ }
3419
+ .page-link:hover { text-decoration: underline; }
3420
+ .page-link svg { width: 13px; height: 13px; }
3421
+ .page-quiet { margin: 10px 2px 0; font-size: 11.5px; color: var(--text-4); }
3422
+ .os-page .cap-group { margin: 14px 2px 4px; }
3423
+ .os-page .rows { border-radius: 8px; }
3424
+ .os-page .model-row { min-height: 40px; align-items: flex-start; padding-top: 9px; padding-bottom: 9px; }
3425
+ .part-note {
3426
+ display: block;
3427
+ margin-top: 2px;
3428
+ font-size: 10.5px;
3429
+ line-height: 1.35;
3430
+ color: var(--text-4);
3431
+ }
3432
+ .os-page .model-row .meta { flex-direction: column; align-items: flex-start; gap: 0; }
3433
+ .os-page .model-row .label { font-size: 12px; font-weight: 500; }
3434
+ /* An operation is an id — the word that goes in terminus.json — so it is
3435
+ set in the code face, the way every id on this page is. */
3436
+ .os-page .model-row .label.mono { font-family: var(--font-code); font-size: 11.5px; font-weight: 400; }
3437
+
3438
+ /* Aqua's other push-button: the Cancel grey, for a verb that is not asking
3439
+ for anything. Poured from the same gradients as the accent gel, so a
3440
+ field of them costs the compositor nothing. */
3441
+ .gel-quiet,
3442
+ .tile-verb.gel {
3443
+ --gel-crown: #e6e7ea;
3444
+ --gel-foot: #fdfdfe;
3445
+ --gel-rim-crown: #8e9096;
3446
+ --gel-rim-foot: hsl(0 0% 62%);
3447
+ --gel-label: #1b1d22;
3448
+ }
3449
+ :root[data-theme="dark"] .gel-quiet,
3450
+ :root[data-theme="dark"] .tile-verb.gel {
3451
+ --gel-crown: #565a63;
3452
+ --gel-foot: #8b9099;
3453
+ --gel-rim-crown: #2b2e34;
3454
+ --gel-rim-foot: hsl(0 0% 28%);
3455
+ --gel-label: #f5f6f8;
3456
+ }
3457
+
3458
+ /* ------------------------------------------------------------------------- */
3459
+ /* Popovers and toast */
3460
+ /* ------------------------------------------------------------------------- */
3461
+
3462
+ .model-pop {
3463
+ position: absolute;
3464
+ z-index: 30;
3465
+ display: flex;
3466
+ flex-direction: column;
3467
+ overflow: hidden;
3468
+ border-radius: 16px;
3469
+ background: var(--pop-fill);
3470
+ box-shadow: var(--pop-throw);
3471
+ }
3472
+ .model-pop .heading { padding: 12px 14px 6px; }
3473
+ .model-pop .heading .sw-heading { font-size: 16px; }
3474
+ .model-pop .columns { flex: 1; min-height: 0; display: flex; }
3475
+ .model-pop .models { flex: 1; min-height: 0; padding: 0 6px 6px; }
3476
+ .model-pop .empty { margin: 0; padding: 14px; font-size: var(--text-base); color: var(--text-3); }
3477
+
3478
+ .model-pop .row {
3479
+ display: flex;
3480
+ align-items: center;
3481
+ gap: 10px;
3482
+ width: 100%;
3483
+ padding: 7px 9px;
3484
+ border-radius: 10px;
3485
+ text-align: left;
3486
+ transition: background-color var(--fast) var(--ease);
3487
+ }
3488
+ .model-pop .row:hover { background: var(--bg-3); }
3489
+ .model-pop .row .name {
3490
+ display: block;
3491
+ font-size: var(--text-base);
3492
+ color: var(--text-1);
3493
+ overflow: hidden;
3494
+ text-overflow: ellipsis;
3495
+ white-space: nowrap;
3496
+ }
3497
+ .model-pop .row .name.selected { font-weight: 600; }
3498
+ .model-pop .row .meta {
3499
+ display: block;
3500
+ font-family: var(--font-code);
3501
+ font-size: var(--text-xs);
3502
+ color: var(--text-3);
3503
+ overflow: hidden;
3504
+ text-overflow: ellipsis;
3505
+ white-space: nowrap;
3506
+ }
3507
+ .model-pop .row .check { display: inline-flex; color: var(--accent-text); flex: none; }
3508
+ .model-pop .row .check svg { width: 14px; height: 14px; }
3509
+
3510
+ .t-pop { animation: pop-in var(--normal) var(--ease-out-strong) both; }
3511
+ .t-pop[data-state="closed"] { animation: pop-out var(--fast) var(--ease) both; }
3512
+ @keyframes pop-in {
3513
+ from { opacity: 0; transform: translateY(4px) scale(0.985); }
3514
+ to { opacity: 1; transform: none; }
3515
+ }
3516
+ @keyframes pop-out {
3517
+ from { opacity: 1; transform: none; }
3518
+ to { opacity: 0; transform: translateY(2px) scale(0.99); }
3519
+ }
3520
+ .t-collapse {
3521
+ display: grid;
3522
+ grid-template-rows: 0fr;
3523
+ transition: grid-template-rows var(--normal) var(--ease);
3524
+ }
3525
+ .t-collapse[data-open="true"] { grid-template-rows: 1fr; }
3526
+ .t-collapse > * { min-height: 0; overflow: hidden; }
3527
+
3528
+ /* The toast: a capsule under the conversation. */
3529
+ .toast {
3530
+ position: fixed;
3531
+ left: 50%;
3532
+ bottom: 28px;
3533
+ translate: -50% 0;
3534
+ z-index: 60;
3535
+ max-width: min(78vw, 560px);
3536
+ padding: 9px 16px;
3537
+ border-radius: 999px;
3538
+ background: var(--pop-fill);
3539
+ box-shadow: var(--pop-throw);
3540
+ color: var(--text-1);
3541
+ font-size: var(--text-base);
3542
+ font-weight: 500;
3543
+ text-align: center;
3544
+ animation: toast-in var(--normal) var(--ease-out-strong) both;
3545
+ }
3546
+ @keyframes toast-in {
3547
+ from { opacity: 0; transform: translateY(8px) scale(0.98); }
3548
+ to { opacity: 1; transform: none; }
3549
+ }
3550
+
3551
+ .t-fade-up { animation: fade-up var(--normal) var(--ease-out-strong) both; }
3552
+ @keyframes fade-up {
3553
+ from { opacity: 0; transform: translateY(4px); }
3554
+ to { opacity: 1; transform: none; }
3555
+ }
3556
+
3557
+ /* ------------------------------------------------------------------------- */
3558
+ /* History: the record lives in dev.db; this plane reads it */
3559
+ /* ------------------------------------------------------------------------- */
3560
+
3561
+ .history { flex: 1; min-height: 0; display: flex; flex-direction: column; }
3562
+ .history-head {
3563
+ display: flex;
3564
+ align-items: center;
3565
+ gap: 10px;
3566
+ width: 100%;
3567
+ max-width: calc(var(--measure) + 2 * var(--gutter));
3568
+ margin: 0 auto;
3569
+ padding: 22px var(--gutter) 10px;
3570
+ }
3571
+ .history-head .title {
3572
+ min-width: 0;
3573
+ font-family: var(--font-brand);
3574
+ font-size: 26px;
3575
+ font-weight: 600;
3576
+ line-height: 1.15;
3577
+ letter-spacing: -0.015em;
3578
+ color: var(--text-1);
3579
+ overflow: hidden;
3580
+ text-overflow: ellipsis;
3581
+ white-space: nowrap;
3582
+ }
3583
+ .history-head .meta { font-size: var(--text-sm); color: var(--text-3); white-space: nowrap; }
3584
+ .history-head [data-history-back] { margin-left: -8px; }
3585
+ .history-head [data-history-back] svg { transform: rotate(90deg); }
3586
+ .history-list { flex: 1; min-height: 0; overflow-y: auto; padding: 0 var(--gutter) 24px; }
3587
+ .history-list > .rows { max-width: var(--measure); margin: 0 auto; }
3588
+ .history-row {
3589
+ display: grid;
3590
+ grid-template-columns: minmax(0, 1fr) auto;
3591
+ grid-template-areas: "title when" "meta when";
3592
+ align-items: center;
3593
+ column-gap: 16px;
3594
+ row-gap: 2px;
3595
+ width: 100%;
3596
+ padding: 11px 4px;
3597
+ text-align: left;
3598
+ background: transparent;
3599
+ cursor: pointer;
3600
+ }
3601
+ .history-row .title {
3602
+ grid-area: title;
3603
+ display: flex;
3604
+ align-items: center;
3605
+ gap: 8px;
3606
+ min-width: 0;
3607
+ font-size: var(--text-read);
3608
+ font-weight: 500;
3609
+ color: var(--text-1);
3610
+ }
3611
+ .history-row .title .text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
3612
+ .history-row .meta { grid-area: meta; font-size: var(--text-sm); color: var(--text-3); }
3613
+ .history-row .when { grid-area: when; font-size: var(--text-sm); color: var(--text-3); }
3614
+ .history-row:hover .title { color: var(--accent-text); }
3615
+ .history .transcript { flex: 1; min-height: 0; overflow-y: auto; }
3616
+ .history .transcript-inner { padding-top: 12px; }
3617
+
3618
+ /* ------------------------------------------------------------------------- */
3619
+ /* Narrow viewports */
3620
+ /* ------------------------------------------------------------------------- */
3621
+
3622
+ @media (max-width: 860px) {
3623
+ :root { --sidebar-w: 208px; --inspector-w: 300px; }
3624
+ .brand .name { font-size: 20px; }
3625
+ }
3626
+
3627
+ @media (max-width: 560px) {
3628
+ .spend-readout { display: none; }
3629
+ /* The provider's mark still says which model; the name gives the words
3630
+ their room. */
3631
+ .model-trigger .label { display: none; }
3632
+ }
3633
+
3634
+ @media (prefers-reduced-motion: reduce) {
3635
+ *, *::before, *::after {
3636
+ animation-duration: 1ms !important;
3637
+ animation-iteration-count: 1 !important;
3638
+ transition-duration: 1ms !important;
3639
+ }
3640
+ }